@angular/cdk
Version:
Angular Material Component Development Kit
885 lines • 111 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { ChangeDetectorRef, ContentChildren, Directive, ElementRef, forwardRef, inject, Input, NgZone, Output, QueryList, } from '@angular/core';
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
import { A, DOWN_ARROW, END, ENTER, hasModifierKey, HOME, LEFT_ARROW, RIGHT_ARROW, SPACE, UP_ARROW, } from '@angular/cdk/keycodes';
import { coerceArray, coerceBooleanProperty } from '@angular/cdk/coercion';
import { SelectionModel } from '@angular/cdk/collections';
import { defer, fromEvent, merge, Subject } from 'rxjs';
import { filter, map, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { Directionality } from '@angular/cdk/bidi';
import * as i0 from "@angular/core";
/** The next id to use for creating unique DOM IDs. */
let nextId = 0;
/**
* An implementation of SelectionModel that internally always represents the selection as a
* multi-selection. This is necessary so that we can recover the full selection if the user
* switches the listbox from single-selection to multi-selection after initialization.
*
* This selection model may report multiple selected values, even if it is in single-selection
* mode. It is up to the user (CdkListbox) to check for invalid selections.
*/
class ListboxSelectionModel extends SelectionModel {
constructor(multiple = false, initiallySelectedValues, emitChanges = true, compareWith) {
super(true, initiallySelectedValues, emitChanges, compareWith);
this.multiple = multiple;
}
isMultipleSelection() {
return this.multiple;
}
select(...values) {
// The super class is always in multi-selection mode, so we need to override the behavior if
// this selection model actually belongs to a single-selection listbox.
if (this.multiple) {
return super.select(...values);
}
else {
return super.setSelection(...values);
}
}
}
/** A selectable option in a listbox. */
export class CdkOption {
constructor() {
this._generatedId = `cdk-option-${nextId++}`;
this._disabled = false;
/** The option's host element */
this.element = inject(ElementRef).nativeElement;
/** The parent listbox this option belongs to. */
this.listbox = inject(CdkListbox);
/** Emits when the option is destroyed. */
this.destroyed = new Subject();
/** Emits when the option is clicked. */
this._clicked = new Subject();
}
/** The id of the option's host element. */
get id() {
return this._id || this._generatedId;
}
set id(value) {
this._id = value;
}
/** Whether this option is disabled. */
get disabled() {
return this.listbox.disabled || this._disabled;
}
set disabled(value) {
this._disabled = coerceBooleanProperty(value);
}
/** The tabindex of the option when it is enabled. */
get enabledTabIndex() {
return this._enabledTabIndex === undefined
? this.listbox.enabledTabIndex
: this._enabledTabIndex;
}
set enabledTabIndex(value) {
this._enabledTabIndex = value;
}
ngOnDestroy() {
this.destroyed.next();
this.destroyed.complete();
}
/** Whether this option is selected. */
isSelected() {
return this.listbox.isSelected(this);
}
/** Whether this option is active. */
isActive() {
return this.listbox.isActive(this);
}
/** Toggle the selected state of this option. */
toggle() {
this.listbox.toggle(this);
}
/** Select this option if it is not selected. */
select() {
this.listbox.select(this);
}
/** Deselect this option if it is selected. */
deselect() {
this.listbox.deselect(this);
}
/** Focus this option. */
focus() {
this.element.focus();
}
/** Get the label for this element which is required by the FocusableOption interface. */
getLabel() {
return (this.typeaheadLabel ?? this.element.textContent?.trim()) || '';
}
/**
* No-op implemented as a part of `Highlightable`.
* @docs-private
*/
setActiveStyles() { }
/**
* No-op implemented as a part of `Highlightable`.
* @docs-private
*/
setInactiveStyles() { }
/** Handle focus events on the option. */
_handleFocus() {
// Options can wind up getting focused in active descendant mode if the user clicks on them.
// In this case, we push focus back to the parent listbox to prevent an extra tab stop when
// the user performs a shift+tab.
if (this.listbox.useActiveDescendant) {
this.listbox._setActiveOption(this);
this.listbox.focus();
}
}
/** Get the tabindex for this option. */
_getTabIndex() {
if (this.listbox.useActiveDescendant || this.disabled) {
return -1;
}
return this.isActive() ? this.enabledTabIndex : -1;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: CdkOption, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.1.1", type: CdkOption, isStandalone: true, selector: "[cdkOption]", inputs: { id: "id", value: ["cdkOption", "value"], typeaheadLabel: ["cdkOptionTypeaheadLabel", "typeaheadLabel"], disabled: ["cdkOptionDisabled", "disabled"], enabledTabIndex: ["tabindex", "enabledTabIndex"] }, host: { attributes: { "role": "option" }, listeners: { "click": "_clicked.next($event)", "focus": "_handleFocus()" }, properties: { "id": "id", "attr.aria-selected": "isSelected()", "attr.tabindex": "_getTabIndex()", "attr.aria-disabled": "disabled", "class.cdk-option-active": "isActive()" }, classAttribute: "cdk-option" }, exportAs: ["cdkOption"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: CdkOption, decorators: [{
type: Directive,
args: [{
selector: '[cdkOption]',
standalone: true,
exportAs: 'cdkOption',
host: {
'role': 'option',
'class': 'cdk-option',
'[id]': 'id',
'[attr.aria-selected]': 'isSelected()',
'[attr.tabindex]': '_getTabIndex()',
'[attr.aria-disabled]': 'disabled',
'[class.cdk-option-active]': 'isActive()',
'(click)': '_clicked.next($event)',
'(focus)': '_handleFocus()',
},
}]
}], propDecorators: { id: [{
type: Input
}], value: [{
type: Input,
args: ['cdkOption']
}], typeaheadLabel: [{
type: Input,
args: ['cdkOptionTypeaheadLabel']
}], disabled: [{
type: Input,
args: ['cdkOptionDisabled']
}], enabledTabIndex: [{
type: Input,
args: ['tabindex']
}] } });
export class CdkListbox {
/** The id of the option's host element. */
get id() {
return this._id || this._generatedId;
}
set id(value) {
this._id = value;
}
/** The tabindex to use when the listbox is enabled. */
get enabledTabIndex() {
return this._enabledTabIndex === undefined ? 0 : this._enabledTabIndex;
}
set enabledTabIndex(value) {
this._enabledTabIndex = value;
}
/** The value selected in the listbox, represented as an array of option values. */
get value() {
return this._invalid ? [] : this.selectionModel.selected;
}
set value(value) {
this._setSelection(value);
}
/**
* Whether the listbox allows multiple options to be selected. If the value switches from `true`
* to `false`, and more than one option is selected, all options are deselected.
*/
get multiple() {
return this.selectionModel.multiple;
}
set multiple(value) {
this.selectionModel.multiple = coerceBooleanProperty(value);
if (this.options) {
this._updateInternalValue();
}
}
/** Whether the listbox is disabled. */
get disabled() {
return this._disabled;
}
set disabled(value) {
this._disabled = coerceBooleanProperty(value);
}
/** Whether the listbox will use active descendant or will move focus onto the options. */
get useActiveDescendant() {
return this._useActiveDescendant;
}
set useActiveDescendant(shouldUseActiveDescendant) {
this._useActiveDescendant = coerceBooleanProperty(shouldUseActiveDescendant);
}
/** The orientation of the listbox. Only affects keyboard interaction, not visual layout. */
get orientation() {
return this._orientation;
}
set orientation(value) {
this._orientation = value === 'horizontal' ? 'horizontal' : 'vertical';
if (value === 'horizontal') {
this.listKeyManager?.withHorizontalOrientation(this._dir?.value || 'ltr');
}
else {
this.listKeyManager?.withVerticalOrientation();
}
}
/** The function used to compare option values. */
get compareWith() {
return this.selectionModel.compareWith;
}
set compareWith(fn) {
this.selectionModel.compareWith = fn;
}
/**
* Whether the keyboard navigation should wrap when the user presses arrow down on the last item
* or arrow up on the first item.
*/
get navigationWrapDisabled() {
return this._navigationWrapDisabled;
}
set navigationWrapDisabled(wrap) {
this._navigationWrapDisabled = coerceBooleanProperty(wrap);
this.listKeyManager?.withWrap(!this._navigationWrapDisabled);
}
/** Whether keyboard navigation should skip over disabled items. */
get navigateDisabledOptions() {
return this._navigateDisabledOptions;
}
set navigateDisabledOptions(skip) {
this._navigateDisabledOptions = coerceBooleanProperty(skip);
this.listKeyManager?.skipPredicate(this._navigateDisabledOptions ? this._skipNonePredicate : this._skipDisabledPredicate);
}
constructor() {
this._generatedId = `cdk-listbox-${nextId++}`;
this._disabled = false;
this._useActiveDescendant = false;
this._orientation = 'vertical';
this._navigationWrapDisabled = false;
this._navigateDisabledOptions = false;
/** Emits when the selected value(s) in the listbox change. */
this.valueChange = new Subject();
/** The selection model used by the listbox. */
this.selectionModel = new ListboxSelectionModel();
/** Emits when the listbox is destroyed. */
this.destroyed = new Subject();
/** The host element of the listbox. */
this.element = inject(ElementRef).nativeElement;
/** The Angular zone. */
this.ngZone = inject(NgZone);
/** The change detector for this listbox. */
this.changeDetectorRef = inject(ChangeDetectorRef);
/** Whether the currently selected value in the selection model is invalid. */
this._invalid = false;
/** The last user-triggered option. */
this._lastTriggered = null;
/** Callback called when the listbox has been touched */
this._onTouched = () => { };
/** Callback called when the listbox value changes */
this._onChange = () => { };
/** Emits when an option has been clicked. */
this._optionClicked = defer(() => this.options.changes.pipe(startWith(this.options), switchMap(options => merge(...options.map(option => option._clicked.pipe(map(event => ({ option, event }))))))));
/** The directionality of the page. */
this._dir = inject(Directionality, { optional: true });
/** A predicate that skips disabled options. */
this._skipDisabledPredicate = (option) => option.disabled;
/** A predicate that does not skip any options. */
this._skipNonePredicate = () => false;
/** Whether the listbox currently has focus. */
this._hasFocus = false;
/** A reference to the option that was active before the listbox lost focus. */
this._previousActiveOption = null;
this._setPreviousActiveOptionAsActiveOptionOnWindowBlur();
}
ngAfterContentInit() {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
this._verifyNoOptionValueCollisions();
this._verifyOptionValues();
}
this._initKeyManager();
// Update the internal value whenever the options or the model value changes.
merge(this.selectionModel.changed, this.options.changes)
.pipe(startWith(null), takeUntil(this.destroyed))
.subscribe(() => this._updateInternalValue());
this._optionClicked
.pipe(filter(({ option }) => !option.disabled), takeUntil(this.destroyed))
.subscribe(({ option, event }) => this._handleOptionClicked(option, event));
}
ngOnDestroy() {
this.listKeyManager?.destroy();
this.destroyed.next();
this.destroyed.complete();
}
/**
* Toggle the selected state of the given option.
* @param option The option to toggle
*/
toggle(option) {
this.toggleValue(option.value);
}
/**
* Toggle the selected state of the given value.
* @param value The value to toggle
*/
toggleValue(value) {
if (this._invalid) {
this.selectionModel.clear(false);
}
this.selectionModel.toggle(value);
}
/**
* Select the given option.
* @param option The option to select
*/
select(option) {
this.selectValue(option.value);
}
/**
* Select the given value.
* @param value The value to select
*/
selectValue(value) {
if (this._invalid) {
this.selectionModel.clear(false);
}
this.selectionModel.select(value);
}
/**
* Deselect the given option.
* @param option The option to deselect
*/
deselect(option) {
this.deselectValue(option.value);
}
/**
* Deselect the given value.
* @param value The value to deselect
*/
deselectValue(value) {
if (this._invalid) {
this.selectionModel.clear(false);
}
this.selectionModel.deselect(value);
}
/**
* Set the selected state of all options.
* @param isSelected The new selected state to set
*/
setAllSelected(isSelected) {
if (!isSelected) {
this.selectionModel.clear();
}
else {
if (this._invalid) {
this.selectionModel.clear(false);
}
this.selectionModel.select(...this.options.map(option => option.value));
}
}
/**
* Get whether the given option is selected.
* @param option The option to get the selected state of
*/
isSelected(option) {
return this.isValueSelected(option.value);
}
/**
* Get whether the given option is active.
* @param option The option to get the active state of
*/
isActive(option) {
return !!(this.listKeyManager?.activeItem === option);
}
/**
* Get whether the given value is selected.
* @param value The value to get the selected state of
*/
isValueSelected(value) {
if (this._invalid) {
return false;
}
return this.selectionModel.isSelected(value);
}
/**
* Registers a callback to be invoked when the listbox's value changes from user input.
* @param fn The callback to register
* @docs-private
*/
registerOnChange(fn) {
this._onChange = fn;
}
/**
* Registers a callback to be invoked when the listbox is blurred by the user.
* @param fn The callback to register
* @docs-private
*/
registerOnTouched(fn) {
this._onTouched = fn;
}
/**
* Sets the listbox's value.
* @param value The new value of the listbox
* @docs-private
*/
writeValue(value) {
this._setSelection(value);
this._verifyOptionValues();
}
/**
* Sets the disabled state of the listbox.
* @param isDisabled The new disabled state
* @docs-private
*/
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
/** Focus the listbox's host element. */
focus() {
this.element.focus();
}
/**
* Triggers the given option in response to user interaction.
* - In single selection mode: selects the option and deselects any other selected option.
* - In multi selection mode: toggles the selected state of the option.
* @param option The option to trigger
*/
triggerOption(option) {
if (option && !option.disabled) {
this._lastTriggered = option;
const changed = this.multiple
? this.selectionModel.toggle(option.value)
: this.selectionModel.select(option.value);
if (changed) {
this._onChange(this.value);
this.valueChange.next({
value: this.value,
listbox: this,
option: option,
});
}
}
}
/**
* Trigger the given range of options in response to user interaction.
* Should only be called in multi-selection mode.
* @param trigger The option that was triggered
* @param from The start index of the options to toggle
* @param to The end index of the options to toggle
* @param on Whether to toggle the option range on
*/
triggerRange(trigger, from, to, on) {
if (this.disabled || (trigger && trigger.disabled)) {
return;
}
this._lastTriggered = trigger;
const isEqual = this.compareWith ?? Object.is;
const updateValues = [...this.options]
.slice(Math.max(0, Math.min(from, to)), Math.min(this.options.length, Math.max(from, to) + 1))
.filter(option => !option.disabled)
.map(option => option.value);
const selected = [...this.value];
for (const updateValue of updateValues) {
const selectedIndex = selected.findIndex(selectedValue => isEqual(selectedValue, updateValue));
if (on && selectedIndex === -1) {
selected.push(updateValue);
}
else if (!on && selectedIndex !== -1) {
selected.splice(selectedIndex, 1);
}
}
let changed = this.selectionModel.setSelection(...selected);
if (changed) {
this._onChange(this.value);
this.valueChange.next({
value: this.value,
listbox: this,
option: trigger,
});
}
}
/**
* Sets the given option as active.
* @param option The option to make active
*/
_setActiveOption(option) {
this.listKeyManager.setActiveItem(option);
}
/** Called when the listbox receives focus. */
_handleFocus() {
if (!this.useActiveDescendant) {
if (this.selectionModel.selected.length > 0) {
this._setNextFocusToSelectedOption();
}
else {
this.listKeyManager.setNextItemActive();
}
this._focusActiveOption();
}
}
/** Called when the user presses keydown on the listbox. */
_handleKeydown(event) {
if (this._disabled) {
return;
}
const { keyCode } = event;
const previousActiveIndex = this.listKeyManager.activeItemIndex;
const ctrlKeys = ['ctrlKey', 'metaKey'];
if (this.multiple && keyCode === A && hasModifierKey(event, ...ctrlKeys)) {
// Toggle all options off if they're all selected, otherwise toggle them all on.
this.triggerRange(null, 0, this.options.length - 1, this.options.length !== this.value.length);
event.preventDefault();
return;
}
if (this.multiple &&
(keyCode === SPACE || keyCode === ENTER) &&
hasModifierKey(event, 'shiftKey')) {
if (this.listKeyManager.activeItem && this.listKeyManager.activeItemIndex != null) {
this.triggerRange(this.listKeyManager.activeItem, this._getLastTriggeredIndex() ?? this.listKeyManager.activeItemIndex, this.listKeyManager.activeItemIndex, !this.listKeyManager.activeItem.isSelected());
}
event.preventDefault();
return;
}
if (this.multiple &&
keyCode === HOME &&
hasModifierKey(event, ...ctrlKeys) &&
hasModifierKey(event, 'shiftKey')) {
const trigger = this.listKeyManager.activeItem;
if (trigger) {
const from = this.listKeyManager.activeItemIndex;
this.listKeyManager.setFirstItemActive();
this.triggerRange(trigger, from, this.listKeyManager.activeItemIndex, !trigger.isSelected());
}
event.preventDefault();
return;
}
if (this.multiple &&
keyCode === END &&
hasModifierKey(event, ...ctrlKeys) &&
hasModifierKey(event, 'shiftKey')) {
const trigger = this.listKeyManager.activeItem;
if (trigger) {
const from = this.listKeyManager.activeItemIndex;
this.listKeyManager.setLastItemActive();
this.triggerRange(trigger, from, this.listKeyManager.activeItemIndex, !trigger.isSelected());
}
event.preventDefault();
return;
}
if (keyCode === SPACE || keyCode === ENTER) {
this.triggerOption(this.listKeyManager.activeItem);
event.preventDefault();
return;
}
const isNavKey = keyCode === UP_ARROW ||
keyCode === DOWN_ARROW ||
keyCode === LEFT_ARROW ||
keyCode === RIGHT_ARROW ||
keyCode === HOME ||
keyCode === END;
this.listKeyManager.onKeydown(event);
// Will select an option if shift was pressed while navigating to the option
if (isNavKey && event.shiftKey && previousActiveIndex !== this.listKeyManager.activeItemIndex) {
this.triggerOption(this.listKeyManager.activeItem);
}
}
/** Called when a focus moves into the listbox. */
_handleFocusIn() {
// Note that we use a `focusin` handler for this instead of the existing `focus` handler,
// because focus won't land on the listbox if `useActiveDescendant` is enabled.
this._hasFocus = true;
}
/**
* Called when the focus leaves an element in the listbox.
* @param event The focusout event
*/
_handleFocusOut(event) {
// Some browsers (e.g. Chrome and Firefox) trigger the focusout event when the user returns back to the document.
// To prevent losing the active option in this case, we store it in `_previousActiveOption` and restore it on the window `blur` event
// This ensures that the `activeItem` matches the actual focused element when the user returns to the document.
this._previousActiveOption = this.listKeyManager.activeItem;
const otherElement = event.relatedTarget;
if (this.element !== otherElement && !this.element.contains(otherElement)) {
this._onTouched();
this._hasFocus = false;
this._setNextFocusToSelectedOption();
}
}
/** Get the id of the active option if active descendant is being used. */
_getAriaActiveDescendant() {
return this._useActiveDescendant ? this.listKeyManager?.activeItem?.id : null;
}
/** Get the tabindex for the listbox. */
_getTabIndex() {
if (this.disabled) {
return -1;
}
return this.useActiveDescendant || !this.listKeyManager.activeItem ? this.enabledTabIndex : -1;
}
/** Initialize the key manager. */
_initKeyManager() {
this.listKeyManager = new ActiveDescendantKeyManager(this.options)
.withWrap(!this._navigationWrapDisabled)
.withTypeAhead()
.withHomeAndEnd()
.withAllowedModifierKeys(['shiftKey'])
.skipPredicate(this._navigateDisabledOptions ? this._skipNonePredicate : this._skipDisabledPredicate);
if (this.orientation === 'vertical') {
this.listKeyManager.withVerticalOrientation();
}
else {
this.listKeyManager.withHorizontalOrientation(this._dir?.value || 'ltr');
}
if (this.selectionModel.selected.length) {
Promise.resolve().then(() => this._setNextFocusToSelectedOption());
}
this.listKeyManager.change.subscribe(() => this._focusActiveOption());
}
/** Focus the active option. */
_focusActiveOption() {
if (!this.useActiveDescendant) {
this.listKeyManager.activeItem?.focus();
}
this.changeDetectorRef.markForCheck();
}
/**
* Set the selected values.
* @param value The list of new selected values.
*/
_setSelection(value) {
if (this._invalid) {
this.selectionModel.clear(false);
}
this.selectionModel.setSelection(...this._coerceValue(value));
if (!this._hasFocus) {
this._setNextFocusToSelectedOption();
}
}
/** Sets the first selected option as first in the keyboard focus order. */
_setNextFocusToSelectedOption() {
// Null check the options since they only get defined after `ngAfterContentInit`.
const selected = this.options?.find(option => option.isSelected());
if (selected) {
this.listKeyManager.updateActiveItem(selected);
}
}
/** Update the internal value of the listbox based on the selection model. */
_updateInternalValue() {
const indexCache = new Map();
this.selectionModel.sort((a, b) => {
const aIndex = this._getIndexForValue(indexCache, a);
const bIndex = this._getIndexForValue(indexCache, b);
return aIndex - bIndex;
});
const selected = this.selectionModel.selected;
this._invalid =
(!this.multiple && selected.length > 1) || !!this._getInvalidOptionValues(selected).length;
this.changeDetectorRef.markForCheck();
}
/**
* Gets the index of the given value in the given list of options.
* @param cache The cache of indices found so far
* @param value The value to find
* @return The index of the value in the options list
*/
_getIndexForValue(cache, value) {
const isEqual = this.compareWith || Object.is;
if (!cache.has(value)) {
let index = -1;
for (let i = 0; i < this.options.length; i++) {
if (isEqual(value, this.options.get(i).value)) {
index = i;
break;
}
}
cache.set(value, index);
}
return cache.get(value);
}
/**
* Handle the user clicking an option.
* @param option The option that was clicked.
*/
_handleOptionClicked(option, event) {
event.preventDefault();
this.listKeyManager.setActiveItem(option);
if (event.shiftKey && this.multiple) {
this.triggerRange(option, this._getLastTriggeredIndex() ?? this.listKeyManager.activeItemIndex, this.listKeyManager.activeItemIndex, !option.isSelected());
}
else {
this.triggerOption(option);
}
}
/** Verifies that no two options represent the same value under the compareWith function. */
_verifyNoOptionValueCollisions() {
this.options.changes.pipe(startWith(this.options), takeUntil(this.destroyed)).subscribe(() => {
const isEqual = this.compareWith ?? Object.is;
for (let i = 0; i < this.options.length; i++) {
const option = this.options.get(i);
let duplicate = null;
for (let j = i + 1; j < this.options.length; j++) {
const other = this.options.get(j);
if (isEqual(option.value, other.value)) {
duplicate = other;
break;
}
}
if (duplicate) {
// TODO(mmalerba): Link to docs about this.
if (this.compareWith) {
console.warn(`Found multiple CdkOption representing the same value under the given compareWith function`, {
option1: option.element,
option2: duplicate.element,
compareWith: this.compareWith,
});
}
else {
console.warn(`Found multiple CdkOption with the same value`, {
option1: option.element,
option2: duplicate.element,
});
}
return;
}
}
});
}
/** Verifies that the option values are valid. */
_verifyOptionValues() {
if (this.options && (typeof ngDevMode === 'undefined' || ngDevMode)) {
const selected = this.selectionModel.selected;
const invalidValues = this._getInvalidOptionValues(selected);
if (!this.multiple && selected.length > 1) {
throw Error('Listbox cannot have more than one selected value in multi-selection mode.');
}
if (invalidValues.length) {
throw Error('Listbox has selected values that do not match any of its options.');
}
}
}
/**
* Coerces a value into an array representing a listbox selection.
* @param value The value to coerce
* @return An array
*/
_coerceValue(value) {
return value == null ? [] : coerceArray(value);
}
/**
* Get the sublist of values that do not represent valid option values in this listbox.
* @param values The list of values
* @return The sublist of values that are not valid option values
*/
_getInvalidOptionValues(values) {
const isEqual = this.compareWith || Object.is;
const validValues = (this.options || []).map(option => option.value);
return values.filter(value => !validValues.some(validValue => isEqual(value, validValue)));
}
/** Get the index of the last triggered option. */
_getLastTriggeredIndex() {
const index = this.options.toArray().indexOf(this._lastTriggered);
return index === -1 ? null : index;
}
/**
* Set previous active option as active option on window blur.
* This ensures that the `activeOption` matches the actual focused element when the user returns to the document.
*/
_setPreviousActiveOptionAsActiveOptionOnWindowBlur() {
this.ngZone.runOutsideAngular(() => {
fromEvent(window, 'blur')
.pipe(takeUntil(this.destroyed))
.subscribe(() => {
if (this.element.contains(document.activeElement) && this._previousActiveOption) {
this._setActiveOption(this._previousActiveOption);
this._previousActiveOption = null;
}
});
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: CdkListbox, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.1.1", type: CdkListbox, isStandalone: true, selector: "[cdkListbox]", inputs: { id: "id", enabledTabIndex: ["tabindex", "enabledTabIndex"], value: ["cdkListboxValue", "value"], multiple: ["cdkListboxMultiple", "multiple"], disabled: ["cdkListboxDisabled", "disabled"], useActiveDescendant: ["cdkListboxUseActiveDescendant", "useActiveDescendant"], orientation: ["cdkListboxOrientation", "orientation"], compareWith: ["cdkListboxCompareWith", "compareWith"], navigationWrapDisabled: ["cdkListboxNavigationWrapDisabled", "navigationWrapDisabled"], navigateDisabledOptions: ["cdkListboxNavigatesDisabledOptions", "navigateDisabledOptions"] }, outputs: { valueChange: "cdkListboxValueChange" }, host: { attributes: { "role": "listbox" }, listeners: { "focus": "_handleFocus()", "keydown": "_handleKeydown($event)", "focusout": "_handleFocusOut($event)", "focusin": "_handleFocusIn()" }, properties: { "id": "id", "attr.tabindex": "_getTabIndex()", "attr.aria-disabled": "disabled", "attr.aria-multiselectable": "multiple", "attr.aria-activedescendant": "_getAriaActiveDescendant()", "attr.aria-orientation": "orientation" }, classAttribute: "cdk-listbox" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CdkListbox),
multi: true,
},
], queries: [{ propertyName: "options", predicate: CdkOption, descendants: true }], exportAs: ["cdkListbox"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.1", ngImport: i0, type: CdkListbox, decorators: [{
type: Directive,
args: [{
selector: '[cdkListbox]',
standalone: true,
exportAs: 'cdkListbox',
host: {
'role': 'listbox',
'class': 'cdk-listbox',
'[id]': 'id',
'[attr.tabindex]': '_getTabIndex()',
'[attr.aria-disabled]': 'disabled',
'[attr.aria-multiselectable]': 'multiple',
'[attr.aria-activedescendant]': '_getAriaActiveDescendant()',
'[attr.aria-orientation]': 'orientation',
'(focus)': '_handleFocus()',
'(keydown)': '_handleKeydown($event)',
'(focusout)': '_handleFocusOut($event)',
'(focusin)': '_handleFocusIn()',
},
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CdkListbox),
multi: true,
},
],
}]
}], ctorParameters: function () { return []; }, propDecorators: { id: [{
type: Input
}], enabledTabIndex: [{
type: Input,
args: ['tabindex']
}], value: [{
type: Input,
args: ['cdkListboxValue']
}], multiple: [{
type: Input,
args: ['cdkListboxMultiple']
}], disabled: [{
type: Input,
args: ['cdkListboxDisabled']
}], useActiveDescendant: [{
type: Input,
args: ['cdkListboxUseActiveDescendant']
}], orientation: [{
type: Input,
args: ['cdkListboxOrientation']
}], compareWith: [{
type: Input,
args: ['cdkListboxCompareWith']
}], navigationWrapDisabled: [{
type: Input,
args: ['cdkListboxNavigationWrapDisabled']
}], navigateDisabledOptions: [{
type: Input,
args: ['cdkListboxNavigatesDisabledOptions']
}], valueChange: [{
type: Output,
args: ['cdkListboxValueChange']
}], options: [{
type: ContentChildren,
args: [CdkOption, { descendants: true }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdGJveC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jZGsvbGlzdGJveC9saXN0Ym94LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFFTCxpQkFBaUIsRUFDakIsZUFBZSxFQUNmLFNBQVMsRUFDVCxVQUFVLEVBQ1YsVUFBVSxFQUNWLE1BQU0sRUFDTixLQUFLLEVBQ0wsTUFBTSxFQUVOLE1BQU0sRUFDTixTQUFTLEdBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFDLDBCQUEwQixFQUFzQyxNQUFNLG1CQUFtQixDQUFDO0FBQ2xHLE9BQU8sRUFDTCxDQUFDLEVBQ0QsVUFBVSxFQUNWLEdBQUcsRUFDSCxLQUFLLEVBQ0wsY0FBYyxFQUNkLElBQUksRUFDSixVQUFVLEVBQ1YsV0FBVyxFQUNYLEtBQUssRUFDTCxRQUFRLEdBQ1QsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQixPQUFPLEVBQWUsV0FBVyxFQUFFLHFCQUFxQixFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFDdkYsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLDBCQUEwQixDQUFDO0FBQ3hELE9BQU8sRUFBQyxLQUFLLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBYyxPQUFPLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFDbEUsT0FBTyxFQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUM1RSxPQUFPLEVBQXVCLGlCQUFpQixFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFDdkUsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLG1CQUFtQixDQUFDOztBQUVqRCxzREFBc0Q7QUFDdEQsSUFBSSxNQUFNLEdBQUcsQ0FBQyxDQUFDO0FBRWY7Ozs7Ozs7R0FPRztBQUNILE1BQU0scUJBQXlCLFNBQVEsY0FBaUI7SUFDdEQsWUFDUyxX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