| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156 |
- import * as i8 from '@angular/cdk/overlay';
- import { Overlay, CdkConnectedOverlay, CdkOverlayOrigin, OverlayModule } from '@angular/cdk/overlay';
- import * as i7 from '@angular/common';
- import { CommonModule } from '@angular/common';
- import * as i0 from '@angular/core';
- import { InjectionToken, EventEmitter, Directive, Optional, Inject, Self, Attribute, Input, ViewChild, Output, Component, ViewEncapsulation, ChangeDetectionStrategy, ContentChildren, ContentChild, NgModule } from '@angular/core';
- import * as i2 from '@angular/material/core';
- import { mixinDisableRipple, mixinTabIndex, mixinDisabled, mixinErrorState, _countGroupLabelsBeforeOption, _getOptionScrollPosition, MAT_OPTION_PARENT_COMPONENT, MatOption, MAT_OPTGROUP, MatOptionModule, MatCommonModule } from '@angular/material/core';
- import * as i6 from '@angular/material/form-field';
- import { MAT_FORM_FIELD, MatFormFieldControl, MatFormFieldModule } from '@angular/material/form-field';
- import * as i1 from '@angular/cdk/scrolling';
- import { CdkScrollableModule } from '@angular/cdk/scrolling';
- import * as i5 from '@angular/cdk/a11y';
- import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
- import * as i3 from '@angular/cdk/bidi';
- import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
- import { SelectionModel } from '@angular/cdk/collections';
- import { DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE, hasModifierKey, A } from '@angular/cdk/keycodes';
- import * as i4 from '@angular/forms';
- import { Validators } from '@angular/forms';
- import { Subject, defer, merge } from 'rxjs';
- import { startWith, switchMap, take, filter, map, distinctUntilChanged, takeUntil } from 'rxjs/operators';
- import { trigger, transition, query, animateChild, state, style, animate } from '@angular/animations';
- /**
- * The following are all the animations for the mat-select component, with each
- * const containing the metadata for one animation.
- *
- * The values below match the implementation of the AngularJS Material mat-select animation.
- * @docs-private
- */
- const matSelectAnimations = {
- /**
- * This animation ensures the select's overlay panel animation (transformPanel) is called when
- * closing the select.
- * This is needed due to https://github.com/angular/angular/issues/23302
- */
- transformPanelWrap: trigger('transformPanelWrap', [
- transition('* => void', query('@transformPanel', [animateChild()], { optional: true })),
- ]),
- /** This animation transforms the select's overlay panel on and off the page. */
- transformPanel: trigger('transformPanel', [
- state('void', style({
- opacity: 0,
- transform: 'scale(1, 0.8)',
- })),
- transition('void => showing', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({
- opacity: 1,
- transform: 'scale(1, 1)',
- }))),
- transition('* => void', animate('100ms linear', style({ opacity: 0 }))),
- ]),
- };
- // Note that these have been copied over verbatim from
- // `material/select` so that we don't have to expose them publicly.
- /**
- * Returns an exception to be thrown when attempting to change a select's `multiple` option
- * after initialization.
- * @docs-private
- */
- function getMatSelectDynamicMultipleError() {
- return Error('Cannot change `multiple` mode of select after initialization.');
- }
- /**
- * Returns an exception to be thrown when attempting to assign a non-array value to a select
- * in `multiple` mode. Note that `undefined` and `null` are still valid values to allow for
- * resetting the value.
- * @docs-private
- */
- function getMatSelectNonArrayValueError() {
- return Error('Value must be an array in multiple-selection mode.');
- }
- /**
- * Returns an exception to be thrown when assigning a non-function value to the comparator
- * used to determine if a value corresponds to an option. Note that whether the function
- * actually takes two values and returns a boolean is not checked.
- */
- function getMatSelectNonFunctionValueError() {
- return Error('`compareWith` must be a function.');
- }
- let nextUniqueId = 0;
- /** Injection token that determines the scroll handling while a select is open. */
- const MAT_SELECT_SCROLL_STRATEGY = new InjectionToken('mat-select-scroll-strategy');
- /** @docs-private */
- function MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
- return () => overlay.scrollStrategies.reposition();
- }
- /** Injection token that can be used to provide the default options the select module. */
- const MAT_SELECT_CONFIG = new InjectionToken('MAT_SELECT_CONFIG');
- /** @docs-private */
- const MAT_SELECT_SCROLL_STRATEGY_PROVIDER = {
- provide: MAT_SELECT_SCROLL_STRATEGY,
- deps: [Overlay],
- useFactory: MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY,
- };
- /**
- * Injection token that can be used to reference instances of `MatSelectTrigger`. It serves as
- * alternative token to the actual `MatSelectTrigger` class which could cause unnecessary
- * retention of the class and its directive metadata.
- */
- const MAT_SELECT_TRIGGER = new InjectionToken('MatSelectTrigger');
- /** Change event object that is emitted when the select value has changed. */
- class MatSelectChange {
- constructor(
- /** Reference to the select that emitted the change event. */
- source,
- /** Current value of the select that emitted the event. */
- value) {
- this.source = source;
- this.value = value;
- }
- }
- // Boilerplate for applying mixins to MatSelect.
- /** @docs-private */
- const _MatSelectMixinBase = mixinDisableRipple(mixinTabIndex(mixinDisabled(mixinErrorState(class {
- constructor(_elementRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup,
- /**
- * Form control bound to the component.
- * Implemented as part of `MatFormFieldControl`.
- * @docs-private
- */
- ngControl) {
- this._elementRef = _elementRef;
- this._defaultErrorStateMatcher = _defaultErrorStateMatcher;
- this._parentForm = _parentForm;
- this._parentFormGroup = _parentFormGroup;
- this.ngControl = ngControl;
- /**
- * Emits whenever the component state changes and should cause the parent
- * form-field to update. Implemented as part of `MatFormFieldControl`.
- * @docs-private
- */
- this.stateChanges = new Subject();
- }
- }))));
- /** Base class with all of the `MatSelect` functionality. */
- class _MatSelectBase extends _MatSelectMixinBase {
- /** Whether the select is focused. */
- get focused() {
- return this._focused || this._panelOpen;
- }
- /** Placeholder to be shown if no value has been selected. */
- get placeholder() {
- return this._placeholder;
- }
- set placeholder(value) {
- this._placeholder = value;
- this.stateChanges.next();
- }
- /** Whether the component is required. */
- get required() {
- return this._required ?? this.ngControl?.control?.hasValidator(Validators.required) ?? false;
- }
- set required(value) {
- this._required = coerceBooleanProperty(value);
- this.stateChanges.next();
- }
- /** Whether the user should be allowed to select multiple options. */
- get multiple() {
- return this._multiple;
- }
- set multiple(value) {
- if (this._selectionModel && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getMatSelectDynamicMultipleError();
- }
- this._multiple = coerceBooleanProperty(value);
- }
- /** Whether to center the active option over the trigger. */
- get disableOptionCentering() {
- return this._disableOptionCentering;
- }
- set disableOptionCentering(value) {
- this._disableOptionCentering = coerceBooleanProperty(value);
- }
- /**
- * Function to compare the option values with the selected values. The first argument
- * is a value from an option. The second is a value from the selection. A boolean
- * should be returned.
- */
- get compareWith() {
- return this._compareWith;
- }
- set compareWith(fn) {
- if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getMatSelectNonFunctionValueError();
- }
- this._compareWith = fn;
- if (this._selectionModel) {
- // A different comparator means the selection could change.
- this._initializeSelection();
- }
- }
- /** Value of the select control. */
- get value() {
- return this._value;
- }
- set value(newValue) {
- const hasAssigned = this._assignValue(newValue);
- if (hasAssigned) {
- this._onChange(newValue);
- }
- }
- /** Time to wait in milliseconds after the last keystroke before moving focus to an item. */
- get typeaheadDebounceInterval() {
- return this._typeaheadDebounceInterval;
- }
- set typeaheadDebounceInterval(value) {
- this._typeaheadDebounceInterval = coerceNumberProperty(value);
- }
- /** Unique id of the element. */
- get id() {
- return this._id;
- }
- set id(value) {
- this._id = value || this._uid;
- this.stateChanges.next();
- }
- constructor(_viewportRuler, _changeDetectorRef, _ngZone, _defaultErrorStateMatcher, elementRef, _dir, _parentForm, _parentFormGroup, _parentFormField, ngControl, tabIndex, scrollStrategyFactory, _liveAnnouncer, _defaultOptions) {
- super(elementRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
- this._viewportRuler = _viewportRuler;
- this._changeDetectorRef = _changeDetectorRef;
- this._ngZone = _ngZone;
- this._dir = _dir;
- this._parentFormField = _parentFormField;
- this._liveAnnouncer = _liveAnnouncer;
- this._defaultOptions = _defaultOptions;
- /** Whether or not the overlay panel is open. */
- this._panelOpen = false;
- /** Comparison function to specify which option is displayed. Defaults to object equality. */
- this._compareWith = (o1, o2) => o1 === o2;
- /** Unique id for this input. */
- this._uid = `mat-select-${nextUniqueId++}`;
- /** Current `aria-labelledby` value for the select trigger. */
- this._triggerAriaLabelledBy = null;
- /** Emits whenever the component is destroyed. */
- this._destroy = new Subject();
- /** `View -> model callback called when value changes` */
- this._onChange = () => { };
- /** `View -> model callback called when select has been touched` */
- this._onTouched = () => { };
- /** ID for the DOM node containing the select's value. */
- this._valueId = `mat-select-value-${nextUniqueId++}`;
- /** Emits when the panel element is finished transforming in. */
- this._panelDoneAnimatingStream = new Subject();
- this._overlayPanelClass = this._defaultOptions?.overlayPanelClass || '';
- this._focused = false;
- /** A name for this control that can be used by `mat-form-field`. */
- this.controlType = 'mat-select';
- this._multiple = false;
- this._disableOptionCentering = this._defaultOptions?.disableOptionCentering ?? false;
- /** Aria label of the select. */
- this.ariaLabel = '';
- /** Combined stream of all of the child options' change events. */
- this.optionSelectionChanges = defer(() => {
- const options = this.options;
- if (options) {
- return options.changes.pipe(startWith(options), switchMap(() => merge(...options.map(option => option.onSelectionChange))));
- }
- return this._ngZone.onStable.pipe(take(1), switchMap(() => this.optionSelectionChanges));
- });
- /** Event emitted when the select panel has been toggled. */
- this.openedChange = new EventEmitter();
- /** Event emitted when the select has been opened. */
- this._openedStream = this.openedChange.pipe(filter(o => o), map(() => { }));
- /** Event emitted when the select has been closed. */
- this._closedStream = this.openedChange.pipe(filter(o => !o), map(() => { }));
- /** Event emitted when the selected value has been changed by the user. */
- this.selectionChange = new EventEmitter();
- /**
- * Event that emits whenever the raw value of the select changes. This is here primarily
- * to facilitate the two-way binding for the `value` input.
- * @docs-private
- */
- this.valueChange = new EventEmitter();
- if (this.ngControl) {
- // Note: we provide the value accessor through here, instead of
- // the `providers` to avoid running into a circular import.
- this.ngControl.valueAccessor = this;
- }
- // Note that we only want to set this when the defaults pass it in, otherwise it should
- // stay as `undefined` so that it falls back to the default in the key manager.
- if (_defaultOptions?.typeaheadDebounceInterval != null) {
- this._typeaheadDebounceInterval = _defaultOptions.typeaheadDebounceInterval;
- }
- this._scrollStrategyFactory = scrollStrategyFactory;
- this._scrollStrategy = this._scrollStrategyFactory();
- this.tabIndex = parseInt(tabIndex) || 0;
- // Force setter to be called in case id was not specified.
- this.id = this.id;
- }
- ngOnInit() {
- this._selectionModel = new SelectionModel(this.multiple);
- this.stateChanges.next();
- // We need `distinctUntilChanged` here, because some browsers will
- // fire the animation end event twice for the same animation. See:
- // https://github.com/angular/angular/issues/24084
- this._panelDoneAnimatingStream
- .pipe(distinctUntilChanged(), takeUntil(this._destroy))
- .subscribe(() => this._panelDoneAnimating(this.panelOpen));
- }
- ngAfterContentInit() {
- this._initKeyManager();
- this._selectionModel.changed.pipe(takeUntil(this._destroy)).subscribe(event => {
- event.added.forEach(option => option.select());
- event.removed.forEach(option => option.deselect());
- });
- this.options.changes.pipe(startWith(null), takeUntil(this._destroy)).subscribe(() => {
- this._resetOptions();
- this._initializeSelection();
- });
- }
- ngDoCheck() {
- const newAriaLabelledby = this._getTriggerAriaLabelledby();
- const ngControl = this.ngControl;
- // We have to manage setting the `aria-labelledby` ourselves, because part of its value
- // is computed as a result of a content query which can cause this binding to trigger a
- // "changed after checked" error.
- if (newAriaLabelledby !== this._triggerAriaLabelledBy) {
- const element = this._elementRef.nativeElement;
- this._triggerAriaLabelledBy = newAriaLabelledby;
- if (newAriaLabelledby) {
- element.setAttribute('aria-labelledby', newAriaLabelledby);
- }
- else {
- element.removeAttribute('aria-labelledby');
- }
- }
- if (ngControl) {
- // The disabled state might go out of sync if the form group is swapped out. See #17860.
- if (this._previousControl !== ngControl.control) {
- if (this._previousControl !== undefined &&
- ngControl.disabled !== null &&
- ngControl.disabled !== this.disabled) {
- this.disabled = ngControl.disabled;
- }
- this._previousControl = ngControl.control;
- }
- this.updateErrorState();
- }
- }
- ngOnChanges(changes) {
- // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let
- // the parent form field know to run change detection when the disabled state changes.
- if (changes['disabled'] || changes['userAriaDescribedBy']) {
- this.stateChanges.next();
- }
- if (changes['typeaheadDebounceInterval'] && this._keyManager) {
- this._keyManager.withTypeAhead(this._typeaheadDebounceInterval);
- }
- }
- ngOnDestroy() {
- this._keyManager?.destroy();
- this._destroy.next();
- this._destroy.complete();
- this.stateChanges.complete();
- }
- /** Toggles the overlay panel open or closed. */
- toggle() {
- this.panelOpen ? this.close() : this.open();
- }
- /** Opens the overlay panel. */
- open() {
- if (this._canOpen()) {
- this._panelOpen = true;
- this._keyManager.withHorizontalOrientation(null);
- this._highlightCorrectOption();
- this._changeDetectorRef.markForCheck();
- }
- }
- /** Closes the overlay panel and focuses the host element. */
- close() {
- if (this._panelOpen) {
- this._panelOpen = false;
- this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');
- this._changeDetectorRef.markForCheck();
- this._onTouched();
- }
- }
- /**
- * Sets the select's value. Part of the ControlValueAccessor interface
- * required to integrate with Angular's core forms API.
- *
- * @param value New value to be written to the model.
- */
- writeValue(value) {
- this._assignValue(value);
- }
- /**
- * Saves a callback function to be invoked when the select's value
- * changes from user input. Part of the ControlValueAccessor interface
- * required to integrate with Angular's core forms API.
- *
- * @param fn Callback to be triggered when the value changes.
- */
- registerOnChange(fn) {
- this._onChange = fn;
- }
- /**
- * Saves a callback function to be invoked when the select is blurred
- * by the user. Part of the ControlValueAccessor interface required
- * to integrate with Angular's core forms API.
- *
- * @param fn Callback to be triggered when the component has been touched.
- */
- registerOnTouched(fn) {
- this._onTouched = fn;
- }
- /**
- * Disables the select. Part of the ControlValueAccessor interface required
- * to integrate with Angular's core forms API.
- *
- * @param isDisabled Sets whether the component is disabled.
- */
- setDisabledState(isDisabled) {
- this.disabled = isDisabled;
- this._changeDetectorRef.markForCheck();
- this.stateChanges.next();
- }
- /** Whether or not the overlay panel is open. */
- get panelOpen() {
- return this._panelOpen;
- }
- /** The currently selected option. */
- get selected() {
- return this.multiple ? this._selectionModel?.selected || [] : this._selectionModel?.selected[0];
- }
- /** The value displayed in the trigger. */
- get triggerValue() {
- if (this.empty) {
- return '';
- }
- if (this._multiple) {
- const selectedOptions = this._selectionModel.selected.map(option => option.viewValue);
- if (this._isRtl()) {
- selectedOptions.reverse();
- }
- // TODO(crisbeto): delimiter should be configurable for proper localization.
- return selectedOptions.join(', ');
- }
- return this._selectionModel.selected[0].viewValue;
- }
- /** Whether the element is in RTL mode. */
- _isRtl() {
- return this._dir ? this._dir.value === 'rtl' : false;
- }
- /** Handles all keydown events on the select. */
- _handleKeydown(event) {
- if (!this.disabled) {
- this.panelOpen ? this._handleOpenKeydown(event) : this._handleClosedKeydown(event);
- }
- }
- /** Handles keyboard events while the select is closed. */
- _handleClosedKeydown(event) {
- const keyCode = event.keyCode;
- const isArrowKey = keyCode === DOWN_ARROW ||
- keyCode === UP_ARROW ||
- keyCode === LEFT_ARROW ||
- keyCode === RIGHT_ARROW;
- const isOpenKey = keyCode === ENTER || keyCode === SPACE;
- const manager = this._keyManager;
- // Open the select on ALT + arrow key to match the native <select>
- if ((!manager.isTyping() && isOpenKey && !hasModifierKey(event)) ||
- ((this.multiple || event.altKey) && isArrowKey)) {
- event.preventDefault(); // prevents the page from scrolling down when pressing space
- this.open();
- }
- else if (!this.multiple) {
- const previouslySelectedOption = this.selected;
- manager.onKeydown(event);
- const selectedOption = this.selected;
- // Since the value has changed, we need to announce it ourselves.
- if (selectedOption && previouslySelectedOption !== selectedOption) {
- // We set a duration on the live announcement, because we want the live element to be
- // cleared after a while so that users can't navigate to it using the arrow keys.
- this._liveAnnouncer.announce(selectedOption.viewValue, 10000);
- }
- }
- }
- /** Handles keyboard events when the selected is open. */
- _handleOpenKeydown(event) {
- const manager = this._keyManager;
- const keyCode = event.keyCode;
- const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
- const isTyping = manager.isTyping();
- if (isArrowKey && event.altKey) {
- // Close the select on ALT + arrow key to match the native <select>
- event.preventDefault();
- this.close();
- // Don't do anything in this case if the user is typing,
- // because the typing sequence can include the space key.
- }
- else if (!isTyping &&
- (keyCode === ENTER || keyCode === SPACE) &&
- manager.activeItem &&
- !hasModifierKey(event)) {
- event.preventDefault();
- manager.activeItem._selectViaInteraction();
- }
- else if (!isTyping && this._multiple && keyCode === A && event.ctrlKey) {
- event.preventDefault();
- const hasDeselectedOptions = this.options.some(opt => !opt.disabled && !opt.selected);
- this.options.forEach(option => {
- if (!option.disabled) {
- hasDeselectedOptions ? option.select() : option.deselect();
- }
- });
- }
- else {
- const previouslyFocusedIndex = manager.activeItemIndex;
- manager.onKeydown(event);
- if (this._multiple &&
- isArrowKey &&
- event.shiftKey &&
- manager.activeItem &&
- manager.activeItemIndex !== previouslyFocusedIndex) {
- manager.activeItem._selectViaInteraction();
- }
- }
- }
- _onFocus() {
- if (!this.disabled) {
- this._focused = true;
- this.stateChanges.next();
- }
- }
- /**
- * Calls the touched callback only if the panel is closed. Otherwise, the trigger will
- * "blur" to the panel when it opens, causing a false positive.
- */
- _onBlur() {
- this._focused = false;
- this._keyManager?.cancelTypeahead();
- if (!this.disabled && !this.panelOpen) {
- this._onTouched();
- this._changeDetectorRef.markForCheck();
- this.stateChanges.next();
- }
- }
- /**
- * Callback that is invoked when the overlay panel has been attached.
- */
- _onAttached() {
- this._overlayDir.positionChange.pipe(take(1)).subscribe(() => {
- this._changeDetectorRef.detectChanges();
- this._positioningSettled();
- });
- }
- /** Returns the theme to be used on the panel. */
- _getPanelTheme() {
- return this._parentFormField ? `mat-${this._parentFormField.color}` : '';
- }
- /** Whether the select has a value. */
- get empty() {
- return !this._selectionModel || this._selectionModel.isEmpty();
- }
- _initializeSelection() {
- // Defer setting the value in order to avoid the "Expression
- // has changed after it was checked" errors from Angular.
- Promise.resolve().then(() => {
- if (this.ngControl) {
- this._value = this.ngControl.value;
- }
- this._setSelectionByValue(this._value);
- this.stateChanges.next();
- });
- }
- /**
- * Sets the selected option based on a value. If no option can be
- * found with the designated value, the select trigger is cleared.
- */
- _setSelectionByValue(value) {
- this.options.forEach(option => option.setInactiveStyles());
- this._selectionModel.clear();
- if (this.multiple && value) {
- if (!Array.isArray(value) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw getMatSelectNonArrayValueError();
- }
- value.forEach((currentValue) => this._selectOptionByValue(currentValue));
- this._sortValues();
- }
- else {
- const correspondingOption = this._selectOptionByValue(value);
- // Shift focus to the active item. Note that we shouldn't do this in multiple
- // mode, because we don't know what option the user interacted with last.
- if (correspondingOption) {
- this._keyManager.updateActiveItem(correspondingOption);
- }
- else if (!this.panelOpen) {
- // Otherwise reset the highlighted option. Note that we only want to do this while
- // closed, because doing it while open can shift the user's focus unnecessarily.
- this._keyManager.updateActiveItem(-1);
- }
- }
- this._changeDetectorRef.markForCheck();
- }
- /**
- * Finds and selects and option based on its value.
- * @returns Option that has the corresponding value.
- */
- _selectOptionByValue(value) {
- const correspondingOption = this.options.find((option) => {
- // Skip options that are already in the model. This allows us to handle cases
- // where the same primitive value is selected multiple times.
- if (this._selectionModel.isSelected(option)) {
- return false;
- }
- try {
- // Treat null as a special reset value.
- return option.value != null && this._compareWith(option.value, value);
- }
- catch (error) {
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
- // Notify developers of errors in their comparator.
- console.warn(error);
- }
- return false;
- }
- });
- if (correspondingOption) {
- this._selectionModel.select(correspondingOption);
- }
- return correspondingOption;
- }
- /** Assigns a specific value to the select. Returns whether the value has changed. */
- _assignValue(newValue) {
- // Always re-assign an array, because it might have been mutated.
- if (newValue !== this._value || (this._multiple && Array.isArray(newValue))) {
- if (this.options) {
- this._setSelectionByValue(newValue);
- }
- this._value = newValue;
- return true;
- }
- return false;
- }
- _skipPredicate(item) {
- return item.disabled;
- }
- /** Sets up a key manager to listen to keyboard events on the overlay panel. */
- _initKeyManager() {
- this._keyManager = new ActiveDescendantKeyManager(this.options)
- .withTypeAhead(this._typeaheadDebounceInterval)
- .withVerticalOrientation()
- .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')
- .withHomeAndEnd()
- .withPageUpDown()
- .withAllowedModifierKeys(['shiftKey'])
- .skipPredicate(this._skipPredicate);
- this._keyManager.tabOut.subscribe(() => {
- if (this.panelOpen) {
- // Select the active item when tabbing away. This is consistent with how the native
- // select behaves. Note that we only want to do this in single selection mode.
- if (!this.multiple && this._keyManager.activeItem) {
- this._keyManager.activeItem._selectViaInteraction();
- }
- // Restore focus to the trigger before closing. Ensures that the focus
- // position won't be lost if the user got focus into the overlay.
- this.focus();
- this.close();
- }
- });
- this._keyManager.change.subscribe(() => {
- if (this._panelOpen && this.panel) {
- this._scrollOptionIntoView(this._keyManager.activeItemIndex || 0);
- }
- else if (!this._panelOpen && !this.multiple && this._keyManager.activeItem) {
- this._keyManager.activeItem._selectViaInteraction();
- }
- });
- }
- /** Drops current option subscriptions and IDs and resets from scratch. */
- _resetOptions() {
- const changedOrDestroyed = merge(this.options.changes, this._destroy);
- this.optionSelectionChanges.pipe(takeUntil(changedOrDestroyed)).subscribe(event => {
- this._onSelect(event.source, event.isUserInput);
- if (event.isUserInput && !this.multiple && this._panelOpen) {
- this.close();
- this.focus();
- }
- });
- // Listen to changes in the internal state of the options and react accordingly.
- // Handles cases like the labels of the selected options changing.
- merge(...this.options.map(option => option._stateChanges))
- .pipe(takeUntil(changedOrDestroyed))
- .subscribe(() => {
- // `_stateChanges` can fire as a result of a change in the label's DOM value which may
- // be the result of an expression changing. We have to use `detectChanges` in order
- // to avoid "changed after checked" errors (see #14793).
- this._changeDetectorRef.detectChanges();
- this.stateChanges.next();
- });
- }
- /** Invoked when an option is clicked. */
- _onSelect(option, isUserInput) {
- const wasSelected = this._selectionModel.isSelected(option);
- if (option.value == null && !this._multiple) {
- option.deselect();
- this._selectionModel.clear();
- if (this.value != null) {
- this._propagateChanges(option.value);
- }
- }
- else {
- if (wasSelected !== option.selected) {
- option.selected
- ? this._selectionModel.select(option)
- : this._selectionModel.deselect(option);
- }
- if (isUserInput) {
- this._keyManager.setActiveItem(option);
- }
- if (this.multiple) {
- this._sortValues();
- if (isUserInput) {
- // In case the user selected the option with their mouse, we
- // want to restore focus back to the trigger, in order to
- // prevent the select keyboard controls from clashing with
- // the ones from `mat-option`.
- this.focus();
- }
- }
- }
- if (wasSelected !== this._selectionModel.isSelected(option)) {
- this._propagateChanges();
- }
- this.stateChanges.next();
- }
- /** Sorts the selected values in the selected based on their order in the panel. */
- _sortValues() {
- if (this.multiple) {
- const options = this.options.toArray();
- this._selectionModel.sort((a, b) => {
- return this.sortComparator
- ? this.sortComparator(a, b, options)
- : options.indexOf(a) - options.indexOf(b);
- });
- this.stateChanges.next();
- }
- }
- /** Emits change event to set the model value. */
- _propagateChanges(fallbackValue) {
- let valueToEmit = null;
- if (this.multiple) {
- valueToEmit = this.selected.map(option => option.value);
- }
- else {
- valueToEmit = this.selected ? this.selected.value : fallbackValue;
- }
- this._value = valueToEmit;
- this.valueChange.emit(valueToEmit);
- this._onChange(valueToEmit);
- this.selectionChange.emit(this._getChangeEvent(valueToEmit));
- this._changeDetectorRef.markForCheck();
- }
- /**
- * Highlights the selected item. If no option is selected, it will highlight
- * the first *enabled* option.
- */
- _highlightCorrectOption() {
- if (this._keyManager) {
- if (this.empty) {
- // Find the index of the first *enabled* option. Avoid calling `_keyManager.setActiveItem`
- // because it activates the first option that passes the skip predicate, rather than the
- // first *enabled* option.
- let firstEnabledOptionIndex = -1;
- for (let index = 0; index < this.options.length; index++) {
- const option = this.options.get(index);
- if (!option.disabled) {
- firstEnabledOptionIndex = index;
- break;
- }
- }
- this._keyManager.setActiveItem(firstEnabledOptionIndex);
- }
- else {
- this._keyManager.setActiveItem(this._selectionModel.selected[0]);
- }
- }
- }
- /** Whether the panel is allowed to open. */
- _canOpen() {
- return !this._panelOpen && !this.disabled && this.options?.length > 0;
- }
- /** Focuses the select element. */
- focus(options) {
- this._elementRef.nativeElement.focus(options);
- }
- /** Gets the aria-labelledby for the select panel. */
- _getPanelAriaLabelledby() {
- if (this.ariaLabel) {
- return null;
- }
- const labelId = this._parentFormField?.getLabelId();
- const labelExpression = labelId ? labelId + ' ' : '';
- return this.ariaLabelledby ? labelExpression + this.ariaLabelledby : labelId;
- }
- /** Determines the `aria-activedescendant` to be set on the host. */
- _getAriaActiveDescendant() {
- if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {
- return this._keyManager.activeItem.id;
- }
- return null;
- }
- /** Gets the aria-labelledby of the select component trigger. */
- _getTriggerAriaLabelledby() {
- if (this.ariaLabel) {
- return null;
- }
- const labelId = this._parentFormField?.getLabelId();
- let value = (labelId ? labelId + ' ' : '') + this._valueId;
- if (this.ariaLabelledby) {
- value += ' ' + this.ariaLabelledby;
- }
- return value;
- }
- /** Called when the overlay panel is done animating. */
- _panelDoneAnimating(isOpen) {
- this.openedChange.emit(isOpen);
- }
- /**
- * Implemented as part of MatFormFieldControl.
- * @docs-private
- */
- setDescribedByIds(ids) {
- if (ids.length) {
- this._elementRef.nativeElement.setAttribute('aria-describedby', ids.join(' '));
- }
- else {
- this._elementRef.nativeElement.removeAttribute('aria-describedby');
- }
- }
- /**
- * Implemented as part of MatFormFieldControl.
- * @docs-private
- */
- onContainerClick() {
- this.focus();
- this.open();
- }
- /**
- * Implemented as part of MatFormFieldControl.
- * @docs-private
- */
- get shouldLabelFloat() {
- return this._panelOpen || !this.empty || (this._focused && !!this._placeholder);
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatSelectBase, deps: [{ token: i1.ViewportRuler }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i2.ErrorStateMatcher }, { token: i0.ElementRef }, { token: i3.Directionality, optional: true }, { token: i4.NgForm, optional: true }, { token: i4.FormGroupDirective, optional: true }, { token: MAT_FORM_FIELD, optional: true }, { token: i4.NgControl, optional: true, self: true }, { token: 'tabindex', attribute: true }, { token: MAT_SELECT_SCROLL_STRATEGY }, { token: i5.LiveAnnouncer }, { token: MAT_SELECT_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: _MatSelectBase, inputs: { userAriaDescribedBy: ["aria-describedby", "userAriaDescribedBy"], panelClass: "panelClass", placeholder: "placeholder", required: "required", multiple: "multiple", disableOptionCentering: "disableOptionCentering", compareWith: "compareWith", value: "value", ariaLabel: ["aria-label", "ariaLabel"], ariaLabelledby: ["aria-labelledby", "ariaLabelledby"], errorStateMatcher: "errorStateMatcher", typeaheadDebounceInterval: "typeaheadDebounceInterval", sortComparator: "sortComparator", id: "id" }, outputs: { openedChange: "openedChange", _openedStream: "opened", _closedStream: "closed", selectionChange: "selectionChange", valueChange: "valueChange" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true }, { propertyName: "_overlayDir", first: true, predicate: CdkConnectedOverlay, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: _MatSelectBase, decorators: [{
- type: Directive
- }], ctorParameters: function () { return [{ type: i1.ViewportRuler }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i2.ErrorStateMatcher }, { type: i0.ElementRef }, { type: i3.Directionality, decorators: [{
- type: Optional
- }] }, { type: i4.NgForm, decorators: [{
- type: Optional
- }] }, { type: i4.FormGroupDirective, decorators: [{
- type: Optional
- }] }, { type: i6.MatFormField, decorators: [{
- type: Optional
- }, {
- type: Inject,
- args: [MAT_FORM_FIELD]
- }] }, { type: i4.NgControl, decorators: [{
- type: Self
- }, {
- type: Optional
- }] }, { type: undefined, decorators: [{
- type: Attribute,
- args: ['tabindex']
- }] }, { type: undefined, decorators: [{
- type: Inject,
- args: [MAT_SELECT_SCROLL_STRATEGY]
- }] }, { type: i5.LiveAnnouncer }, { type: undefined, decorators: [{
- type: Optional
- }, {
- type: Inject,
- args: [MAT_SELECT_CONFIG]
- }] }]; }, propDecorators: { userAriaDescribedBy: [{
- type: Input,
- args: ['aria-describedby']
- }], trigger: [{
- type: ViewChild,
- args: ['trigger']
- }], panel: [{
- type: ViewChild,
- args: ['panel']
- }], _overlayDir: [{
- type: ViewChild,
- args: [CdkConnectedOverlay]
- }], panelClass: [{
- type: Input
- }], placeholder: [{
- type: Input
- }], required: [{
- type: Input
- }], multiple: [{
- type: Input
- }], disableOptionCentering: [{
- type: Input
- }], compareWith: [{
- type: Input
- }], value: [{
- type: Input
- }], ariaLabel: [{
- type: Input,
- args: ['aria-label']
- }], ariaLabelledby: [{
- type: Input,
- args: ['aria-labelledby']
- }], errorStateMatcher: [{
- type: Input
- }], typeaheadDebounceInterval: [{
- type: Input
- }], sortComparator: [{
- type: Input
- }], id: [{
- type: Input
- }], openedChange: [{
- type: Output
- }], _openedStream: [{
- type: Output,
- args: ['opened']
- }], _closedStream: [{
- type: Output,
- args: ['closed']
- }], selectionChange: [{
- type: Output
- }], valueChange: [{
- type: Output
- }] } });
- /**
- * Allows the user to customize the trigger that is displayed when the select has a value.
- */
- class MatSelectTrigger {
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelectTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: MatSelectTrigger, selector: "mat-select-trigger", providers: [{ provide: MAT_SELECT_TRIGGER, useExisting: MatSelectTrigger }], ngImport: i0 }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelectTrigger, decorators: [{
- type: Directive,
- args: [{
- selector: 'mat-select-trigger',
- providers: [{ provide: MAT_SELECT_TRIGGER, useExisting: MatSelectTrigger }],
- }]
- }] });
- class MatSelect extends _MatSelectBase {
- constructor() {
- super(...arguments);
- this._positions = [
- {
- originX: 'start',
- originY: 'bottom',
- overlayX: 'start',
- overlayY: 'top',
- },
- {
- originX: 'start',
- originY: 'top',
- overlayX: 'start',
- overlayY: 'bottom',
- panelClass: 'mat-mdc-select-panel-above',
- },
- ];
- this._hideSingleSelectionIndicator = this._defaultOptions?.hideSingleSelectionIndicator ?? false;
- // `skipPredicate` determines if key manager should avoid putting a given option in the tab
- // order. Allow disabled list items to receive focus via keyboard to align with WAI ARIA
- // recommendation.
- //
- // Normally WAI ARIA's instructions are to exclude disabled items from the tab order, but it
- // makes a few exceptions for compound widgets.
- //
- // From [Developing a Keyboard Interface](
- // https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/):
- // "For the following composite widget elements, keep them focusable when disabled: Options in a
- // Listbox..."
- //
- // The user can focus disabled options using the keyboard, but the user cannot click disabled
- // options.
- this._skipPredicate = (option) => {
- if (this.panelOpen) {
- // Support keyboard focusing disabled options in an ARIA listbox.
- return false;
- }
- // When the panel is closed, skip over disabled options. Support options via the UP/DOWN arrow
- // keys on a closed select. ARIA listbox interaction pattern is less relevant when the panel is
- // closed.
- return option.disabled;
- };
- }
- get shouldLabelFloat() {
- // Since the panel doesn't overlap the trigger, we
- // want the label to only float when there's a value.
- return this.panelOpen || !this.empty || (this.focused && !!this.placeholder);
- }
- ngOnInit() {
- super.ngOnInit();
- this._viewportRuler
- .change()
- .pipe(takeUntil(this._destroy))
- .subscribe(() => {
- if (this.panelOpen) {
- this._overlayWidth = this._getOverlayWidth();
- this._changeDetectorRef.detectChanges();
- }
- });
- }
- ngAfterViewInit() {
- // Note that it's important that we read this in `ngAfterViewInit`, because
- // reading it earlier will cause the form field to return a different element.
- if (this._parentFormField) {
- this._preferredOverlayOrigin = this._parentFormField.getConnectedOverlayOrigin();
- }
- }
- open() {
- this._overlayWidth = this._getOverlayWidth();
- super.open();
- // Required for the MDC form field to pick up when the overlay has been opened.
- this.stateChanges.next();
- }
- close() {
- super.close();
- // Required for the MDC form field to pick up when the overlay has been closed.
- this.stateChanges.next();
- }
- /** Scrolls the active option into view. */
- _scrollOptionIntoView(index) {
- const option = this.options.toArray()[index];
- if (option) {
- const panel = this.panel.nativeElement;
- const labelCount = _countGroupLabelsBeforeOption(index, this.options, this.optionGroups);
- const element = option._getHostElement();
- if (index === 0 && labelCount === 1) {
- // If we've got one group label before the option and we're at the top option,
- // scroll the list to the top. This is better UX than scrolling the list to the
- // top of the option, because it allows the user to read the top group's label.
- panel.scrollTop = 0;
- }
- else {
- panel.scrollTop = _getOptionScrollPosition(element.offsetTop, element.offsetHeight, panel.scrollTop, panel.offsetHeight);
- }
- }
- }
- _positioningSettled() {
- this._scrollOptionIntoView(this._keyManager.activeItemIndex || 0);
- }
- _getChangeEvent(value) {
- return new MatSelectChange(this, value);
- }
- /** Gets how wide the overlay panel should be. */
- _getOverlayWidth() {
- const refToMeasure = this._preferredOverlayOrigin instanceof CdkOverlayOrigin
- ? this._preferredOverlayOrigin.elementRef
- : this._preferredOverlayOrigin || this._elementRef;
- return refToMeasure.nativeElement.getBoundingClientRect().width;
- }
- /** Whether checkmark indicator for single-selection options is hidden. */
- get hideSingleSelectionIndicator() {
- return this._hideSingleSelectionIndicator;
- }
- set hideSingleSelectionIndicator(value) {
- this._hideSingleSelectionIndicator = coerceBooleanProperty(value);
- this._syncParentProperties();
- }
- /** Syncs the parent state with the individual options. */
- _syncParentProperties() {
- if (this.options) {
- for (const option of this.options) {
- option._changeDetectorRef.markForCheck();
- }
- }
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelect, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: MatSelect, selector: "mat-select", inputs: { disabled: "disabled", disableRipple: "disableRipple", tabIndex: "tabIndex", hideSingleSelectionIndicator: "hideSingleSelectionIndicator" }, host: { attributes: { "role": "combobox", "aria-autocomplete": "none", "aria-haspopup": "listbox", "ngSkipHydration": "" }, listeners: { "keydown": "_handleKeydown($event)", "focus": "_onFocus()", "blur": "_onBlur()" }, properties: { "attr.id": "id", "attr.tabindex": "tabIndex", "attr.aria-controls": "panelOpen ? id + \"-panel\" : null", "attr.aria-expanded": "panelOpen", "attr.aria-label": "ariaLabel || null", "attr.aria-required": "required.toString()", "attr.aria-disabled": "disabled.toString()", "attr.aria-invalid": "errorState", "attr.aria-activedescendant": "_getAriaActiveDescendant()", "class.mat-mdc-select-disabled": "disabled", "class.mat-mdc-select-invalid": "errorState", "class.mat-mdc-select-required": "required", "class.mat-mdc-select-empty": "empty", "class.mat-mdc-select-multiple": "multiple" }, classAttribute: "mat-mdc-select" }, providers: [
- { provide: MatFormFieldControl, useExisting: MatSelect },
- { provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatSelect },
- ], queries: [{ propertyName: "customTrigger", first: true, predicate: MAT_SELECT_TRIGGER, descendants: true }, { propertyName: "options", predicate: MatOption, descendants: true }, { propertyName: "optionGroups", predicate: MAT_OPTGROUP, descendants: true }], exportAs: ["matSelect"], usesInheritance: true, ngImport: i0, template: "<!--\n Note that the select trigger element specifies `aria-owns` pointing to the listbox overlay.\n While aria-owns is not required for the ARIA 1.2 `role=\"combobox\"` interaction pattern,\n it fixes an issue with VoiceOver when the select appears inside of an `aria-model=\"true\"`\n element (e.g. a dialog). Without this `aria-owns`, the `aria-modal` on a dialog prevents\n VoiceOver from \"seeing\" the select's listbox overlay for aria-activedescendant.\n Using `aria-owns` re-parents the select overlay so that it works again.\n See https://github.com/angular/components/issues/20694\n-->\n<div cdk-overlay-origin\n [attr.aria-owns]=\"panelOpen ? id + '-panel' : null\"\n class=\"mat-mdc-select-trigger\"\n (click)=\"toggle()\"\n #fallbackOverlayOrigin=\"cdkOverlayOrigin\"\n #trigger>\n <div class=\"mat-mdc-select-value\" [ngSwitch]=\"empty\" [attr.id]=\"_valueId\">\n <span class=\"mat-mdc-select-placeholder mat-mdc-select-min-line\" *ngSwitchCase=\"true\">{{placeholder}}</span>\n <span class=\"mat-mdc-select-value-text\" *ngSwitchCase=\"false\" [ngSwitch]=\"!!customTrigger\">\n <span class=\"mat-mdc-select-min-line\" *ngSwitchDefault>{{triggerValue}}</span>\n <ng-content select=\"mat-select-trigger\" *ngSwitchCase=\"true\"></ng-content>\n </span>\n </div>\n\n <div class=\"mat-mdc-select-arrow-wrapper\">\n <div class=\"mat-mdc-select-arrow\">\n <!-- Use an inline SVG, because it works better than a CSS triangle in high contrast mode. -->\n <svg viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\"/>\n </svg>\n </div>\n </div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayPanelClass]=\"_overlayPanelClass\"\n [cdkConnectedOverlayScrollStrategy]=\"_scrollStrategy\"\n [cdkConnectedOverlayOrigin]=\"_preferredOverlayOrigin || fallbackOverlayOrigin\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayPositions]=\"_positions\"\n [cdkConnectedOverlayWidth]=\"_overlayWidth\"\n (backdropClick)=\"close()\"\n (attach)=\"_onAttached()\"\n (detach)=\"close()\">\n <div\n #panel\n role=\"listbox\"\n tabindex=\"-1\"\n class=\"mat-mdc-select-panel mdc-menu-surface mdc-menu-surface--open {{ _getPanelTheme() }}\"\n [attr.id]=\"id + '-panel'\"\n [attr.aria-multiselectable]=\"multiple\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"_getPanelAriaLabelledby()\"\n [ngClass]=\"panelClass\"\n [@transformPanel]=\"'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n (keydown)=\"_handleKeydown($event)\">\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: [".mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;margin:0;padding:0;transform:scale(1);transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--animating-open{display:inline-block;transform:scale(0.8);opacity:0}.mdc-menu-surface--open{display:inline-block;transform:scale(1);opacity:1}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0}[dir=rtl] .mdc-menu-surface,.mdc-menu-surface[dir=rtl]{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed}.mdc-menu-surface--fullwidth{width:100%}.mdc-menu-surface{max-width:calc(100vw - 32px);max-width:var(--mdc-menu-max-width, calc(100vw - 32px));max-height:calc(100vh - 32px);max-height:var(--mdc-menu-max-height, calc(100vh - 32px));z-index:8;border-radius:4px;border-radius:var(--mdc-shape-medium, 4px)}.mat-mdc-select{display:inline-block;width:100%;outline:none}.mat-mdc-select-trigger{display:inline-flex;align-items:center;cursor:pointer;position:relative;box-sizing:border-box;width:100%}.mat-mdc-select-disabled .mat-mdc-select-trigger{-webkit-user-select:none;user-select:none;cursor:default}.mat-mdc-select-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mat-mdc-select-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mat-mdc-select-arrow-wrapper{height:24px;flex-shrink:0;display:inline-flex;align-items:center}.mat-form-field-appearance-fill .mat-mdc-select-arrow-wrapper{transform:translateY(-8px)}.mat-form-field-appearance-fill .mdc-text-field--no-label .mat-mdc-select-arrow-wrapper{transform:none}.mat-mdc-select-arrow{width:10px;height:5px;position:relative}.mat-mdc-select-arrow svg{fill:currentColor;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.cdk-high-contrast-active .mat-mdc-select-arrow svg{fill:CanvasText}.mat-mdc-select-disabled .cdk-high-contrast-active .mat-mdc-select-arrow svg{fill:GrayText}.mdc-menu-surface.mat-mdc-select-panel{width:100%;max-height:275px;position:static;outline:0;margin:0;padding:8px 0;list-style-type:none}.mdc-menu-surface.mat-mdc-select-panel:focus{outline:none}.cdk-high-contrast-active .mdc-menu-surface.mat-mdc-select-panel{outline:solid 1px}.cdk-overlay-pane:not(.mat-mdc-select-panel-above) .mdc-menu-surface.mat-mdc-select-panel{border-top-left-radius:0;border-top-right-radius:0;transform-origin:top center}.mat-mdc-select-panel-above .mdc-menu-surface.mat-mdc-select-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:bottom center}.mat-mdc-select-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}._mat-animation-noopable .mat-mdc-select-placeholder{transition:none}.mat-form-field-hide-placeholder .mat-mdc-select-placeholder{color:rgba(0,0,0,0);-webkit-text-fill-color:rgba(0,0,0,0);transition:none;display:block}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mat-mdc-floating-label{max-width:calc(100% - 18px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 24px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-text-field--label-floating .mdc-notched-outline__notch{max-width:calc(100% - 24px)}.mat-mdc-select-min-line:empty::before{content:\" \";white-space:pre;width:1px;display:inline-block;visibility:hidden}"], dependencies: [{ kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i7.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i7.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i8.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i8.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], animations: [matSelectAnimations.transformPanel], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelect, decorators: [{
- type: Component,
- args: [{ selector: 'mat-select', exportAs: 'matSelect', inputs: ['disabled', 'disableRipple', 'tabIndex'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
- 'role': 'combobox',
- 'aria-autocomplete': 'none',
- 'aria-haspopup': 'listbox',
- 'class': 'mat-mdc-select',
- '[attr.id]': 'id',
- '[attr.tabindex]': 'tabIndex',
- '[attr.aria-controls]': 'panelOpen ? id + "-panel" : null',
- '[attr.aria-expanded]': 'panelOpen',
- '[attr.aria-label]': 'ariaLabel || null',
- '[attr.aria-required]': 'required.toString()',
- '[attr.aria-disabled]': 'disabled.toString()',
- '[attr.aria-invalid]': 'errorState',
- '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',
- 'ngSkipHydration': '',
- '[class.mat-mdc-select-disabled]': 'disabled',
- '[class.mat-mdc-select-invalid]': 'errorState',
- '[class.mat-mdc-select-required]': 'required',
- '[class.mat-mdc-select-empty]': 'empty',
- '[class.mat-mdc-select-multiple]': 'multiple',
- '(keydown)': '_handleKeydown($event)',
- '(focus)': '_onFocus()',
- '(blur)': '_onBlur()',
- }, animations: [matSelectAnimations.transformPanel], providers: [
- { provide: MatFormFieldControl, useExisting: MatSelect },
- { provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatSelect },
- ], template: "<!--\n Note that the select trigger element specifies `aria-owns` pointing to the listbox overlay.\n While aria-owns is not required for the ARIA 1.2 `role=\"combobox\"` interaction pattern,\n it fixes an issue with VoiceOver when the select appears inside of an `aria-model=\"true\"`\n element (e.g. a dialog). Without this `aria-owns`, the `aria-modal` on a dialog prevents\n VoiceOver from \"seeing\" the select's listbox overlay for aria-activedescendant.\n Using `aria-owns` re-parents the select overlay so that it works again.\n See https://github.com/angular/components/issues/20694\n-->\n<div cdk-overlay-origin\n [attr.aria-owns]=\"panelOpen ? id + '-panel' : null\"\n class=\"mat-mdc-select-trigger\"\n (click)=\"toggle()\"\n #fallbackOverlayOrigin=\"cdkOverlayOrigin\"\n #trigger>\n <div class=\"mat-mdc-select-value\" [ngSwitch]=\"empty\" [attr.id]=\"_valueId\">\n <span class=\"mat-mdc-select-placeholder mat-mdc-select-min-line\" *ngSwitchCase=\"true\">{{placeholder}}</span>\n <span class=\"mat-mdc-select-value-text\" *ngSwitchCase=\"false\" [ngSwitch]=\"!!customTrigger\">\n <span class=\"mat-mdc-select-min-line\" *ngSwitchDefault>{{triggerValue}}</span>\n <ng-content select=\"mat-select-trigger\" *ngSwitchCase=\"true\"></ng-content>\n </span>\n </div>\n\n <div class=\"mat-mdc-select-arrow-wrapper\">\n <div class=\"mat-mdc-select-arrow\">\n <!-- Use an inline SVG, because it works better than a CSS triangle in high contrast mode. -->\n <svg viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M7 10l5 5 5-5z\"/>\n </svg>\n </div>\n </div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayPanelClass]=\"_overlayPanelClass\"\n [cdkConnectedOverlayScrollStrategy]=\"_scrollStrategy\"\n [cdkConnectedOverlayOrigin]=\"_preferredOverlayOrigin || fallbackOverlayOrigin\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayPositions]=\"_positions\"\n [cdkConnectedOverlayWidth]=\"_overlayWidth\"\n (backdropClick)=\"close()\"\n (attach)=\"_onAttached()\"\n (detach)=\"close()\">\n <div\n #panel\n role=\"listbox\"\n tabindex=\"-1\"\n class=\"mat-mdc-select-panel mdc-menu-surface mdc-menu-surface--open {{ _getPanelTheme() }}\"\n [attr.id]=\"id + '-panel'\"\n [attr.aria-multiselectable]=\"multiple\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"_getPanelAriaLabelledby()\"\n [ngClass]=\"panelClass\"\n [@transformPanel]=\"'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n (keydown)=\"_handleKeydown($event)\">\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: [".mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;margin:0;padding:0;transform:scale(1);transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--animating-open{display:inline-block;transform:scale(0.8);opacity:0}.mdc-menu-surface--open{display:inline-block;transform:scale(1);opacity:1}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0}[dir=rtl] .mdc-menu-surface,.mdc-menu-surface[dir=rtl]{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed}.mdc-menu-surface--fullwidth{width:100%}.mdc-menu-surface{max-width:calc(100vw - 32px);max-width:var(--mdc-menu-max-width, calc(100vw - 32px));max-height:calc(100vh - 32px);max-height:var(--mdc-menu-max-height, calc(100vh - 32px));z-index:8;border-radius:4px;border-radius:var(--mdc-shape-medium, 4px)}.mat-mdc-select{display:inline-block;width:100%;outline:none}.mat-mdc-select-trigger{display:inline-flex;align-items:center;cursor:pointer;position:relative;box-sizing:border-box;width:100%}.mat-mdc-select-disabled .mat-mdc-select-trigger{-webkit-user-select:none;user-select:none;cursor:default}.mat-mdc-select-value{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mat-mdc-select-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mat-mdc-select-arrow-wrapper{height:24px;flex-shrink:0;display:inline-flex;align-items:center}.mat-form-field-appearance-fill .mat-mdc-select-arrow-wrapper{transform:translateY(-8px)}.mat-form-field-appearance-fill .mdc-text-field--no-label .mat-mdc-select-arrow-wrapper{transform:none}.mat-mdc-select-arrow{width:10px;height:5px;position:relative}.mat-mdc-select-arrow svg{fill:currentColor;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.cdk-high-contrast-active .mat-mdc-select-arrow svg{fill:CanvasText}.mat-mdc-select-disabled .cdk-high-contrast-active .mat-mdc-select-arrow svg{fill:GrayText}.mdc-menu-surface.mat-mdc-select-panel{width:100%;max-height:275px;position:static;outline:0;margin:0;padding:8px 0;list-style-type:none}.mdc-menu-surface.mat-mdc-select-panel:focus{outline:none}.cdk-high-contrast-active .mdc-menu-surface.mat-mdc-select-panel{outline:solid 1px}.cdk-overlay-pane:not(.mat-mdc-select-panel-above) .mdc-menu-surface.mat-mdc-select-panel{border-top-left-radius:0;border-top-right-radius:0;transform-origin:top center}.mat-mdc-select-panel-above .mdc-menu-surface.mat-mdc-select-panel{border-bottom-left-radius:0;border-bottom-right-radius:0;transform-origin:bottom center}.mat-mdc-select-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}._mat-animation-noopable .mat-mdc-select-placeholder{transition:none}.mat-form-field-hide-placeholder .mat-mdc-select-placeholder{color:rgba(0,0,0,0);-webkit-text-fill-color:rgba(0,0,0,0);transition:none;display:block}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mat-mdc-floating-label{max-width:calc(100% - 18px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-fill .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 24px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mat-mdc-form-field-type-mat-select.mat-form-field-appearance-outline .mdc-text-field--label-floating .mdc-notched-outline__notch{max-width:calc(100% - 24px)}.mat-mdc-select-min-line:empty::before{content:\" \";white-space:pre;width:1px;display:inline-block;visibility:hidden}"] }]
- }], propDecorators: { options: [{
- type: ContentChildren,
- args: [MatOption, { descendants: true }]
- }], optionGroups: [{
- type: ContentChildren,
- args: [MAT_OPTGROUP, { descendants: true }]
- }], customTrigger: [{
- type: ContentChild,
- args: [MAT_SELECT_TRIGGER]
- }], hideSingleSelectionIndicator: [{
- type: Input
- }] } });
- class MatSelectModule {
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.0", ngImport: i0, type: MatSelectModule, declarations: [MatSelect, MatSelectTrigger], imports: [CommonModule, OverlayModule, MatOptionModule, MatCommonModule], exports: [CdkScrollableModule,
- MatFormFieldModule,
- MatSelect,
- MatSelectTrigger,
- MatOptionModule,
- MatCommonModule] }); }
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelectModule, providers: [MAT_SELECT_SCROLL_STRATEGY_PROVIDER], imports: [CommonModule, OverlayModule, MatOptionModule, MatCommonModule, CdkScrollableModule,
- MatFormFieldModule,
- MatOptionModule,
- MatCommonModule] }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: MatSelectModule, decorators: [{
- type: NgModule,
- args: [{
- imports: [CommonModule, OverlayModule, MatOptionModule, MatCommonModule],
- exports: [
- CdkScrollableModule,
- MatFormFieldModule,
- MatSelect,
- MatSelectTrigger,
- MatOptionModule,
- MatCommonModule,
- ],
- declarations: [MatSelect, MatSelectTrigger],
- providers: [MAT_SELECT_SCROLL_STRATEGY_PROVIDER],
- }]
- }] });
- /**
- * Generated bundle index. Do not edit.
- */
- export { MAT_SELECT_CONFIG, MAT_SELECT_SCROLL_STRATEGY, MAT_SELECT_SCROLL_STRATEGY_PROVIDER, MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY, MAT_SELECT_TRIGGER, MatSelect, MatSelectChange, MatSelectModule, MatSelectTrigger, _MatSelectBase, matSelectAnimations };
- //# sourceMappingURL=select.mjs.map
|