select.es5.js 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { animate, animateChild, query, state, style, transition, trigger } from '@angular/animations';
  9. import { __extends } from 'tslib';
  10. import { ActiveDescendantKeyManager, LiveAnnouncer } from '@angular/cdk/a11y';
  11. import { Directionality } from '@angular/cdk/bidi';
  12. import { coerceBooleanProperty } from '@angular/cdk/coercion';
  13. import { SelectionModel } from '@angular/cdk/collections';
  14. import { A, DOWN_ARROW, END, ENTER, HOME, LEFT_ARROW, RIGHT_ARROW, SPACE, UP_ARROW, hasModifierKey } from '@angular/cdk/keycodes';
  15. import { CdkConnectedOverlay, Overlay, OverlayModule } from '@angular/cdk/overlay';
  16. import { ViewportRuler } from '@angular/cdk/scrolling';
  17. import { Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, Inject, InjectionToken, Input, isDevMode, NgZone, Optional, Output, Self, ViewChild, ViewEncapsulation, NgModule } from '@angular/core';
  18. import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
  19. import { _countGroupLabelsBeforeOption, _getOptionScrollPosition, ErrorStateMatcher, MAT_OPTION_PARENT_COMPONENT, MatOptgroup, MatOption, mixinDisabled, mixinDisableRipple, mixinErrorState, mixinTabIndex, MatCommonModule, MatOptionModule } from '@angular/material/core';
  20. import { MatFormField, MatFormFieldControl, MatFormFieldModule } from '@angular/material/form-field';
  21. import { defer, merge, Subject } from 'rxjs';
  22. import { distinctUntilChanged, filter, map, startWith, switchMap, take, takeUntil } from 'rxjs/operators';
  23. import { CommonModule } from '@angular/common';
  24. /**
  25. * @fileoverview added by tsickle
  26. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  27. */
  28. /**
  29. * The following are all the animations for the mat-select component, with each
  30. * const containing the metadata for one animation.
  31. *
  32. * The values below match the implementation of the AngularJS Material mat-select animation.
  33. * \@docs-private
  34. * @type {?}
  35. */
  36. var matSelectAnimations = {
  37. /**
  38. * This animation ensures the select's overlay panel animation (transformPanel) is called when
  39. * closing the select.
  40. * This is needed due to https://github.com/angular/angular/issues/23302
  41. */
  42. transformPanelWrap: trigger('transformPanelWrap', [
  43. transition('* => void', query('@transformPanel', [animateChild()], { optional: true }))
  44. ]),
  45. /**
  46. * This animation transforms the select's overlay panel on and off the page.
  47. *
  48. * When the panel is attached to the DOM, it expands its width by the amount of padding, scales it
  49. * up to 100% on the Y axis, fades in its border, and translates slightly up and to the
  50. * side to ensure the option text correctly overlaps the trigger text.
  51. *
  52. * When the panel is removed from the DOM, it simply fades out linearly.
  53. */
  54. transformPanel: trigger('transformPanel', [
  55. state('void', style({
  56. transform: 'scaleY(0.8)',
  57. minWidth: '100%',
  58. opacity: 0
  59. })),
  60. state('showing', style({
  61. opacity: 1,
  62. minWidth: 'calc(100% + 32px)',
  63. // 32px = 2 * 16px padding
  64. transform: 'scaleY(1)'
  65. })),
  66. state('showing-multiple', style({
  67. opacity: 1,
  68. minWidth: 'calc(100% + 64px)',
  69. // 64px = 48px padding on the left + 16px padding on the right
  70. transform: 'scaleY(1)'
  71. })),
  72. transition('void => *', animate('120ms cubic-bezier(0, 0, 0.2, 1)')),
  73. transition('* => void', animate('100ms 25ms linear', style({ opacity: 0 })))
  74. ]),
  75. /**
  76. * This animation fades in the background color and text content of the
  77. * select's options. It is time delayed to occur 100ms after the overlay
  78. * panel has transformed in.
  79. * @deprecated Not used anymore. To be removed.
  80. * \@breaking-change 8.0.0
  81. */
  82. fadeInContent: trigger('fadeInContent', [
  83. state('showing', style({ opacity: 1 })),
  84. transition('void => showing', [
  85. style({ opacity: 0 }),
  86. animate('150ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')
  87. ])
  88. ])
  89. };
  90. /**
  91. * @deprecated
  92. * \@breaking-change 8.0.0
  93. * \@docs-private
  94. * @type {?}
  95. */
  96. var transformPanel = matSelectAnimations.transformPanel;
  97. /**
  98. * @deprecated
  99. * \@breaking-change 8.0.0
  100. * \@docs-private
  101. * @type {?}
  102. */
  103. var fadeInContent = matSelectAnimations.fadeInContent;
  104. /**
  105. * @fileoverview added by tsickle
  106. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  107. */
  108. /**
  109. * Returns an exception to be thrown when attempting to change a select's `multiple` option
  110. * after initialization.
  111. * \@docs-private
  112. * @return {?}
  113. */
  114. function getMatSelectDynamicMultipleError() {
  115. return Error('Cannot change `multiple` mode of select after initialization.');
  116. }
  117. /**
  118. * Returns an exception to be thrown when attempting to assign a non-array value to a select
  119. * in `multiple` mode. Note that `undefined` and `null` are still valid values to allow for
  120. * resetting the value.
  121. * \@docs-private
  122. * @return {?}
  123. */
  124. function getMatSelectNonArrayValueError() {
  125. return Error('Value must be an array in multiple-selection mode.');
  126. }
  127. /**
  128. * Returns an exception to be thrown when assigning a non-function value to the comparator
  129. * used to determine if a value corresponds to an option. Note that whether the function
  130. * actually takes two values and returns a boolean is not checked.
  131. * @return {?}
  132. */
  133. function getMatSelectNonFunctionValueError() {
  134. return Error('`compareWith` must be a function.');
  135. }
  136. /**
  137. * @fileoverview added by tsickle
  138. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  139. */
  140. /** @type {?} */
  141. var nextUniqueId = 0;
  142. /**
  143. * The max height of the select's overlay panel
  144. * @type {?}
  145. */
  146. var SELECT_PANEL_MAX_HEIGHT = 256;
  147. /**
  148. * The panel's padding on the x-axis
  149. * @type {?}
  150. */
  151. var SELECT_PANEL_PADDING_X = 16;
  152. /**
  153. * The panel's x axis padding if it is indented (e.g. there is an option group).
  154. * @type {?}
  155. */
  156. var SELECT_PANEL_INDENT_PADDING_X = SELECT_PANEL_PADDING_X * 2;
  157. /**
  158. * The height of the select items in `em` units.
  159. * @type {?}
  160. */
  161. var SELECT_ITEM_HEIGHT_EM = 3;
  162. // TODO(josephperrott): Revert to a constant after 2018 spec updates are fully merged.
  163. /**
  164. * Distance between the panel edge and the option text in
  165. * multi-selection mode.
  166. *
  167. * Calculated as:
  168. * (SELECT_PANEL_PADDING_X * 1.5) + 16 = 40
  169. * The padding is multiplied by 1.5 because the checkbox's margin is half the padding.
  170. * The checkbox width is 16px.
  171. * @type {?}
  172. */
  173. var SELECT_MULTIPLE_PANEL_PADDING_X = SELECT_PANEL_PADDING_X * 1.5 + 16;
  174. /**
  175. * The select panel will only "fit" inside the viewport if it is positioned at
  176. * this value or more away from the viewport boundary.
  177. * @type {?}
  178. */
  179. var SELECT_PANEL_VIEWPORT_PADDING = 8;
  180. /**
  181. * Injection token that determines the scroll handling while a select is open.
  182. * @type {?}
  183. */
  184. var MAT_SELECT_SCROLL_STRATEGY = new InjectionToken('mat-select-scroll-strategy');
  185. /**
  186. * \@docs-private
  187. * @param {?} overlay
  188. * @return {?}
  189. */
  190. function MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
  191. return (/**
  192. * @return {?}
  193. */
  194. function () { return overlay.scrollStrategies.reposition(); });
  195. }
  196. /**
  197. * \@docs-private
  198. * @type {?}
  199. */
  200. var MAT_SELECT_SCROLL_STRATEGY_PROVIDER = {
  201. provide: MAT_SELECT_SCROLL_STRATEGY,
  202. deps: [Overlay],
  203. useFactory: MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY,
  204. };
  205. /**
  206. * Change event object that is emitted when the select value has changed.
  207. */
  208. var /**
  209. * Change event object that is emitted when the select value has changed.
  210. */
  211. MatSelectChange = /** @class */ (function () {
  212. function MatSelectChange(source, value) {
  213. this.source = source;
  214. this.value = value;
  215. }
  216. return MatSelectChange;
  217. }());
  218. // Boilerplate for applying mixins to MatSelect.
  219. /**
  220. * \@docs-private
  221. */
  222. var
  223. // Boilerplate for applying mixins to MatSelect.
  224. /**
  225. * \@docs-private
  226. */
  227. MatSelectBase = /** @class */ (function () {
  228. function MatSelectBase(_elementRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl) {
  229. this._elementRef = _elementRef;
  230. this._defaultErrorStateMatcher = _defaultErrorStateMatcher;
  231. this._parentForm = _parentForm;
  232. this._parentFormGroup = _parentFormGroup;
  233. this.ngControl = ngControl;
  234. }
  235. return MatSelectBase;
  236. }());
  237. /** @type {?} */
  238. var _MatSelectMixinBase = mixinDisableRipple(mixinTabIndex(mixinDisabled(mixinErrorState(MatSelectBase))));
  239. /**
  240. * Allows the user to customize the trigger that is displayed when the select has a value.
  241. */
  242. var MatSelectTrigger = /** @class */ (function () {
  243. function MatSelectTrigger() {
  244. }
  245. MatSelectTrigger.decorators = [
  246. { type: Directive, args: [{
  247. selector: 'mat-select-trigger'
  248. },] },
  249. ];
  250. return MatSelectTrigger;
  251. }());
  252. var MatSelect = /** @class */ (function (_super) {
  253. __extends(MatSelect, _super);
  254. function MatSelect(_viewportRuler, _changeDetectorRef, _ngZone, _defaultErrorStateMatcher, elementRef, _dir, _parentForm, _parentFormGroup, _parentFormField, ngControl, tabIndex, scrollStrategyFactory, _liveAnnouncer) {
  255. var _this = _super.call(this, elementRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl) || this;
  256. _this._viewportRuler = _viewportRuler;
  257. _this._changeDetectorRef = _changeDetectorRef;
  258. _this._ngZone = _ngZone;
  259. _this._dir = _dir;
  260. _this._parentFormField = _parentFormField;
  261. _this.ngControl = ngControl;
  262. _this._liveAnnouncer = _liveAnnouncer;
  263. /**
  264. * Whether or not the overlay panel is open.
  265. */
  266. _this._panelOpen = false;
  267. /**
  268. * Whether filling out the select is required in the form.
  269. */
  270. _this._required = false;
  271. /**
  272. * The scroll position of the overlay panel, calculated to center the selected option.
  273. */
  274. _this._scrollTop = 0;
  275. /**
  276. * Whether the component is in multiple selection mode.
  277. */
  278. _this._multiple = false;
  279. /**
  280. * Comparison function to specify which option is displayed. Defaults to object equality.
  281. */
  282. _this._compareWith = (/**
  283. * @param {?} o1
  284. * @param {?} o2
  285. * @return {?}
  286. */
  287. function (o1, o2) { return o1 === o2; });
  288. /**
  289. * Unique id for this input.
  290. */
  291. _this._uid = "mat-select-" + nextUniqueId++;
  292. /**
  293. * Emits whenever the component is destroyed.
  294. */
  295. _this._destroy = new Subject();
  296. /**
  297. * The cached font-size of the trigger element.
  298. */
  299. _this._triggerFontSize = 0;
  300. /**
  301. * `View -> model callback called when value changes`
  302. */
  303. _this._onChange = (/**
  304. * @return {?}
  305. */
  306. function () { });
  307. /**
  308. * `View -> model callback called when select has been touched`
  309. */
  310. _this._onTouched = (/**
  311. * @return {?}
  312. */
  313. function () { });
  314. /**
  315. * The IDs of child options to be passed to the aria-owns attribute.
  316. */
  317. _this._optionIds = '';
  318. /**
  319. * The value of the select panel's transform-origin property.
  320. */
  321. _this._transformOrigin = 'top';
  322. /**
  323. * Emits when the panel element is finished transforming in.
  324. */
  325. _this._panelDoneAnimatingStream = new Subject();
  326. /**
  327. * The y-offset of the overlay panel in relation to the trigger's top start corner.
  328. * This must be adjusted to align the selected option text over the trigger text.
  329. * when the panel opens. Will change based on the y-position of the selected option.
  330. */
  331. _this._offsetY = 0;
  332. /**
  333. * This position config ensures that the top "start" corner of the overlay
  334. * is aligned with with the top "start" of the origin by default (overlapping
  335. * the trigger completely). If the panel cannot fit below the trigger, it
  336. * will fall back to a position above the trigger.
  337. */
  338. _this._positions = [
  339. {
  340. originX: 'start',
  341. originY: 'top',
  342. overlayX: 'start',
  343. overlayY: 'top',
  344. },
  345. {
  346. originX: 'start',
  347. originY: 'bottom',
  348. overlayX: 'start',
  349. overlayY: 'bottom',
  350. },
  351. ];
  352. /**
  353. * Whether the component is disabling centering of the active option over the trigger.
  354. */
  355. _this._disableOptionCentering = false;
  356. _this._focused = false;
  357. /**
  358. * A name for this control that can be used by `mat-form-field`.
  359. */
  360. _this.controlType = 'mat-select';
  361. /**
  362. * Aria label of the select. If not specified, the placeholder will be used as label.
  363. */
  364. _this.ariaLabel = '';
  365. /**
  366. * Combined stream of all of the child options' change events.
  367. */
  368. _this.optionSelectionChanges = (/** @type {?} */ (defer((/**
  369. * @return {?}
  370. */
  371. function () {
  372. /** @type {?} */
  373. var options = _this.options;
  374. if (options) {
  375. return options.changes.pipe(startWith(options), switchMap((/**
  376. * @return {?}
  377. */
  378. function () { return merge.apply(void 0, options.map((/**
  379. * @param {?} option
  380. * @return {?}
  381. */
  382. function (option) { return option.onSelectionChange; }))); })));
  383. }
  384. return _this._ngZone.onStable
  385. .asObservable()
  386. .pipe(take(1), switchMap((/**
  387. * @return {?}
  388. */
  389. function () { return _this.optionSelectionChanges; })));
  390. }))));
  391. /**
  392. * Event emitted when the select panel has been toggled.
  393. */
  394. _this.openedChange = new EventEmitter();
  395. /**
  396. * Event emitted when the select has been opened.
  397. */
  398. _this._openedStream = _this.openedChange.pipe(filter((/**
  399. * @param {?} o
  400. * @return {?}
  401. */
  402. function (o) { return o; })), map((/**
  403. * @return {?}
  404. */
  405. function () { })));
  406. /**
  407. * Event emitted when the select has been closed.
  408. */
  409. _this._closedStream = _this.openedChange.pipe(filter((/**
  410. * @param {?} o
  411. * @return {?}
  412. */
  413. function (o) { return !o; })), map((/**
  414. * @return {?}
  415. */
  416. function () { })));
  417. /**
  418. * Event emitted when the selected value has been changed by the user.
  419. */
  420. _this.selectionChange = new EventEmitter();
  421. /**
  422. * Event that emits whenever the raw value of the select changes. This is here primarily
  423. * to facilitate the two-way binding for the `value` input.
  424. * \@docs-private
  425. */
  426. _this.valueChange = new EventEmitter();
  427. if (_this.ngControl) {
  428. // Note: we provide the value accessor through here, instead of
  429. // the `providers` to avoid running into a circular import.
  430. _this.ngControl.valueAccessor = _this;
  431. }
  432. _this._scrollStrategyFactory = scrollStrategyFactory;
  433. _this._scrollStrategy = _this._scrollStrategyFactory();
  434. _this.tabIndex = parseInt(tabIndex) || 0;
  435. // Force setter to be called in case id was not specified.
  436. _this.id = _this.id;
  437. return _this;
  438. }
  439. Object.defineProperty(MatSelect.prototype, "focused", {
  440. /** Whether the select is focused. */
  441. get: /**
  442. * Whether the select is focused.
  443. * @return {?}
  444. */
  445. function () {
  446. return this._focused || this._panelOpen;
  447. },
  448. /**
  449. * @deprecated Setter to be removed as this property is intended to be readonly.
  450. * @breaking-change 8.0.0
  451. */
  452. set: /**
  453. * @deprecated Setter to be removed as this property is intended to be readonly.
  454. * \@breaking-change 8.0.0
  455. * @param {?} value
  456. * @return {?}
  457. */
  458. function (value) {
  459. this._focused = value;
  460. },
  461. enumerable: true,
  462. configurable: true
  463. });
  464. Object.defineProperty(MatSelect.prototype, "placeholder", {
  465. /** Placeholder to be shown if no value has been selected. */
  466. get: /**
  467. * Placeholder to be shown if no value has been selected.
  468. * @return {?}
  469. */
  470. function () { return this._placeholder; },
  471. set: /**
  472. * @param {?} value
  473. * @return {?}
  474. */
  475. function (value) {
  476. this._placeholder = value;
  477. this.stateChanges.next();
  478. },
  479. enumerable: true,
  480. configurable: true
  481. });
  482. Object.defineProperty(MatSelect.prototype, "required", {
  483. /** Whether the component is required. */
  484. get: /**
  485. * Whether the component is required.
  486. * @return {?}
  487. */
  488. function () { return this._required; },
  489. set: /**
  490. * @param {?} value
  491. * @return {?}
  492. */
  493. function (value) {
  494. this._required = coerceBooleanProperty(value);
  495. this.stateChanges.next();
  496. },
  497. enumerable: true,
  498. configurable: true
  499. });
  500. Object.defineProperty(MatSelect.prototype, "multiple", {
  501. /** Whether the user should be allowed to select multiple options. */
  502. get: /**
  503. * Whether the user should be allowed to select multiple options.
  504. * @return {?}
  505. */
  506. function () { return this._multiple; },
  507. set: /**
  508. * @param {?} value
  509. * @return {?}
  510. */
  511. function (value) {
  512. if (this._selectionModel) {
  513. throw getMatSelectDynamicMultipleError();
  514. }
  515. this._multiple = coerceBooleanProperty(value);
  516. },
  517. enumerable: true,
  518. configurable: true
  519. });
  520. Object.defineProperty(MatSelect.prototype, "disableOptionCentering", {
  521. /** Whether to center the active option over the trigger. */
  522. get: /**
  523. * Whether to center the active option over the trigger.
  524. * @return {?}
  525. */
  526. function () { return this._disableOptionCentering; },
  527. set: /**
  528. * @param {?} value
  529. * @return {?}
  530. */
  531. function (value) {
  532. this._disableOptionCentering = coerceBooleanProperty(value);
  533. },
  534. enumerable: true,
  535. configurable: true
  536. });
  537. Object.defineProperty(MatSelect.prototype, "compareWith", {
  538. /**
  539. * Function to compare the option values with the selected values. The first argument
  540. * is a value from an option. The second is a value from the selection. A boolean
  541. * should be returned.
  542. */
  543. get: /**
  544. * Function to compare the option values with the selected values. The first argument
  545. * is a value from an option. The second is a value from the selection. A boolean
  546. * should be returned.
  547. * @return {?}
  548. */
  549. function () { return this._compareWith; },
  550. set: /**
  551. * @param {?} fn
  552. * @return {?}
  553. */
  554. function (fn) {
  555. if (typeof fn !== 'function') {
  556. throw getMatSelectNonFunctionValueError();
  557. }
  558. this._compareWith = fn;
  559. if (this._selectionModel) {
  560. // A different comparator means the selection could change.
  561. this._initializeSelection();
  562. }
  563. },
  564. enumerable: true,
  565. configurable: true
  566. });
  567. Object.defineProperty(MatSelect.prototype, "value", {
  568. /** Value of the select control. */
  569. get: /**
  570. * Value of the select control.
  571. * @return {?}
  572. */
  573. function () { return this._value; },
  574. set: /**
  575. * @param {?} newValue
  576. * @return {?}
  577. */
  578. function (newValue) {
  579. if (newValue !== this._value) {
  580. this.writeValue(newValue);
  581. this._value = newValue;
  582. }
  583. },
  584. enumerable: true,
  585. configurable: true
  586. });
  587. Object.defineProperty(MatSelect.prototype, "id", {
  588. /** Unique id of the element. */
  589. get: /**
  590. * Unique id of the element.
  591. * @return {?}
  592. */
  593. function () { return this._id; },
  594. set: /**
  595. * @param {?} value
  596. * @return {?}
  597. */
  598. function (value) {
  599. this._id = value || this._uid;
  600. this.stateChanges.next();
  601. },
  602. enumerable: true,
  603. configurable: true
  604. });
  605. /**
  606. * @return {?}
  607. */
  608. MatSelect.prototype.ngOnInit = /**
  609. * @return {?}
  610. */
  611. function () {
  612. var _this = this;
  613. this._selectionModel = new SelectionModel(this.multiple);
  614. this.stateChanges.next();
  615. // We need `distinctUntilChanged` here, because some browsers will
  616. // fire the animation end event twice for the same animation. See:
  617. // https://github.com/angular/angular/issues/24084
  618. this._panelDoneAnimatingStream
  619. .pipe(distinctUntilChanged(), takeUntil(this._destroy))
  620. .subscribe((/**
  621. * @return {?}
  622. */
  623. function () {
  624. if (_this.panelOpen) {
  625. _this._scrollTop = 0;
  626. _this.openedChange.emit(true);
  627. }
  628. else {
  629. _this.openedChange.emit(false);
  630. _this.overlayDir.offsetX = 0;
  631. _this._changeDetectorRef.markForCheck();
  632. }
  633. }));
  634. this._viewportRuler.change()
  635. .pipe(takeUntil(this._destroy))
  636. .subscribe((/**
  637. * @return {?}
  638. */
  639. function () {
  640. if (_this._panelOpen) {
  641. _this._triggerRect = _this.trigger.nativeElement.getBoundingClientRect();
  642. _this._changeDetectorRef.markForCheck();
  643. }
  644. }));
  645. };
  646. /**
  647. * @return {?}
  648. */
  649. MatSelect.prototype.ngAfterContentInit = /**
  650. * @return {?}
  651. */
  652. function () {
  653. var _this = this;
  654. this._initKeyManager();
  655. this._selectionModel.onChange.pipe(takeUntil(this._destroy)).subscribe((/**
  656. * @param {?} event
  657. * @return {?}
  658. */
  659. function (event) {
  660. event.added.forEach((/**
  661. * @param {?} option
  662. * @return {?}
  663. */
  664. function (option) { return option.select(); }));
  665. event.removed.forEach((/**
  666. * @param {?} option
  667. * @return {?}
  668. */
  669. function (option) { return option.deselect(); }));
  670. }));
  671. this.options.changes.pipe(startWith(null), takeUntil(this._destroy)).subscribe((/**
  672. * @return {?}
  673. */
  674. function () {
  675. _this._resetOptions();
  676. _this._initializeSelection();
  677. }));
  678. };
  679. /**
  680. * @return {?}
  681. */
  682. MatSelect.prototype.ngDoCheck = /**
  683. * @return {?}
  684. */
  685. function () {
  686. if (this.ngControl) {
  687. this.updateErrorState();
  688. }
  689. };
  690. /**
  691. * @param {?} changes
  692. * @return {?}
  693. */
  694. MatSelect.prototype.ngOnChanges = /**
  695. * @param {?} changes
  696. * @return {?}
  697. */
  698. function (changes) {
  699. // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let
  700. // the parent form field know to run change detection when the disabled state changes.
  701. if (changes['disabled']) {
  702. this.stateChanges.next();
  703. }
  704. };
  705. /**
  706. * @return {?}
  707. */
  708. MatSelect.prototype.ngOnDestroy = /**
  709. * @return {?}
  710. */
  711. function () {
  712. this._destroy.next();
  713. this._destroy.complete();
  714. this.stateChanges.complete();
  715. };
  716. /** Toggles the overlay panel open or closed. */
  717. /**
  718. * Toggles the overlay panel open or closed.
  719. * @return {?}
  720. */
  721. MatSelect.prototype.toggle = /**
  722. * Toggles the overlay panel open or closed.
  723. * @return {?}
  724. */
  725. function () {
  726. this.panelOpen ? this.close() : this.open();
  727. };
  728. /** Opens the overlay panel. */
  729. /**
  730. * Opens the overlay panel.
  731. * @return {?}
  732. */
  733. MatSelect.prototype.open = /**
  734. * Opens the overlay panel.
  735. * @return {?}
  736. */
  737. function () {
  738. var _this = this;
  739. if (this.disabled || !this.options || !this.options.length || this._panelOpen) {
  740. return;
  741. }
  742. this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();
  743. // Note: The computed font-size will be a string pixel value (e.g. "16px").
  744. // `parseInt` ignores the trailing 'px' and converts this to a number.
  745. this._triggerFontSize = parseInt(getComputedStyle(this.trigger.nativeElement).fontSize || '0');
  746. this._panelOpen = true;
  747. this._keyManager.withHorizontalOrientation(null);
  748. this._calculateOverlayPosition();
  749. this._highlightCorrectOption();
  750. this._changeDetectorRef.markForCheck();
  751. // Set the font size on the panel element once it exists.
  752. this._ngZone.onStable.asObservable().pipe(take(1)).subscribe((/**
  753. * @return {?}
  754. */
  755. function () {
  756. if (_this._triggerFontSize && _this.overlayDir.overlayRef &&
  757. _this.overlayDir.overlayRef.overlayElement) {
  758. _this.overlayDir.overlayRef.overlayElement.style.fontSize = _this._triggerFontSize + "px";
  759. }
  760. }));
  761. };
  762. /** Closes the overlay panel and focuses the host element. */
  763. /**
  764. * Closes the overlay panel and focuses the host element.
  765. * @return {?}
  766. */
  767. MatSelect.prototype.close = /**
  768. * Closes the overlay panel and focuses the host element.
  769. * @return {?}
  770. */
  771. function () {
  772. if (this._panelOpen) {
  773. this._panelOpen = false;
  774. this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');
  775. this._changeDetectorRef.markForCheck();
  776. this._onTouched();
  777. }
  778. };
  779. /**
  780. * Sets the select's value. Part of the ControlValueAccessor interface
  781. * required to integrate with Angular's core forms API.
  782. *
  783. * @param value New value to be written to the model.
  784. */
  785. /**
  786. * Sets the select's value. Part of the ControlValueAccessor interface
  787. * required to integrate with Angular's core forms API.
  788. *
  789. * @param {?} value New value to be written to the model.
  790. * @return {?}
  791. */
  792. MatSelect.prototype.writeValue = /**
  793. * Sets the select's value. Part of the ControlValueAccessor interface
  794. * required to integrate with Angular's core forms API.
  795. *
  796. * @param {?} value New value to be written to the model.
  797. * @return {?}
  798. */
  799. function (value) {
  800. if (this.options) {
  801. this._setSelectionByValue(value);
  802. }
  803. };
  804. /**
  805. * Saves a callback function to be invoked when the select's value
  806. * changes from user input. Part of the ControlValueAccessor interface
  807. * required to integrate with Angular's core forms API.
  808. *
  809. * @param fn Callback to be triggered when the value changes.
  810. */
  811. /**
  812. * Saves a callback function to be invoked when the select's value
  813. * changes from user input. Part of the ControlValueAccessor interface
  814. * required to integrate with Angular's core forms API.
  815. *
  816. * @param {?} fn Callback to be triggered when the value changes.
  817. * @return {?}
  818. */
  819. MatSelect.prototype.registerOnChange = /**
  820. * Saves a callback function to be invoked when the select's value
  821. * changes from user input. Part of the ControlValueAccessor interface
  822. * required to integrate with Angular's core forms API.
  823. *
  824. * @param {?} fn Callback to be triggered when the value changes.
  825. * @return {?}
  826. */
  827. function (fn) {
  828. this._onChange = fn;
  829. };
  830. /**
  831. * Saves a callback function to be invoked when the select is blurred
  832. * by the user. Part of the ControlValueAccessor interface required
  833. * to integrate with Angular's core forms API.
  834. *
  835. * @param fn Callback to be triggered when the component has been touched.
  836. */
  837. /**
  838. * Saves a callback function to be invoked when the select is blurred
  839. * by the user. Part of the ControlValueAccessor interface required
  840. * to integrate with Angular's core forms API.
  841. *
  842. * @param {?} fn Callback to be triggered when the component has been touched.
  843. * @return {?}
  844. */
  845. MatSelect.prototype.registerOnTouched = /**
  846. * Saves a callback function to be invoked when the select is blurred
  847. * by the user. Part of the ControlValueAccessor interface required
  848. * to integrate with Angular's core forms API.
  849. *
  850. * @param {?} fn Callback to be triggered when the component has been touched.
  851. * @return {?}
  852. */
  853. function (fn) {
  854. this._onTouched = fn;
  855. };
  856. /**
  857. * Disables the select. Part of the ControlValueAccessor interface required
  858. * to integrate with Angular's core forms API.
  859. *
  860. * @param isDisabled Sets whether the component is disabled.
  861. */
  862. /**
  863. * Disables the select. Part of the ControlValueAccessor interface required
  864. * to integrate with Angular's core forms API.
  865. *
  866. * @param {?} isDisabled Sets whether the component is disabled.
  867. * @return {?}
  868. */
  869. MatSelect.prototype.setDisabledState = /**
  870. * Disables the select. Part of the ControlValueAccessor interface required
  871. * to integrate with Angular's core forms API.
  872. *
  873. * @param {?} isDisabled Sets whether the component is disabled.
  874. * @return {?}
  875. */
  876. function (isDisabled) {
  877. this.disabled = isDisabled;
  878. this._changeDetectorRef.markForCheck();
  879. this.stateChanges.next();
  880. };
  881. Object.defineProperty(MatSelect.prototype, "panelOpen", {
  882. /** Whether or not the overlay panel is open. */
  883. get: /**
  884. * Whether or not the overlay panel is open.
  885. * @return {?}
  886. */
  887. function () {
  888. return this._panelOpen;
  889. },
  890. enumerable: true,
  891. configurable: true
  892. });
  893. Object.defineProperty(MatSelect.prototype, "selected", {
  894. /** The currently selected option. */
  895. get: /**
  896. * The currently selected option.
  897. * @return {?}
  898. */
  899. function () {
  900. return this.multiple ? this._selectionModel.selected : this._selectionModel.selected[0];
  901. },
  902. enumerable: true,
  903. configurable: true
  904. });
  905. Object.defineProperty(MatSelect.prototype, "triggerValue", {
  906. /** The value displayed in the trigger. */
  907. get: /**
  908. * The value displayed in the trigger.
  909. * @return {?}
  910. */
  911. function () {
  912. if (this.empty) {
  913. return '';
  914. }
  915. if (this._multiple) {
  916. /** @type {?} */
  917. var selectedOptions = this._selectionModel.selected.map((/**
  918. * @param {?} option
  919. * @return {?}
  920. */
  921. function (option) { return option.viewValue; }));
  922. if (this._isRtl()) {
  923. selectedOptions.reverse();
  924. }
  925. // TODO(crisbeto): delimiter should be configurable for proper localization.
  926. return selectedOptions.join(', ');
  927. }
  928. return this._selectionModel.selected[0].viewValue;
  929. },
  930. enumerable: true,
  931. configurable: true
  932. });
  933. /** Whether the element is in RTL mode. */
  934. /**
  935. * Whether the element is in RTL mode.
  936. * @return {?}
  937. */
  938. MatSelect.prototype._isRtl = /**
  939. * Whether the element is in RTL mode.
  940. * @return {?}
  941. */
  942. function () {
  943. return this._dir ? this._dir.value === 'rtl' : false;
  944. };
  945. /** Handles all keydown events on the select. */
  946. /**
  947. * Handles all keydown events on the select.
  948. * @param {?} event
  949. * @return {?}
  950. */
  951. MatSelect.prototype._handleKeydown = /**
  952. * Handles all keydown events on the select.
  953. * @param {?} event
  954. * @return {?}
  955. */
  956. function (event) {
  957. if (!this.disabled) {
  958. this.panelOpen ? this._handleOpenKeydown(event) : this._handleClosedKeydown(event);
  959. }
  960. };
  961. /** Handles keyboard events while the select is closed. */
  962. /**
  963. * Handles keyboard events while the select is closed.
  964. * @private
  965. * @param {?} event
  966. * @return {?}
  967. */
  968. MatSelect.prototype._handleClosedKeydown = /**
  969. * Handles keyboard events while the select is closed.
  970. * @private
  971. * @param {?} event
  972. * @return {?}
  973. */
  974. function (event) {
  975. /** @type {?} */
  976. var keyCode = event.keyCode;
  977. /** @type {?} */
  978. var isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW ||
  979. keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW;
  980. /** @type {?} */
  981. var isOpenKey = keyCode === ENTER || keyCode === SPACE;
  982. /** @type {?} */
  983. var manager = this._keyManager;
  984. // Open the select on ALT + arrow key to match the native <select>
  985. if ((isOpenKey && !hasModifierKey(event)) || ((this.multiple || event.altKey) && isArrowKey)) {
  986. event.preventDefault(); // prevents the page from scrolling down when pressing space
  987. this.open();
  988. }
  989. else if (!this.multiple) {
  990. /** @type {?} */
  991. var previouslySelectedOption = this.selected;
  992. if (keyCode === HOME || keyCode === END) {
  993. keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
  994. event.preventDefault();
  995. }
  996. else {
  997. manager.onKeydown(event);
  998. }
  999. /** @type {?} */
  1000. var selectedOption = this.selected;
  1001. // Since the value has changed, we need to announce it ourselves.
  1002. // @breaking-change 8.0.0 remove null check for _liveAnnouncer.
  1003. if (this._liveAnnouncer && selectedOption && previouslySelectedOption !== selectedOption) {
  1004. // We set a duration on the live announcement, because we want the live element to be
  1005. // cleared after a while so that users can't navigate to it using the arrow keys.
  1006. this._liveAnnouncer.announce(((/** @type {?} */ (selectedOption))).viewValue, 10000);
  1007. }
  1008. }
  1009. };
  1010. /** Handles keyboard events when the selected is open. */
  1011. /**
  1012. * Handles keyboard events when the selected is open.
  1013. * @private
  1014. * @param {?} event
  1015. * @return {?}
  1016. */
  1017. MatSelect.prototype._handleOpenKeydown = /**
  1018. * Handles keyboard events when the selected is open.
  1019. * @private
  1020. * @param {?} event
  1021. * @return {?}
  1022. */
  1023. function (event) {
  1024. /** @type {?} */
  1025. var keyCode = event.keyCode;
  1026. /** @type {?} */
  1027. var isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
  1028. /** @type {?} */
  1029. var manager = this._keyManager;
  1030. if (keyCode === HOME || keyCode === END) {
  1031. event.preventDefault();
  1032. keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
  1033. }
  1034. else if (isArrowKey && event.altKey) {
  1035. // Close the select on ALT + arrow key to match the native <select>
  1036. event.preventDefault();
  1037. this.close();
  1038. }
  1039. else if ((keyCode === ENTER || keyCode === SPACE) && manager.activeItem &&
  1040. !hasModifierKey(event)) {
  1041. event.preventDefault();
  1042. manager.activeItem._selectViaInteraction();
  1043. }
  1044. else if (this._multiple && keyCode === A && event.ctrlKey) {
  1045. event.preventDefault();
  1046. /** @type {?} */
  1047. var hasDeselectedOptions_1 = this.options.some((/**
  1048. * @param {?} opt
  1049. * @return {?}
  1050. */
  1051. function (opt) { return !opt.disabled && !opt.selected; }));
  1052. this.options.forEach((/**
  1053. * @param {?} option
  1054. * @return {?}
  1055. */
  1056. function (option) {
  1057. if (!option.disabled) {
  1058. hasDeselectedOptions_1 ? option.select() : option.deselect();
  1059. }
  1060. }));
  1061. }
  1062. else {
  1063. /** @type {?} */
  1064. var previouslyFocusedIndex = manager.activeItemIndex;
  1065. manager.onKeydown(event);
  1066. if (this._multiple && isArrowKey && event.shiftKey && manager.activeItem &&
  1067. manager.activeItemIndex !== previouslyFocusedIndex) {
  1068. manager.activeItem._selectViaInteraction();
  1069. }
  1070. }
  1071. };
  1072. /**
  1073. * @return {?}
  1074. */
  1075. MatSelect.prototype._onFocus = /**
  1076. * @return {?}
  1077. */
  1078. function () {
  1079. if (!this.disabled) {
  1080. this._focused = true;
  1081. this.stateChanges.next();
  1082. }
  1083. };
  1084. /**
  1085. * Calls the touched callback only if the panel is closed. Otherwise, the trigger will
  1086. * "blur" to the panel when it opens, causing a false positive.
  1087. */
  1088. /**
  1089. * Calls the touched callback only if the panel is closed. Otherwise, the trigger will
  1090. * "blur" to the panel when it opens, causing a false positive.
  1091. * @return {?}
  1092. */
  1093. MatSelect.prototype._onBlur = /**
  1094. * Calls the touched callback only if the panel is closed. Otherwise, the trigger will
  1095. * "blur" to the panel when it opens, causing a false positive.
  1096. * @return {?}
  1097. */
  1098. function () {
  1099. this._focused = false;
  1100. if (!this.disabled && !this.panelOpen) {
  1101. this._onTouched();
  1102. this._changeDetectorRef.markForCheck();
  1103. this.stateChanges.next();
  1104. }
  1105. };
  1106. /**
  1107. * Callback that is invoked when the overlay panel has been attached.
  1108. */
  1109. /**
  1110. * Callback that is invoked when the overlay panel has been attached.
  1111. * @return {?}
  1112. */
  1113. MatSelect.prototype._onAttached = /**
  1114. * Callback that is invoked when the overlay panel has been attached.
  1115. * @return {?}
  1116. */
  1117. function () {
  1118. var _this = this;
  1119. this.overlayDir.positionChange.pipe(take(1)).subscribe((/**
  1120. * @return {?}
  1121. */
  1122. function () {
  1123. _this._changeDetectorRef.detectChanges();
  1124. _this._calculateOverlayOffsetX();
  1125. _this.panel.nativeElement.scrollTop = _this._scrollTop;
  1126. }));
  1127. };
  1128. /** Returns the theme to be used on the panel. */
  1129. /**
  1130. * Returns the theme to be used on the panel.
  1131. * @return {?}
  1132. */
  1133. MatSelect.prototype._getPanelTheme = /**
  1134. * Returns the theme to be used on the panel.
  1135. * @return {?}
  1136. */
  1137. function () {
  1138. return this._parentFormField ? "mat-" + this._parentFormField.color : '';
  1139. };
  1140. Object.defineProperty(MatSelect.prototype, "empty", {
  1141. /** Whether the select has a value. */
  1142. get: /**
  1143. * Whether the select has a value.
  1144. * @return {?}
  1145. */
  1146. function () {
  1147. return !this._selectionModel || this._selectionModel.isEmpty();
  1148. },
  1149. enumerable: true,
  1150. configurable: true
  1151. });
  1152. /**
  1153. * @private
  1154. * @return {?}
  1155. */
  1156. MatSelect.prototype._initializeSelection = /**
  1157. * @private
  1158. * @return {?}
  1159. */
  1160. function () {
  1161. var _this = this;
  1162. // Defer setting the value in order to avoid the "Expression
  1163. // has changed after it was checked" errors from Angular.
  1164. Promise.resolve().then((/**
  1165. * @return {?}
  1166. */
  1167. function () {
  1168. _this._setSelectionByValue(_this.ngControl ? _this.ngControl.value : _this._value);
  1169. _this.stateChanges.next();
  1170. }));
  1171. };
  1172. /**
  1173. * Sets the selected option based on a value. If no option can be
  1174. * found with the designated value, the select trigger is cleared.
  1175. */
  1176. /**
  1177. * Sets the selected option based on a value. If no option can be
  1178. * found with the designated value, the select trigger is cleared.
  1179. * @private
  1180. * @param {?} value
  1181. * @return {?}
  1182. */
  1183. MatSelect.prototype._setSelectionByValue = /**
  1184. * Sets the selected option based on a value. If no option can be
  1185. * found with the designated value, the select trigger is cleared.
  1186. * @private
  1187. * @param {?} value
  1188. * @return {?}
  1189. */
  1190. function (value) {
  1191. var _this = this;
  1192. if (this.multiple && value) {
  1193. if (!Array.isArray(value)) {
  1194. throw getMatSelectNonArrayValueError();
  1195. }
  1196. this._selectionModel.clear();
  1197. value.forEach((/**
  1198. * @param {?} currentValue
  1199. * @return {?}
  1200. */
  1201. function (currentValue) { return _this._selectValue(currentValue); }));
  1202. this._sortValues();
  1203. }
  1204. else {
  1205. this._selectionModel.clear();
  1206. /** @type {?} */
  1207. var correspondingOption = this._selectValue(value);
  1208. // Shift focus to the active item. Note that we shouldn't do this in multiple
  1209. // mode, because we don't know what option the user interacted with last.
  1210. if (correspondingOption) {
  1211. this._keyManager.setActiveItem(correspondingOption);
  1212. }
  1213. }
  1214. this._changeDetectorRef.markForCheck();
  1215. };
  1216. /**
  1217. * Finds and selects and option based on its value.
  1218. * @returns Option that has the corresponding value.
  1219. */
  1220. /**
  1221. * Finds and selects and option based on its value.
  1222. * @private
  1223. * @param {?} value
  1224. * @return {?} Option that has the corresponding value.
  1225. */
  1226. MatSelect.prototype._selectValue = /**
  1227. * Finds and selects and option based on its value.
  1228. * @private
  1229. * @param {?} value
  1230. * @return {?} Option that has the corresponding value.
  1231. */
  1232. function (value) {
  1233. var _this = this;
  1234. /** @type {?} */
  1235. var correspondingOption = this.options.find((/**
  1236. * @param {?} option
  1237. * @return {?}
  1238. */
  1239. function (option) {
  1240. try {
  1241. // Treat null as a special reset value.
  1242. return option.value != null && _this._compareWith(option.value, value);
  1243. }
  1244. catch (error) {
  1245. if (isDevMode()) {
  1246. // Notify developers of errors in their comparator.
  1247. console.warn(error);
  1248. }
  1249. return false;
  1250. }
  1251. }));
  1252. if (correspondingOption) {
  1253. this._selectionModel.select(correspondingOption);
  1254. }
  1255. return correspondingOption;
  1256. };
  1257. /** Sets up a key manager to listen to keyboard events on the overlay panel. */
  1258. /**
  1259. * Sets up a key manager to listen to keyboard events on the overlay panel.
  1260. * @private
  1261. * @return {?}
  1262. */
  1263. MatSelect.prototype._initKeyManager = /**
  1264. * Sets up a key manager to listen to keyboard events on the overlay panel.
  1265. * @private
  1266. * @return {?}
  1267. */
  1268. function () {
  1269. var _this = this;
  1270. this._keyManager = new ActiveDescendantKeyManager(this.options)
  1271. .withTypeAhead()
  1272. .withVerticalOrientation()
  1273. .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')
  1274. .withAllowedModifierKeys(['shiftKey']);
  1275. this._keyManager.tabOut.pipe(takeUntil(this._destroy)).subscribe((/**
  1276. * @return {?}
  1277. */
  1278. function () {
  1279. // Restore focus to the trigger before closing. Ensures that the focus
  1280. // position won't be lost if the user got focus into the overlay.
  1281. _this.focus();
  1282. _this.close();
  1283. }));
  1284. this._keyManager.change.pipe(takeUntil(this._destroy)).subscribe((/**
  1285. * @return {?}
  1286. */
  1287. function () {
  1288. if (_this._panelOpen && _this.panel) {
  1289. _this._scrollActiveOptionIntoView();
  1290. }
  1291. else if (!_this._panelOpen && !_this.multiple && _this._keyManager.activeItem) {
  1292. _this._keyManager.activeItem._selectViaInteraction();
  1293. }
  1294. }));
  1295. };
  1296. /** Drops current option subscriptions and IDs and resets from scratch. */
  1297. /**
  1298. * Drops current option subscriptions and IDs and resets from scratch.
  1299. * @private
  1300. * @return {?}
  1301. */
  1302. MatSelect.prototype._resetOptions = /**
  1303. * Drops current option subscriptions and IDs and resets from scratch.
  1304. * @private
  1305. * @return {?}
  1306. */
  1307. function () {
  1308. var _this = this;
  1309. /** @type {?} */
  1310. var changedOrDestroyed = merge(this.options.changes, this._destroy);
  1311. this.optionSelectionChanges.pipe(takeUntil(changedOrDestroyed)).subscribe((/**
  1312. * @param {?} event
  1313. * @return {?}
  1314. */
  1315. function (event) {
  1316. _this._onSelect(event.source, event.isUserInput);
  1317. if (event.isUserInput && !_this.multiple && _this._panelOpen) {
  1318. _this.close();
  1319. _this.focus();
  1320. }
  1321. }));
  1322. // Listen to changes in the internal state of the options and react accordingly.
  1323. // Handles cases like the labels of the selected options changing.
  1324. merge.apply(void 0, this.options.map((/**
  1325. * @param {?} option
  1326. * @return {?}
  1327. */
  1328. function (option) { return option._stateChanges; }))).pipe(takeUntil(changedOrDestroyed))
  1329. .subscribe((/**
  1330. * @return {?}
  1331. */
  1332. function () {
  1333. _this._changeDetectorRef.markForCheck();
  1334. _this.stateChanges.next();
  1335. }));
  1336. this._setOptionIds();
  1337. };
  1338. /** Invoked when an option is clicked. */
  1339. /**
  1340. * Invoked when an option is clicked.
  1341. * @private
  1342. * @param {?} option
  1343. * @param {?} isUserInput
  1344. * @return {?}
  1345. */
  1346. MatSelect.prototype._onSelect = /**
  1347. * Invoked when an option is clicked.
  1348. * @private
  1349. * @param {?} option
  1350. * @param {?} isUserInput
  1351. * @return {?}
  1352. */
  1353. function (option, isUserInput) {
  1354. /** @type {?} */
  1355. var wasSelected = this._selectionModel.isSelected(option);
  1356. if (option.value == null && !this._multiple) {
  1357. option.deselect();
  1358. this._selectionModel.clear();
  1359. this._propagateChanges(option.value);
  1360. }
  1361. else {
  1362. option.selected ? this._selectionModel.select(option) : this._selectionModel.deselect(option);
  1363. if (isUserInput) {
  1364. this._keyManager.setActiveItem(option);
  1365. }
  1366. if (this.multiple) {
  1367. this._sortValues();
  1368. if (isUserInput) {
  1369. // In case the user selected the option with their mouse, we
  1370. // want to restore focus back to the trigger, in order to
  1371. // prevent the select keyboard controls from clashing with
  1372. // the ones from `mat-option`.
  1373. this.focus();
  1374. }
  1375. }
  1376. }
  1377. if (wasSelected !== this._selectionModel.isSelected(option)) {
  1378. this._propagateChanges();
  1379. }
  1380. this.stateChanges.next();
  1381. };
  1382. /** Sorts the selected values in the selected based on their order in the panel. */
  1383. /**
  1384. * Sorts the selected values in the selected based on their order in the panel.
  1385. * @private
  1386. * @return {?}
  1387. */
  1388. MatSelect.prototype._sortValues = /**
  1389. * Sorts the selected values in the selected based on their order in the panel.
  1390. * @private
  1391. * @return {?}
  1392. */
  1393. function () {
  1394. var _this = this;
  1395. if (this.multiple) {
  1396. /** @type {?} */
  1397. var options_1 = this.options.toArray();
  1398. this._selectionModel.sort((/**
  1399. * @param {?} a
  1400. * @param {?} b
  1401. * @return {?}
  1402. */
  1403. function (a, b) {
  1404. return _this.sortComparator ? _this.sortComparator(a, b, options_1) :
  1405. options_1.indexOf(a) - options_1.indexOf(b);
  1406. }));
  1407. this.stateChanges.next();
  1408. }
  1409. };
  1410. /** Emits change event to set the model value. */
  1411. /**
  1412. * Emits change event to set the model value.
  1413. * @private
  1414. * @param {?=} fallbackValue
  1415. * @return {?}
  1416. */
  1417. MatSelect.prototype._propagateChanges = /**
  1418. * Emits change event to set the model value.
  1419. * @private
  1420. * @param {?=} fallbackValue
  1421. * @return {?}
  1422. */
  1423. function (fallbackValue) {
  1424. /** @type {?} */
  1425. var valueToEmit = null;
  1426. if (this.multiple) {
  1427. valueToEmit = ((/** @type {?} */ (this.selected))).map((/**
  1428. * @param {?} option
  1429. * @return {?}
  1430. */
  1431. function (option) { return option.value; }));
  1432. }
  1433. else {
  1434. valueToEmit = this.selected ? ((/** @type {?} */ (this.selected))).value : fallbackValue;
  1435. }
  1436. this._value = valueToEmit;
  1437. this.valueChange.emit(valueToEmit);
  1438. this._onChange(valueToEmit);
  1439. this.selectionChange.emit(new MatSelectChange(this, valueToEmit));
  1440. this._changeDetectorRef.markForCheck();
  1441. };
  1442. /** Records option IDs to pass to the aria-owns property. */
  1443. /**
  1444. * Records option IDs to pass to the aria-owns property.
  1445. * @private
  1446. * @return {?}
  1447. */
  1448. MatSelect.prototype._setOptionIds = /**
  1449. * Records option IDs to pass to the aria-owns property.
  1450. * @private
  1451. * @return {?}
  1452. */
  1453. function () {
  1454. this._optionIds = this.options.map((/**
  1455. * @param {?} option
  1456. * @return {?}
  1457. */
  1458. function (option) { return option.id; })).join(' ');
  1459. };
  1460. /**
  1461. * Highlights the selected item. If no option is selected, it will highlight
  1462. * the first item instead.
  1463. */
  1464. /**
  1465. * Highlights the selected item. If no option is selected, it will highlight
  1466. * the first item instead.
  1467. * @private
  1468. * @return {?}
  1469. */
  1470. MatSelect.prototype._highlightCorrectOption = /**
  1471. * Highlights the selected item. If no option is selected, it will highlight
  1472. * the first item instead.
  1473. * @private
  1474. * @return {?}
  1475. */
  1476. function () {
  1477. if (this._keyManager) {
  1478. if (this.empty) {
  1479. this._keyManager.setFirstItemActive();
  1480. }
  1481. else {
  1482. this._keyManager.setActiveItem(this._selectionModel.selected[0]);
  1483. }
  1484. }
  1485. };
  1486. /** Scrolls the active option into view. */
  1487. /**
  1488. * Scrolls the active option into view.
  1489. * @private
  1490. * @return {?}
  1491. */
  1492. MatSelect.prototype._scrollActiveOptionIntoView = /**
  1493. * Scrolls the active option into view.
  1494. * @private
  1495. * @return {?}
  1496. */
  1497. function () {
  1498. /** @type {?} */
  1499. var activeOptionIndex = this._keyManager.activeItemIndex || 0;
  1500. /** @type {?} */
  1501. var labelCount = _countGroupLabelsBeforeOption(activeOptionIndex, this.options, this.optionGroups);
  1502. this.panel.nativeElement.scrollTop = _getOptionScrollPosition(activeOptionIndex + labelCount, this._getItemHeight(), this.panel.nativeElement.scrollTop, SELECT_PANEL_MAX_HEIGHT);
  1503. };
  1504. /** Focuses the select element. */
  1505. /**
  1506. * Focuses the select element.
  1507. * @param {?=} options
  1508. * @return {?}
  1509. */
  1510. MatSelect.prototype.focus = /**
  1511. * Focuses the select element.
  1512. * @param {?=} options
  1513. * @return {?}
  1514. */
  1515. function (options) {
  1516. this._elementRef.nativeElement.focus(options);
  1517. };
  1518. /** Gets the index of the provided option in the option list. */
  1519. /**
  1520. * Gets the index of the provided option in the option list.
  1521. * @private
  1522. * @param {?} option
  1523. * @return {?}
  1524. */
  1525. MatSelect.prototype._getOptionIndex = /**
  1526. * Gets the index of the provided option in the option list.
  1527. * @private
  1528. * @param {?} option
  1529. * @return {?}
  1530. */
  1531. function (option) {
  1532. return this.options.reduce((/**
  1533. * @param {?} result
  1534. * @param {?} current
  1535. * @param {?} index
  1536. * @return {?}
  1537. */
  1538. function (result, current, index) {
  1539. return result === undefined ? (option === current ? index : undefined) : result;
  1540. }), undefined);
  1541. };
  1542. /** Calculates the scroll position and x- and y-offsets of the overlay panel. */
  1543. /**
  1544. * Calculates the scroll position and x- and y-offsets of the overlay panel.
  1545. * @private
  1546. * @return {?}
  1547. */
  1548. MatSelect.prototype._calculateOverlayPosition = /**
  1549. * Calculates the scroll position and x- and y-offsets of the overlay panel.
  1550. * @private
  1551. * @return {?}
  1552. */
  1553. function () {
  1554. /** @type {?} */
  1555. var itemHeight = this._getItemHeight();
  1556. /** @type {?} */
  1557. var items = this._getItemCount();
  1558. /** @type {?} */
  1559. var panelHeight = Math.min(items * itemHeight, SELECT_PANEL_MAX_HEIGHT);
  1560. /** @type {?} */
  1561. var scrollContainerHeight = items * itemHeight;
  1562. // The farthest the panel can be scrolled before it hits the bottom
  1563. /** @type {?} */
  1564. var maxScroll = scrollContainerHeight - panelHeight;
  1565. // If no value is selected we open the popup to the first item.
  1566. /** @type {?} */
  1567. var selectedOptionOffset = this.empty ? 0 : (/** @type {?} */ (this._getOptionIndex(this._selectionModel.selected[0])));
  1568. selectedOptionOffset += _countGroupLabelsBeforeOption(selectedOptionOffset, this.options, this.optionGroups);
  1569. // We must maintain a scroll buffer so the selected option will be scrolled to the
  1570. // center of the overlay panel rather than the top.
  1571. /** @type {?} */
  1572. var scrollBuffer = panelHeight / 2;
  1573. this._scrollTop = this._calculateOverlayScroll(selectedOptionOffset, scrollBuffer, maxScroll);
  1574. this._offsetY = this._calculateOverlayOffsetY(selectedOptionOffset, scrollBuffer, maxScroll);
  1575. this._checkOverlayWithinViewport(maxScroll);
  1576. };
  1577. /**
  1578. * Calculates the scroll position of the select's overlay panel.
  1579. *
  1580. * Attempts to center the selected option in the panel. If the option is
  1581. * too high or too low in the panel to be scrolled to the center, it clamps the
  1582. * scroll position to the min or max scroll positions respectively.
  1583. */
  1584. /**
  1585. * Calculates the scroll position of the select's overlay panel.
  1586. *
  1587. * Attempts to center the selected option in the panel. If the option is
  1588. * too high or too low in the panel to be scrolled to the center, it clamps the
  1589. * scroll position to the min or max scroll positions respectively.
  1590. * @param {?} selectedIndex
  1591. * @param {?} scrollBuffer
  1592. * @param {?} maxScroll
  1593. * @return {?}
  1594. */
  1595. MatSelect.prototype._calculateOverlayScroll = /**
  1596. * Calculates the scroll position of the select's overlay panel.
  1597. *
  1598. * Attempts to center the selected option in the panel. If the option is
  1599. * too high or too low in the panel to be scrolled to the center, it clamps the
  1600. * scroll position to the min or max scroll positions respectively.
  1601. * @param {?} selectedIndex
  1602. * @param {?} scrollBuffer
  1603. * @param {?} maxScroll
  1604. * @return {?}
  1605. */
  1606. function (selectedIndex, scrollBuffer, maxScroll) {
  1607. /** @type {?} */
  1608. var itemHeight = this._getItemHeight();
  1609. /** @type {?} */
  1610. var optionOffsetFromScrollTop = itemHeight * selectedIndex;
  1611. /** @type {?} */
  1612. var halfOptionHeight = itemHeight / 2;
  1613. // Starts at the optionOffsetFromScrollTop, which scrolls the option to the top of the
  1614. // scroll container, then subtracts the scroll buffer to scroll the option down to
  1615. // the center of the overlay panel. Half the option height must be re-added to the
  1616. // scrollTop so the option is centered based on its middle, not its top edge.
  1617. /** @type {?} */
  1618. var optimalScrollPosition = optionOffsetFromScrollTop - scrollBuffer + halfOptionHeight;
  1619. return Math.min(Math.max(0, optimalScrollPosition), maxScroll);
  1620. };
  1621. /** Returns the aria-label of the select component. */
  1622. /**
  1623. * Returns the aria-label of the select component.
  1624. * @return {?}
  1625. */
  1626. MatSelect.prototype._getAriaLabel = /**
  1627. * Returns the aria-label of the select component.
  1628. * @return {?}
  1629. */
  1630. function () {
  1631. // If an ariaLabelledby value has been set by the consumer, the select should not overwrite the
  1632. // `aria-labelledby` value by setting the ariaLabel to the placeholder.
  1633. return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder;
  1634. };
  1635. /** Returns the aria-labelledby of the select component. */
  1636. /**
  1637. * Returns the aria-labelledby of the select component.
  1638. * @return {?}
  1639. */
  1640. MatSelect.prototype._getAriaLabelledby = /**
  1641. * Returns the aria-labelledby of the select component.
  1642. * @return {?}
  1643. */
  1644. function () {
  1645. if (this.ariaLabelledby) {
  1646. return this.ariaLabelledby;
  1647. }
  1648. // Note: we use `_getAriaLabel` here, because we want to check whether there's a
  1649. // computed label. `this.ariaLabel` is only the user-specified label.
  1650. if (!this._parentFormField || !this._parentFormField._hasFloatingLabel() ||
  1651. this._getAriaLabel()) {
  1652. return null;
  1653. }
  1654. return this._parentFormField._labelId || null;
  1655. };
  1656. /** Determines the `aria-activedescendant` to be set on the host. */
  1657. /**
  1658. * Determines the `aria-activedescendant` to be set on the host.
  1659. * @return {?}
  1660. */
  1661. MatSelect.prototype._getAriaActiveDescendant = /**
  1662. * Determines the `aria-activedescendant` to be set on the host.
  1663. * @return {?}
  1664. */
  1665. function () {
  1666. if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {
  1667. return this._keyManager.activeItem.id;
  1668. }
  1669. return null;
  1670. };
  1671. /**
  1672. * Sets the x-offset of the overlay panel in relation to the trigger's top start corner.
  1673. * This must be adjusted to align the selected option text over the trigger text when
  1674. * the panel opens. Will change based on LTR or RTL text direction. Note that the offset
  1675. * can't be calculated until the panel has been attached, because we need to know the
  1676. * content width in order to constrain the panel within the viewport.
  1677. */
  1678. /**
  1679. * Sets the x-offset of the overlay panel in relation to the trigger's top start corner.
  1680. * This must be adjusted to align the selected option text over the trigger text when
  1681. * the panel opens. Will change based on LTR or RTL text direction. Note that the offset
  1682. * can't be calculated until the panel has been attached, because we need to know the
  1683. * content width in order to constrain the panel within the viewport.
  1684. * @private
  1685. * @return {?}
  1686. */
  1687. MatSelect.prototype._calculateOverlayOffsetX = /**
  1688. * Sets the x-offset of the overlay panel in relation to the trigger's top start corner.
  1689. * This must be adjusted to align the selected option text over the trigger text when
  1690. * the panel opens. Will change based on LTR or RTL text direction. Note that the offset
  1691. * can't be calculated until the panel has been attached, because we need to know the
  1692. * content width in order to constrain the panel within the viewport.
  1693. * @private
  1694. * @return {?}
  1695. */
  1696. function () {
  1697. /** @type {?} */
  1698. var overlayRect = this.overlayDir.overlayRef.overlayElement.getBoundingClientRect();
  1699. /** @type {?} */
  1700. var viewportSize = this._viewportRuler.getViewportSize();
  1701. /** @type {?} */
  1702. var isRtl = this._isRtl();
  1703. /** @type {?} */
  1704. var paddingWidth = this.multiple ? SELECT_MULTIPLE_PANEL_PADDING_X + SELECT_PANEL_PADDING_X :
  1705. SELECT_PANEL_PADDING_X * 2;
  1706. /** @type {?} */
  1707. var offsetX;
  1708. // Adjust the offset, depending on the option padding.
  1709. if (this.multiple) {
  1710. offsetX = SELECT_MULTIPLE_PANEL_PADDING_X;
  1711. }
  1712. else {
  1713. /** @type {?} */
  1714. var selected = this._selectionModel.selected[0] || this.options.first;
  1715. offsetX = selected && selected.group ? SELECT_PANEL_INDENT_PADDING_X : SELECT_PANEL_PADDING_X;
  1716. }
  1717. // Invert the offset in LTR.
  1718. if (!isRtl) {
  1719. offsetX *= -1;
  1720. }
  1721. // Determine how much the select overflows on each side.
  1722. /** @type {?} */
  1723. var leftOverflow = 0 - (overlayRect.left + offsetX - (isRtl ? paddingWidth : 0));
  1724. /** @type {?} */
  1725. var rightOverflow = overlayRect.right + offsetX - viewportSize.width
  1726. + (isRtl ? 0 : paddingWidth);
  1727. // If the element overflows on either side, reduce the offset to allow it to fit.
  1728. if (leftOverflow > 0) {
  1729. offsetX += leftOverflow + SELECT_PANEL_VIEWPORT_PADDING;
  1730. }
  1731. else if (rightOverflow > 0) {
  1732. offsetX -= rightOverflow + SELECT_PANEL_VIEWPORT_PADDING;
  1733. }
  1734. // Set the offset directly in order to avoid having to go through change detection and
  1735. // potentially triggering "changed after it was checked" errors. Round the value to avoid
  1736. // blurry content in some browsers.
  1737. this.overlayDir.offsetX = Math.round(offsetX);
  1738. this.overlayDir.overlayRef.updatePosition();
  1739. };
  1740. /**
  1741. * Calculates the y-offset of the select's overlay panel in relation to the
  1742. * top start corner of the trigger. It has to be adjusted in order for the
  1743. * selected option to be aligned over the trigger when the panel opens.
  1744. */
  1745. /**
  1746. * Calculates the y-offset of the select's overlay panel in relation to the
  1747. * top start corner of the trigger. It has to be adjusted in order for the
  1748. * selected option to be aligned over the trigger when the panel opens.
  1749. * @private
  1750. * @param {?} selectedIndex
  1751. * @param {?} scrollBuffer
  1752. * @param {?} maxScroll
  1753. * @return {?}
  1754. */
  1755. MatSelect.prototype._calculateOverlayOffsetY = /**
  1756. * Calculates the y-offset of the select's overlay panel in relation to the
  1757. * top start corner of the trigger. It has to be adjusted in order for the
  1758. * selected option to be aligned over the trigger when the panel opens.
  1759. * @private
  1760. * @param {?} selectedIndex
  1761. * @param {?} scrollBuffer
  1762. * @param {?} maxScroll
  1763. * @return {?}
  1764. */
  1765. function (selectedIndex, scrollBuffer, maxScroll) {
  1766. /** @type {?} */
  1767. var itemHeight = this._getItemHeight();
  1768. /** @type {?} */
  1769. var optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2;
  1770. /** @type {?} */
  1771. var maxOptionsDisplayed = Math.floor(SELECT_PANEL_MAX_HEIGHT / itemHeight);
  1772. /** @type {?} */
  1773. var optionOffsetFromPanelTop;
  1774. // Disable offset if requested by user by returning 0 as value to offset
  1775. if (this._disableOptionCentering) {
  1776. return 0;
  1777. }
  1778. if (this._scrollTop === 0) {
  1779. optionOffsetFromPanelTop = selectedIndex * itemHeight;
  1780. }
  1781. else if (this._scrollTop === maxScroll) {
  1782. /** @type {?} */
  1783. var firstDisplayedIndex = this._getItemCount() - maxOptionsDisplayed;
  1784. /** @type {?} */
  1785. var selectedDisplayIndex = selectedIndex - firstDisplayedIndex;
  1786. // The first item is partially out of the viewport. Therefore we need to calculate what
  1787. // portion of it is shown in the viewport and account for it in our offset.
  1788. /** @type {?} */
  1789. var partialItemHeight = itemHeight - (this._getItemCount() * itemHeight - SELECT_PANEL_MAX_HEIGHT) % itemHeight;
  1790. // Because the panel height is longer than the height of the options alone,
  1791. // there is always extra padding at the top or bottom of the panel. When
  1792. // scrolled to the very bottom, this padding is at the top of the panel and
  1793. // must be added to the offset.
  1794. optionOffsetFromPanelTop = selectedDisplayIndex * itemHeight + partialItemHeight;
  1795. }
  1796. else {
  1797. // If the option was scrolled to the middle of the panel using a scroll buffer,
  1798. // its offset will be the scroll buffer minus the half height that was added to
  1799. // center it.
  1800. optionOffsetFromPanelTop = scrollBuffer - itemHeight / 2;
  1801. }
  1802. // The final offset is the option's offset from the top, adjusted for the height difference,
  1803. // multiplied by -1 to ensure that the overlay moves in the correct direction up the page.
  1804. // The value is rounded to prevent some browsers from blurring the content.
  1805. return Math.round(optionOffsetFromPanelTop * -1 - optionHeightAdjustment);
  1806. };
  1807. /**
  1808. * Checks that the attempted overlay position will fit within the viewport.
  1809. * If it will not fit, tries to adjust the scroll position and the associated
  1810. * y-offset so the panel can open fully on-screen. If it still won't fit,
  1811. * sets the offset back to 0 to allow the fallback position to take over.
  1812. */
  1813. /**
  1814. * Checks that the attempted overlay position will fit within the viewport.
  1815. * If it will not fit, tries to adjust the scroll position and the associated
  1816. * y-offset so the panel can open fully on-screen. If it still won't fit,
  1817. * sets the offset back to 0 to allow the fallback position to take over.
  1818. * @private
  1819. * @param {?} maxScroll
  1820. * @return {?}
  1821. */
  1822. MatSelect.prototype._checkOverlayWithinViewport = /**
  1823. * Checks that the attempted overlay position will fit within the viewport.
  1824. * If it will not fit, tries to adjust the scroll position and the associated
  1825. * y-offset so the panel can open fully on-screen. If it still won't fit,
  1826. * sets the offset back to 0 to allow the fallback position to take over.
  1827. * @private
  1828. * @param {?} maxScroll
  1829. * @return {?}
  1830. */
  1831. function (maxScroll) {
  1832. /** @type {?} */
  1833. var itemHeight = this._getItemHeight();
  1834. /** @type {?} */
  1835. var viewportSize = this._viewportRuler.getViewportSize();
  1836. /** @type {?} */
  1837. var topSpaceAvailable = this._triggerRect.top - SELECT_PANEL_VIEWPORT_PADDING;
  1838. /** @type {?} */
  1839. var bottomSpaceAvailable = viewportSize.height - this._triggerRect.bottom - SELECT_PANEL_VIEWPORT_PADDING;
  1840. /** @type {?} */
  1841. var panelHeightTop = Math.abs(this._offsetY);
  1842. /** @type {?} */
  1843. var totalPanelHeight = Math.min(this._getItemCount() * itemHeight, SELECT_PANEL_MAX_HEIGHT);
  1844. /** @type {?} */
  1845. var panelHeightBottom = totalPanelHeight - panelHeightTop - this._triggerRect.height;
  1846. if (panelHeightBottom > bottomSpaceAvailable) {
  1847. this._adjustPanelUp(panelHeightBottom, bottomSpaceAvailable);
  1848. }
  1849. else if (panelHeightTop > topSpaceAvailable) {
  1850. this._adjustPanelDown(panelHeightTop, topSpaceAvailable, maxScroll);
  1851. }
  1852. else {
  1853. this._transformOrigin = this._getOriginBasedOnOption();
  1854. }
  1855. };
  1856. /** Adjusts the overlay panel up to fit in the viewport. */
  1857. /**
  1858. * Adjusts the overlay panel up to fit in the viewport.
  1859. * @private
  1860. * @param {?} panelHeightBottom
  1861. * @param {?} bottomSpaceAvailable
  1862. * @return {?}
  1863. */
  1864. MatSelect.prototype._adjustPanelUp = /**
  1865. * Adjusts the overlay panel up to fit in the viewport.
  1866. * @private
  1867. * @param {?} panelHeightBottom
  1868. * @param {?} bottomSpaceAvailable
  1869. * @return {?}
  1870. */
  1871. function (panelHeightBottom, bottomSpaceAvailable) {
  1872. // Browsers ignore fractional scroll offsets, so we need to round.
  1873. /** @type {?} */
  1874. var distanceBelowViewport = Math.round(panelHeightBottom - bottomSpaceAvailable);
  1875. // Scrolls the panel up by the distance it was extending past the boundary, then
  1876. // adjusts the offset by that amount to move the panel up into the viewport.
  1877. this._scrollTop -= distanceBelowViewport;
  1878. this._offsetY -= distanceBelowViewport;
  1879. this._transformOrigin = this._getOriginBasedOnOption();
  1880. // If the panel is scrolled to the very top, it won't be able to fit the panel
  1881. // by scrolling, so set the offset to 0 to allow the fallback position to take
  1882. // effect.
  1883. if (this._scrollTop <= 0) {
  1884. this._scrollTop = 0;
  1885. this._offsetY = 0;
  1886. this._transformOrigin = "50% bottom 0px";
  1887. }
  1888. };
  1889. /** Adjusts the overlay panel down to fit in the viewport. */
  1890. /**
  1891. * Adjusts the overlay panel down to fit in the viewport.
  1892. * @private
  1893. * @param {?} panelHeightTop
  1894. * @param {?} topSpaceAvailable
  1895. * @param {?} maxScroll
  1896. * @return {?}
  1897. */
  1898. MatSelect.prototype._adjustPanelDown = /**
  1899. * Adjusts the overlay panel down to fit in the viewport.
  1900. * @private
  1901. * @param {?} panelHeightTop
  1902. * @param {?} topSpaceAvailable
  1903. * @param {?} maxScroll
  1904. * @return {?}
  1905. */
  1906. function (panelHeightTop, topSpaceAvailable, maxScroll) {
  1907. // Browsers ignore fractional scroll offsets, so we need to round.
  1908. /** @type {?} */
  1909. var distanceAboveViewport = Math.round(panelHeightTop - topSpaceAvailable);
  1910. // Scrolls the panel down by the distance it was extending past the boundary, then
  1911. // adjusts the offset by that amount to move the panel down into the viewport.
  1912. this._scrollTop += distanceAboveViewport;
  1913. this._offsetY += distanceAboveViewport;
  1914. this._transformOrigin = this._getOriginBasedOnOption();
  1915. // If the panel is scrolled to the very bottom, it won't be able to fit the
  1916. // panel by scrolling, so set the offset to 0 to allow the fallback position
  1917. // to take effect.
  1918. if (this._scrollTop >= maxScroll) {
  1919. this._scrollTop = maxScroll;
  1920. this._offsetY = 0;
  1921. this._transformOrigin = "50% top 0px";
  1922. return;
  1923. }
  1924. };
  1925. /** Sets the transform origin point based on the selected option. */
  1926. /**
  1927. * Sets the transform origin point based on the selected option.
  1928. * @private
  1929. * @return {?}
  1930. */
  1931. MatSelect.prototype._getOriginBasedOnOption = /**
  1932. * Sets the transform origin point based on the selected option.
  1933. * @private
  1934. * @return {?}
  1935. */
  1936. function () {
  1937. /** @type {?} */
  1938. var itemHeight = this._getItemHeight();
  1939. /** @type {?} */
  1940. var optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2;
  1941. /** @type {?} */
  1942. var originY = Math.abs(this._offsetY) - optionHeightAdjustment + itemHeight / 2;
  1943. return "50% " + originY + "px 0px";
  1944. };
  1945. /** Calculates the amount of items in the select. This includes options and group labels. */
  1946. /**
  1947. * Calculates the amount of items in the select. This includes options and group labels.
  1948. * @private
  1949. * @return {?}
  1950. */
  1951. MatSelect.prototype._getItemCount = /**
  1952. * Calculates the amount of items in the select. This includes options and group labels.
  1953. * @private
  1954. * @return {?}
  1955. */
  1956. function () {
  1957. return this.options.length + this.optionGroups.length;
  1958. };
  1959. /** Calculates the height of the select's options. */
  1960. /**
  1961. * Calculates the height of the select's options.
  1962. * @private
  1963. * @return {?}
  1964. */
  1965. MatSelect.prototype._getItemHeight = /**
  1966. * Calculates the height of the select's options.
  1967. * @private
  1968. * @return {?}
  1969. */
  1970. function () {
  1971. return this._triggerFontSize * SELECT_ITEM_HEIGHT_EM;
  1972. };
  1973. /**
  1974. * Implemented as part of MatFormFieldControl.
  1975. * @docs-private
  1976. */
  1977. /**
  1978. * Implemented as part of MatFormFieldControl.
  1979. * \@docs-private
  1980. * @param {?} ids
  1981. * @return {?}
  1982. */
  1983. MatSelect.prototype.setDescribedByIds = /**
  1984. * Implemented as part of MatFormFieldControl.
  1985. * \@docs-private
  1986. * @param {?} ids
  1987. * @return {?}
  1988. */
  1989. function (ids) {
  1990. this._ariaDescribedby = ids.join(' ');
  1991. };
  1992. /**
  1993. * Implemented as part of MatFormFieldControl.
  1994. * @docs-private
  1995. */
  1996. /**
  1997. * Implemented as part of MatFormFieldControl.
  1998. * \@docs-private
  1999. * @return {?}
  2000. */
  2001. MatSelect.prototype.onContainerClick = /**
  2002. * Implemented as part of MatFormFieldControl.
  2003. * \@docs-private
  2004. * @return {?}
  2005. */
  2006. function () {
  2007. this.focus();
  2008. this.open();
  2009. };
  2010. Object.defineProperty(MatSelect.prototype, "shouldLabelFloat", {
  2011. /**
  2012. * Implemented as part of MatFormFieldControl.
  2013. * @docs-private
  2014. */
  2015. get: /**
  2016. * Implemented as part of MatFormFieldControl.
  2017. * \@docs-private
  2018. * @return {?}
  2019. */
  2020. function () {
  2021. return this._panelOpen || !this.empty;
  2022. },
  2023. enumerable: true,
  2024. configurable: true
  2025. });
  2026. MatSelect.decorators = [
  2027. { type: Component, args: [{selector: 'mat-select',
  2028. exportAs: 'matSelect',
  2029. template: "<div cdk-overlay-origin class=\"mat-select-trigger\" aria-hidden=\"true\" (click)=\"toggle()\" #origin=\"cdkOverlayOrigin\" #trigger><div class=\"mat-select-value\" [ngSwitch]=\"empty\"><span class=\"mat-select-placeholder\" *ngSwitchCase=\"true\">{{placeholder || '\u00A0'}}</span> <span class=\"mat-select-value-text\" *ngSwitchCase=\"false\" [ngSwitch]=\"!!customTrigger\"><span *ngSwitchDefault>{{triggerValue || '\u00A0'}}</span><ng-content select=\"mat-select-trigger\" *ngSwitchCase=\"true\"></ng-content></span></div><div class=\"mat-select-arrow-wrapper\"><div class=\"mat-select-arrow\"></div></div></div><ng-template cdk-connected-overlay cdkConnectedOverlayLockPosition cdkConnectedOverlayHasBackdrop cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\" [cdkConnectedOverlayScrollStrategy]=\"_scrollStrategy\" [cdkConnectedOverlayOrigin]=\"origin\" [cdkConnectedOverlayOpen]=\"panelOpen\" [cdkConnectedOverlayPositions]=\"_positions\" [cdkConnectedOverlayMinWidth]=\"_triggerRect?.width\" [cdkConnectedOverlayOffsetY]=\"_offsetY\" (backdropClick)=\"close()\" (attach)=\"_onAttached()\" (detach)=\"close()\"><div class=\"mat-select-panel-wrap\" [@transformPanelWrap]><div #panel class=\"mat-select-panel {{ _getPanelTheme() }}\" [ngClass]=\"panelClass\" [@transformPanel]=\"multiple ? 'showing-multiple' : 'showing'\" (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\" [style.transformOrigin]=\"_transformOrigin\" [style.font-size.px]=\"_triggerFontSize\" (keydown)=\"_handleKeydown($event)\"><ng-content></ng-content></div></div></ng-template>",
  2030. styles: [".mat-select{display:inline-block;width:100%;outline:0}.mat-select-trigger{display:inline-table;cursor:pointer;position:relative;box-sizing:border-box}.mat-select-disabled .mat-select-trigger{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.mat-select-value{display:table-cell;max-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mat-select-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mat-select-arrow-wrapper{display:table-cell;vertical-align:middle}.mat-form-field-appearance-fill .mat-select-arrow-wrapper{transform:translateY(-50%)}.mat-form-field-appearance-outline .mat-select-arrow-wrapper{transform:translateY(-25%)}.mat-form-field-appearance-standard.mat-form-field-has-label .mat-select:not(.mat-select-empty) .mat-select-arrow-wrapper{transform:translateY(-50%)}.mat-form-field-appearance-standard .mat-select.mat-select-empty .mat-select-arrow-wrapper{transition:transform .4s cubic-bezier(.25,.8,.25,1)}._mat-animation-noopable.mat-form-field-appearance-standard .mat-select.mat-select-empty .mat-select-arrow-wrapper{transition:none}.mat-select-arrow{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid;margin:0 4px}.mat-select-panel-wrap{flex-basis:100%}.mat-select-panel{min-width:112px;max-width:280px;overflow:auto;-webkit-overflow-scrolling:touch;padding-top:0;padding-bottom:0;max-height:256px;min-width:100%;border-radius:4px}@media (-ms-high-contrast:active){.mat-select-panel{outline:solid 1px}}.mat-select-panel .mat-optgroup-label,.mat-select-panel .mat-option{font-size:inherit;line-height:3em;height:3em}.mat-form-field-type-mat-select:not(.mat-form-field-disabled) .mat-form-field-flex{cursor:pointer}.mat-form-field-type-mat-select .mat-form-field-label{width:calc(100% - 18px)}.mat-select-placeholder{transition:color .4s .133s cubic-bezier(.25,.8,.25,1)}._mat-animation-noopable .mat-select-placeholder{transition:none}.mat-form-field-hide-placeholder .mat-select-placeholder{color:transparent;-webkit-text-fill-color:transparent;transition:none;display:block}"],
  2031. inputs: ['disabled', 'disableRipple', 'tabIndex'],
  2032. encapsulation: ViewEncapsulation.None,
  2033. changeDetection: ChangeDetectionStrategy.OnPush,
  2034. host: {
  2035. 'role': 'listbox',
  2036. '[attr.id]': 'id',
  2037. '[attr.tabindex]': 'tabIndex',
  2038. '[attr.aria-label]': '_getAriaLabel()',
  2039. '[attr.aria-labelledby]': '_getAriaLabelledby()',
  2040. '[attr.aria-required]': 'required.toString()',
  2041. '[attr.aria-disabled]': 'disabled.toString()',
  2042. '[attr.aria-invalid]': 'errorState',
  2043. '[attr.aria-owns]': 'panelOpen ? _optionIds : null',
  2044. '[attr.aria-multiselectable]': 'multiple',
  2045. '[attr.aria-describedby]': '_ariaDescribedby || null',
  2046. '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',
  2047. '[class.mat-select-disabled]': 'disabled',
  2048. '[class.mat-select-invalid]': 'errorState',
  2049. '[class.mat-select-required]': 'required',
  2050. '[class.mat-select-empty]': 'empty',
  2051. 'class': 'mat-select',
  2052. '(keydown)': '_handleKeydown($event)',
  2053. '(focus)': '_onFocus()',
  2054. '(blur)': '_onBlur()',
  2055. },
  2056. animations: [
  2057. matSelectAnimations.transformPanelWrap,
  2058. matSelectAnimations.transformPanel
  2059. ],
  2060. providers: [
  2061. { provide: MatFormFieldControl, useExisting: MatSelect },
  2062. { provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatSelect }
  2063. ],
  2064. },] },
  2065. ];
  2066. /** @nocollapse */
  2067. MatSelect.ctorParameters = function () { return [
  2068. { type: ViewportRuler },
  2069. { type: ChangeDetectorRef },
  2070. { type: NgZone },
  2071. { type: ErrorStateMatcher },
  2072. { type: ElementRef },
  2073. { type: Directionality, decorators: [{ type: Optional }] },
  2074. { type: NgForm, decorators: [{ type: Optional }] },
  2075. { type: FormGroupDirective, decorators: [{ type: Optional }] },
  2076. { type: MatFormField, decorators: [{ type: Optional }] },
  2077. { type: NgControl, decorators: [{ type: Self }, { type: Optional }] },
  2078. { type: String, decorators: [{ type: Attribute, args: ['tabindex',] }] },
  2079. { type: undefined, decorators: [{ type: Inject, args: [MAT_SELECT_SCROLL_STRATEGY,] }] },
  2080. { type: LiveAnnouncer }
  2081. ]; };
  2082. MatSelect.propDecorators = {
  2083. trigger: [{ type: ViewChild, args: ['trigger', { static: false },] }],
  2084. panel: [{ type: ViewChild, args: ['panel', { static: false },] }],
  2085. overlayDir: [{ type: ViewChild, args: [CdkConnectedOverlay, { static: false },] }],
  2086. options: [{ type: ContentChildren, args: [MatOption, { descendants: true },] }],
  2087. optionGroups: [{ type: ContentChildren, args: [MatOptgroup,] }],
  2088. panelClass: [{ type: Input }],
  2089. customTrigger: [{ type: ContentChild, args: [MatSelectTrigger, { static: false },] }],
  2090. placeholder: [{ type: Input }],
  2091. required: [{ type: Input }],
  2092. multiple: [{ type: Input }],
  2093. disableOptionCentering: [{ type: Input }],
  2094. compareWith: [{ type: Input }],
  2095. value: [{ type: Input }],
  2096. ariaLabel: [{ type: Input, args: ['aria-label',] }],
  2097. ariaLabelledby: [{ type: Input, args: ['aria-labelledby',] }],
  2098. errorStateMatcher: [{ type: Input }],
  2099. sortComparator: [{ type: Input }],
  2100. id: [{ type: Input }],
  2101. openedChange: [{ type: Output }],
  2102. _openedStream: [{ type: Output, args: ['opened',] }],
  2103. _closedStream: [{ type: Output, args: ['closed',] }],
  2104. selectionChange: [{ type: Output }],
  2105. valueChange: [{ type: Output }]
  2106. };
  2107. return MatSelect;
  2108. }(_MatSelectMixinBase));
  2109. /**
  2110. * @fileoverview added by tsickle
  2111. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2112. */
  2113. var MatSelectModule = /** @class */ (function () {
  2114. function MatSelectModule() {
  2115. }
  2116. MatSelectModule.decorators = [
  2117. { type: NgModule, args: [{
  2118. imports: [
  2119. CommonModule,
  2120. OverlayModule,
  2121. MatOptionModule,
  2122. MatCommonModule,
  2123. ],
  2124. exports: [MatFormFieldModule, MatSelect, MatSelectTrigger, MatOptionModule, MatCommonModule],
  2125. declarations: [MatSelect, MatSelectTrigger],
  2126. providers: [MAT_SELECT_SCROLL_STRATEGY_PROVIDER]
  2127. },] },
  2128. ];
  2129. return MatSelectModule;
  2130. }());
  2131. /**
  2132. * @fileoverview added by tsickle
  2133. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2134. */
  2135. /**
  2136. * @fileoverview added by tsickle
  2137. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2138. */
  2139. export { MatSelectModule, MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY, SELECT_PANEL_MAX_HEIGHT, SELECT_PANEL_PADDING_X, SELECT_PANEL_INDENT_PADDING_X, SELECT_ITEM_HEIGHT_EM, SELECT_MULTIPLE_PANEL_PADDING_X, SELECT_PANEL_VIEWPORT_PADDING, MAT_SELECT_SCROLL_STRATEGY, MAT_SELECT_SCROLL_STRATEGY_PROVIDER, MatSelectChange, MatSelectTrigger, MatSelect, matSelectAnimations, transformPanel, fadeInContent };
  2140. //# sourceMappingURL=select.es5.js.map