igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1 lines • 103 kB
Source Map (JSON)
{"version":3,"file":"igniteui-angular-drop-down.mjs","sources":["../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down-group.component.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down.common.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down-item.base.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down-item.component.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down-item.component.html","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down-navigation.directive.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down.base.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down.component.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down.component.html","../../../projects/igniteui-angular/drop-down/src/drop-down/autocomplete/autocomplete.directive.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/autocomplete/autocomplete.module.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/public_api.ts","../../../projects/igniteui-angular/drop-down/src/drop-down/drop-down.module.ts","../../../projects/igniteui-angular/drop-down/src/igniteui-angular-drop-down.ts"],"sourcesContent":["import { Component, Input, HostBinding, booleanAttribute } from '@angular/core';\n\nlet NEXT_ID = 0;\n/**\n * The `<igx-drop-down-item>` is a container intended for row items in\n * a `<igx-drop-down>` container.\n */\n@Component({\n selector: 'igx-drop-down-item-group',\n template: `\n <label id=\"{{labelId}}\">{{ label }}</label>\n <ng-content select=\"igx-drop-down-item\"></ng-content>\n `,\n standalone: true\n})\nexport class IgxDropDownGroupComponent {\n /**\n * @hidden @internal\n */\n public get labelId(): string {\n return `igx-item-group-label-${this._id}`;\n }\n\n @HostBinding(`attr.aria-labelledby`)\n public get labelledBy(): string {\n return this.labelId;\n }\n\n /**\n * @hidden @internal\n */\n @HostBinding('attr.role')\n public role = 'group';\n\n /** @hidden @internal */\n @HostBinding('class.igx-drop-down__group')\n public groupClass = true;\n /**\n * Sets/gets if the item group is disabled\n *\n * ```typescript\n * const myDropDownGroup: IgxDropDownGroupComponent = this.dropdownGroup;\n * // get\n * ...\n * const groupState: boolean = myDropDownGroup.disabled;\n * ...\n * //set\n * ...\n * myDropDownGroup,disabled = false;\n * ...\n * ```\n *\n * ```html\n * <igx-drop-down-item-group [label]=\"'My Items'\" [disabled]=\"true\">\n * <igx-drop-down-item *ngFor=\"let item of items[index]\" [value]=\"item.value\">\n * {{ item.text }}\n * </igx-drop-down-item>\n * </igx-drop-down-item-group>\n * ```\n *\n * **NOTE:** All items inside of a disabled drop down group will be treated as disabled\n */\n @Input({ transform: booleanAttribute })\n @HostBinding(`attr.aria-disabled`)\n @HostBinding('class.igx-drop-down__group--disabled')\n public disabled = false;\n\n /**\n * Sets/gets the label of the item group\n *\n * ```typescript\n * const myDropDownGroup: IgxDropDownGroupComponent = this.dropdownGroup;\n * // get\n * ...\n * const myLabel: string = myDropDownGroup.label;\n * ...\n * // set\n * ...\n * myDropDownGroup.label = 'My New Label';\n * ...\n * ```\n *\n * ```html\n * <igx-drop-down-item-group [label]=\"'My new Label'\">\n * ...\n * </igx-drop-down-item-group>\n * ```\n */\n @Input()\n public label: string;\n\n private _id = NEXT_ID++;\n}\n","import { CancelableEventArgs, CancelableBrowserEventArgs, IBaseEventArgs } from 'igniteui-angular/core';\nimport { IgxDropDownItemBaseDirective } from './drop-down-item.base';\nimport { IToggleView } from 'igniteui-angular/core';\nimport { EventEmitter, InjectionToken } from '@angular/core';\n\n/** @hidden */\nexport enum Navigate {\n Up = -1,\n Down = 1\n}\n\n/** Key actions that have designated handlers in IgxDropDownComponent */\nexport const DropDownActionKey = {\n ESCAPE: 'escape',\n ENTER: 'enter',\n SPACE: 'space',\n TAB: 'tab'\n} as const;\nexport type DropDownActionKey = (typeof DropDownActionKey)[keyof typeof DropDownActionKey];\n\n/**\n * Interface that encapsulates selectionChanging event arguments - old selection, new selection and cancel selection.\n *\n * @export\n */\nexport interface ISelectionEventArgs extends CancelableEventArgs, IBaseEventArgs {\n oldSelection: IgxDropDownItemBaseDirective;\n newSelection: IgxDropDownItemBaseDirective;\n}\n\n/**\n * Interface for an instance of IgxDropDownNavigationDirective\n *\n * @export\n */\nexport interface IDropDownNavigationDirective {\n target: any;\n handleKeyDown(event: KeyboardEvent): void;\n onArrowDownKeyDown(event?: KeyboardEvent): void;\n onArrowUpKeyDown(event?: KeyboardEvent): void;\n onEndKeyDown(event?: KeyboardEvent): void;\n onHomeKeyDown(event?: KeyboardEvent): void;\n}\n\nexport const IGX_DROPDOWN_BASE = /*@__PURE__*/new InjectionToken<IDropDownBase>('IgxDropDownBaseToken');\n\n/**\n * @hidden\n */\nexport interface IDropDownList {\n selectionChanging: EventEmitter<ISelectionEventArgs>;\n width: string;\n height: string;\n id: string;\n maxHeight: string;\n collapsed: boolean;\n items: IgxDropDownItemBaseDirective[];\n headers: IgxDropDownItemBaseDirective[];\n focusedItem: IgxDropDownItemBaseDirective;\n navigateFirst(): void;\n navigateLast(): void;\n navigateNext(): void;\n navigatePrev(): void;\n navigateItem(newIndex: number, direction?: Navigate): void;\n onItemActionKey(key: DropDownActionKey, event?: Event): void;\n}\n\n/**\n * @hidden\n */\nexport interface IDropDownBase extends IDropDownList, IToggleView {\n selectedItem: any;\n opening: EventEmitter<CancelableBrowserEventArgs>;\n opened: EventEmitter<IBaseEventArgs>;\n closing: EventEmitter<CancelableBrowserEventArgs>;\n closed: EventEmitter<IBaseEventArgs>;\n allowItemsFocus?: boolean;\n setSelectedItem(index: number): void;\n selectItem(item: IgxDropDownItemBaseDirective, event?: Event, emit?: boolean): void;\n}\n\n","import { IDropDownBase, IGX_DROPDOWN_BASE } from './drop-down.common';\nimport { Directive, Input, HostBinding, HostListener, ElementRef, Output, EventEmitter, booleanAttribute, DoCheck, inject } from '@angular/core';\nimport { IgxSelectionAPIService } from 'igniteui-angular/core';\nimport { IgxDropDownGroupComponent } from './drop-down-group.component';\n\nlet NEXT_ID = 0;\n\n/**\n * An abstract class defining a drop-down item:\n * With properties / styles for selection, highlight, height\n * Bindable property for passing data (`value: any`)\n * Parent component (has to be used under a parent with type `IDropDownBase`)\n * Method for handling click on Host()\n */\n@Directive({\n selector: '[igxDropDownItemBase]',\n standalone: true\n})\nexport class IgxDropDownItemBaseDirective implements DoCheck {\n protected dropDown = inject<IDropDownBase>(IGX_DROPDOWN_BASE);\n protected elementRef = inject(ElementRef);\n protected group = inject(IgxDropDownGroupComponent, { optional: true });\n protected selection? = inject<IgxSelectionAPIService>(IgxSelectionAPIService, { optional: true });\n\n /**\n * Sets/gets the `id` of the item.\n * ```html\n * <igx-drop-down-item [id] = 'igx-drop-down-item-0'></igx-drop-down-item>\n * ```\n * ```typescript\n * let itemId = this.item.id;\n * ```\n *\n * @memberof IgxSelectItemComponent\n */\n @HostBinding('attr.id')\n @Input()\n public id = `igx-drop-down-item-${NEXT_ID++}`;\n\n @HostBinding('attr.aria-label')\n @Input()\n public get ariaLabel(): string | null{\n return this._label ? this._label : this.value ? this.value : null;\n }\n\n public set ariaLabel(value: string | null) {\n this._label = value;\n }\n\n /**\n * @hidden @internal\n */\n public get itemID() {\n return this;\n }\n\n /**\n * The data index of the dropdown item.\n *\n * ```typescript\n * // get the data index of the selected dropdown item\n * let selectedItemIndex = this.dropdown.selectedItem.index\n * ```\n */\n @Input()\n public get index(): number {\n if (this._index === null) {\n return this.itemIndex;\n }\n return this._index;\n }\n\n public set index(value) {\n this._index = value;\n }\n\n /**\n * Gets/sets the value of the item if the item is databound\n *\n * ```typescript\n * // usage in IgxDropDownItemComponent\n * // get\n * let mySelectedItemValue = this.dropdown.selectedItem.value;\n *\n * // set\n * let mySelectedItem = this.dropdown.selectedItem;\n * mySelectedItem.value = { id: 123, name: 'Example Name' }\n *\n * // usage in IgxComboItemComponent\n * // get\n * let myComboItemValue = this.combo.items[0].value;\n * ```\n */\n @Input()\n public value: any;\n\n /**\n * @hidden @internal\n */\n @HostBinding('class.igx-drop-down__item')\n public get itemStyle(): boolean {\n return !this.isHeader;\n }\n\n /**\n * Sets/Gets if the item is the currently selected one in the dropdown\n *\n * ```typescript\n * let mySelectedItem = this.dropdown.selectedItem;\n * let isMyItemSelected = mySelectedItem.selected; // true\n * ```\n *\n * Two-way data binding\n * ```html\n * <igx-drop-down-item [(selected)]='model.isSelected'></igx-drop-down-item>\n * ```\n */\n @Input({ transform: booleanAttribute })\n @HostBinding('attr.aria-selected')\n @HostBinding('class.igx-drop-down__item--selected')\n public get selected(): boolean {\n return this._selected;\n }\n\n public set selected(value: boolean) {\n if (this.isHeader) {\n return;\n }\n this._selected = value;\n this.selectedChange.emit(this._selected);\n }\n\n /**\n * @hidden\n */\n @Output()\n public selectedChange = new EventEmitter<boolean>();\n\n /**\n * Sets/gets if the given item is focused\n * ```typescript\n * let mySelectedItem = this.dropdown.selectedItem;\n * let isMyItemFocused = mySelectedItem.focused;\n * ```\n */\n @HostBinding('class.igx-drop-down__item--focused')\n public get focused(): boolean {\n return this.isSelectable && this._focused;\n }\n\n /**\n * ```html\n * <igx-drop-down-item *ngFor=\"let item of items\" focused={{!item.focused}}>\n * <div>\n * {{item.field}}\n * </div>\n * </igx-drop-down-item>\n * ```\n */\n public set focused(value: boolean) {\n this._focused = value;\n }\n\n /**\n * Sets/gets if the given item is header\n * ```typescript\n * // get\n * let mySelectedItem = this.dropdown.selectedItem;\n * let isMyItemHeader = mySelectedItem.isHeader;\n * ```\n *\n * ```html\n * <!--set-->\n * <igx-drop-down-item *ngFor=\"let item of items\">\n * <div *ngIf=\"items.indexOf(item) === 5; then item.isHeader = true\">\n * {{item.field}}\n * </div>\n * </igx-drop-down-item>\n * ```\n */\n @Input({ transform: booleanAttribute })\n @HostBinding('class.igx-drop-down__header')\n public isHeader: boolean;\n\n /**\n * Sets/gets if the given item is disabled\n *\n * ```typescript\n * // get\n * let mySelectedItem = this.dropdown.selectedItem;\n * let myItemIsDisabled = mySelectedItem.disabled;\n * ```\n *\n * ```html\n * <igx-drop-down-item *ngFor=\"let item of items\" disabled={{!item.disabled}}>\n * <div>\n * {{item.field}}\n * </div>\n * </igx-drop-down-item>\n * ```\n * **NOTE:** Drop-down items inside of a disabled `IgxDropDownGroup` will always count as disabled\n */\n @Input({ transform: booleanAttribute })\n @HostBinding('attr.aria-disabled')\n @HostBinding('class.igx-drop-down__item--disabled')\n public get disabled(): boolean {\n return this.group ? this.group.disabled || this._disabled : this._disabled;\n }\n\n public set disabled(value: boolean) {\n this._disabled = value;\n }\n\n /**\n * Gets/sets the `role` attribute of the item. Default is 'option'.\n *\n * ```html\n * <igx-drop-down-item [role]=\"customRole\"></igx-drop-down-item>\n * ```\n */\n @Input()\n @HostBinding('attr.role')\n public role = 'option';\n\n /**\n * Gets item index\n *\n * @hidden @internal\n */\n public get itemIndex(): number {\n return this.dropDown.items.indexOf(this);\n }\n\n /**\n * Gets item element height\n *\n * @hidden @internal\n */\n public get elementHeight(): number {\n return this.elementRef.nativeElement.clientHeight;\n }\n\n /**\n * Get item html element\n *\n * @hidden @internal\n */\n public get element(): ElementRef {\n return this.elementRef;\n }\n\n protected get hasIndex(): boolean {\n return this._index !== null && this._index !== undefined;\n }\n\n /**\n * @hidden\n */\n protected _focused = false;\n protected _selected = false;\n protected _index = null;\n protected _disabled = false;\n protected _label = null;\n\n /**\n * @hidden\n * @internal\n */\n @HostListener('click', ['$event'])\n public clicked(event): void { // eslint-disable-line\n }\n\n /**\n * @hidden\n * @internal\n */\n @HostListener('mousedown', ['$event'])\n public handleMousedown(event: MouseEvent): void {\n if (!this.dropDown.allowItemsFocus) {\n event.preventDefault();\n }\n }\n\n public ngDoCheck(): void {\n if (this._selected) {\n const dropDownSelectedItem = this.dropDown.selectedItem;\n if (!dropDownSelectedItem) {\n this.dropDown.selectItem(this, undefined, false);\n } else if (this.hasIndex\n ? this._index !== dropDownSelectedItem.index || this.value !== dropDownSelectedItem.value :\n this !== dropDownSelectedItem) {\n this.dropDown.selectItem(this, undefined, false);\n }\n }\n }\n\n /** Returns true if the items is not a header or disabled */\n protected get isSelectable(): boolean {\n return !(this.disabled || this.isHeader);\n }\n\n /** If `allowItemsFocus` is enabled, keep the browser focus on the active item */\n protected ensureItemFocus() {\n if (this.dropDown.allowItemsFocus) {\n const focusedItem = this.dropDown.items.find((item) => item.focused);\n if (!focusedItem) {\n return;\n }\n focusedItem.element.nativeElement.focus({ preventScroll: true });\n }\n }\n}\n","import {\n Component,\n HostBinding\n} from '@angular/core';\nimport { IgxDropDownItemBaseDirective } from './drop-down-item.base';\n\n/**\n * The `<igx-drop-down-item>` is a container intended for row items in\n * a `<igx-drop-down>` container.\n */\n@Component({\n selector: 'igx-drop-down-item',\n templateUrl: 'drop-down-item.component.html',\n standalone: true\n})\nexport class IgxDropDownItemComponent extends IgxDropDownItemBaseDirective {\n /**\n * Sets/gets if the given item is focused\n * ```typescript\n * let mySelectedItem = this.dropdown.selectedItem;\n * let isMyItemFocused = mySelectedItem.focused;\n * ```\n */\n public override get focused(): boolean {\n let focusedState = this._focused;\n if (this.hasIndex) {\n const focusedItem = this.selection.first_item(`${this.dropDown.id}-active`);\n const focusedIndex = focusedItem ? focusedItem.index : -1;\n focusedState = this._index === focusedIndex;\n }\n return this.isSelectable && focusedState;\n }\n\n /**\n * Sets/gets if the given item is focused\n * ```typescript\n * let mySelectedItem = this.dropdown.selectedItem;\n * let isMyItemFocused = mySelectedItem.focused;\n * ```\n */\n public override set focused(value: boolean) {\n this._focused = value;\n }\n /**\n * Sets/Gets if the item is the currently selected one in the dropdown\n *\n * ```typescript\n * let mySelectedItem = this.dropdown.selectedItem;\n * let isMyItemSelected = mySelectedItem.selected; // true\n * ```\n *\n * Two-way data binding\n * ```html\n * <igx-drop-down-item [(selected)]='model.isSelected'></igx-drop-down-item>\n * ```\n */\n public override get selected(): boolean {\n if (this.hasIndex) {\n const item = this.selection.first_item(`${this.dropDown.id}`);\n return item ? item.index === this._index && item.value === this.value : false;\n }\n return this._selected;\n }\n\n /**\n * Sets/Gets if the item is the currently selected one in the dropdown\n *\n */\n public override set selected(value: boolean) {\n if (this.isHeader) {\n return;\n }\n this._selected = value;\n this.selectedChange.emit(this._selected);\n }\n /**\n * @hidden @internal\n */\n @HostBinding('attr.tabindex')\n public get setTabIndex() {\n const shouldSetTabIndex = this.dropDown.allowItemsFocus && this.isSelectable;\n if (shouldSetTabIndex) {\n return 0;\n } else {\n return null;\n }\n }\n\n public override clicked(event): void {\n if (!this.isSelectable) {\n this.ensureItemFocus();\n return;\n }\n if (this.selection) {\n this.dropDown.selectItem(this, event);\n }\n }\n}\n","<span class=\"igx-drop-down__content\">\n <ng-content select=\"igx-prefix, [igxPrefix]\"></ng-content>\n <span class=\"igx-drop-down__inner\">\n <ng-content></ng-content>\n </span>\n <ng-content select=\"igx-suffix, [igxSuffix]\"></ng-content>\n <ng-content select=\"igx-divider\"></ng-content>\n</span>\n","import { Directive, Input, HostListener, inject, HostBinding } from '@angular/core';\nimport { IGX_DROPDOWN_BASE } from './drop-down.common';\nimport { IDropDownNavigationDirective } from './drop-down.common';\nimport { IgxDropDownBaseDirective } from './drop-down.base';\nimport { DropDownActionKey } from './drop-down.common';\n\n/**\n * Navigation Directive that handles keyboard events on its host and controls a targeted IgxDropDownBaseDirective component\n */\n@Directive({\n selector: '[igxDropDownItemNavigation]',\n standalone: true\n})\nexport class IgxDropDownItemNavigationDirective implements IDropDownNavigationDirective {\n public dropdown = inject<IgxDropDownBaseDirective>(IGX_DROPDOWN_BASE, { self: true, optional: true });\n\n\n protected _target: IgxDropDownBaseDirective = null;\n\n /**\n * Gets the target of the navigation directive;\n *\n * ```typescript\n * // Get\n * export class MyComponent {\n * ...\n * @ContentChild(IgxDropDownNavigationDirective)\n * navDirective: IgxDropDownNavigationDirective = null\n * ...\n * const navTarget: IgxDropDownBaseDirective = navDirective.navTarget\n * }\n * ```\n */\n public get target(): IgxDropDownBaseDirective {\n return this._target;\n }\n\n /**\n * Sets the target of the navigation directive;\n * If no valid target is passed, it falls back to the drop down context\n *\n * ```html\n * <!-- Set -->\n * <input [igxDropDownItemNavigation]=\"dropdown\" />\n * ...\n * <igx-drop-down #dropdown>\n * ...\n * </igx-drop-down>\n * ```\n */\n @Input('igxDropDownItemNavigation')\n public set target(target: IgxDropDownBaseDirective) {\n this._target = target ? target : this.dropdown;\n }\n\n @HostBinding('attr.aria-activedescendant')\n public get activeDescendant(): string {\n return this._target?.activeDescendant;\n }\n\n /**\n * Captures keydown events and calls the appropriate handlers on the target component\n */\n @HostListener('keydown', ['$event'])\n public handleKeyDown(event: KeyboardEvent) {\n if (event) {\n const key = event.key.toLowerCase();\n if (!this.target.collapsed) { // If dropdown is opened\n const navKeys = ['esc', 'escape', 'enter', 'space', 'spacebar', ' ',\n 'arrowup', 'up', 'arrowdown', 'down', 'home', 'end', 'tab'];\n if (navKeys.indexOf(key) === -1) { // If key has appropriate function in DD\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n } else { // If dropdown is closed, do nothing\n return;\n }\n switch (key) {\n case 'esc':\n case 'escape':\n this.target.onItemActionKey(DropDownActionKey.ESCAPE, event);\n break;\n case 'enter':\n this.target.onItemActionKey(DropDownActionKey.ENTER, event);\n break;\n case 'space':\n case 'spacebar':\n case ' ':\n this.target.onItemActionKey(DropDownActionKey.SPACE, event);\n break;\n case 'arrowup':\n case 'up':\n this.onArrowUpKeyDown();\n break;\n case 'arrowdown':\n case 'down':\n this.onArrowDownKeyDown();\n break;\n case 'home':\n this.onHomeKeyDown();\n break;\n case 'end':\n this.onEndKeyDown();\n break;\n case 'tab':\n this.target.onItemActionKey(DropDownActionKey.TAB, event);\n break;\n default:\n return;\n }\n }\n }\n\n /**\n * Navigates to previous item\n */\n public onArrowDownKeyDown() {\n this.target.navigateNext();\n }\n\n /**\n * Navigates to previous item\n */\n public onArrowUpKeyDown() {\n this.target.navigatePrev();\n }\n\n /**\n * Navigates to target's last item\n */\n public onEndKeyDown() {\n this.target.navigateLast();\n }\n\n /**\n * Navigates to target's first item\n */\n public onHomeKeyDown() {\n this.target.navigateFirst();\n }\n}\n","import {\n Input, HostBinding, ElementRef, QueryList, Output, EventEmitter, ChangeDetectorRef, Directive,\n OnInit,\n DOCUMENT,\n inject\n} from '@angular/core';\n\nimport { Navigate, ISelectionEventArgs } from './drop-down.common';\nimport { IDropDownList } from './drop-down.common';\nimport { DropDownActionKey } from './drop-down.common';\nimport { IgxDropDownItemBaseDirective } from './drop-down-item.base';\n\nlet NEXT_ID = 0;\n\n/**\n * An abstract class, defining a drop-down component, with:\n * Properties for display styles and classes\n * A collection items of type `IgxDropDownItemBaseDirective`\n * Properties and methods for navigating (highlighting/focusing) items from the collection\n * Properties and methods for selecting items from the collection\n */\n@Directive()\nexport abstract class IgxDropDownBaseDirective implements IDropDownList, OnInit {\n protected elementRef = inject(ElementRef);\n protected cdr = inject(ChangeDetectorRef);\n public document = inject(DOCUMENT);\n \n /**\n * Emitted when item selection is changing, before the selection completes\n *\n * ```html\n * <igx-drop-down (selectionChanging)='handleSelection()'></igx-drop-down>\n * ```\n */\n @Output()\n public selectionChanging = new EventEmitter<ISelectionEventArgs>();\n\n /**\n * Gets/Sets the width of the drop down\n *\n * ```typescript\n * // get\n * let myDropDownCurrentWidth = this.dropdown.width;\n * ```\n * ```html\n * <!--set-->\n * <igx-drop-down [width]='160px'></igx-drop-down>\n * ```\n */\n @Input()\n public width: string;\n\n /**\n * Gets/Sets the height of the drop down\n *\n * ```typescript\n * // get\n * let myDropDownCurrentHeight = this.dropdown.height;\n * ```\n * ```html\n * <!--set-->\n * <igx-drop-down [height]='400px'></igx-drop-down>\n * ```\n */\n @Input()\n public height: string;\n\n /**\n * Gets/Sets the drop down's id\n *\n * ```typescript\n * // get\n * let myDropDownCurrentId = this.dropdown.id;\n * ```\n * ```html\n * <!--set-->\n * <igx-drop-down [id]='newDropDownId'></igx-drop-down>\n * ```\n */\n @HostBinding('attr.id')\n @Input()\n public get id(): string {\n return this._id;\n }\n public set id(value: string) {\n this._id = value;\n }\n\n /**\n * Gets/Sets the drop down's container max height.\n *\n * ```typescript\n * // get\n * let maxHeight = this.dropdown.maxHeight;\n * ```\n * ```html\n * <!--set-->\n * <igx-drop-down [maxHeight]='200px'></igx-drop-down>\n * ```\n */\n @Input()\n @HostBinding('style.maxHeight')\n public maxHeight = null;\n\n /**\n * @hidden @internal\n */\n @HostBinding('class.igx-drop-down')\n public cssClass = true;\n\n /**\n * Get all non-header items\n *\n * ```typescript\n * let myDropDownItems = this.dropdown.items;\n * ```\n */\n public get items(): IgxDropDownItemBaseDirective[] {\n const items: IgxDropDownItemBaseDirective[] = [];\n if (this.children !== undefined) {\n for (const child of this.children.toArray()) {\n if (!child.isHeader) {\n items.push(child);\n }\n }\n }\n\n return items;\n }\n\n /**\n * Get all header items\n *\n * ```typescript\n * let myDropDownHeaderItems = this.dropdown.headers;\n * ```\n */\n public get headers(): IgxDropDownItemBaseDirective[] {\n const headers: IgxDropDownItemBaseDirective[] = [];\n if (this.children !== undefined) {\n for (const child of this.children.toArray()) {\n if (child.isHeader) {\n headers.push(child);\n }\n }\n }\n\n return headers;\n }\n\n /**\n * Get dropdown html element\n *\n * ```typescript\n * let myDropDownElement = this.dropdown.element;\n * ```\n */\n public get element() {\n return this.elementRef.nativeElement;\n }\n /**\n * @hidden @internal\n * Get dropdown's html element of its scroll container\n */\n public get scrollContainer(): HTMLElement {\n return this.element;\n }\n\n /**\n * @hidden @internal\n * Gets the id of the focused item during dropdown navigation.\n * This is used to update the `aria-activedescendant` attribute of\n * the IgxDropDownNavigationDirective host element.\n */\n public get activeDescendant (): string {\n return this.focusedItem ? this.focusedItem.id : null;\n }\n\n /**\n * @hidden\n * @internal\n */\n public children: QueryList<IgxDropDownItemBaseDirective>;\n\n protected _width;\n protected _height;\n protected _focusedItem: any = null;\n protected _id = `igx-drop-down-${NEXT_ID++}`;\n protected computedStyles;\n\n /**\n * Gets if the dropdown is collapsed\n */\n public abstract readonly collapsed: boolean;\n\n public ngOnInit(): void {\n this.computedStyles = this.document.defaultView.getComputedStyle(this.elementRef.nativeElement);\n }\n\n /** Keydown Handler */\n public onItemActionKey(key: DropDownActionKey, event?: Event) {\n switch (key) {\n case DropDownActionKey.ENTER:\n case DropDownActionKey.SPACE:\n this.selectItem(this.focusedItem, event);\n break;\n case DropDownActionKey.ESCAPE:\n case DropDownActionKey.TAB:\n }\n }\n\n /**\n * Emits selectionChanging with the target item & event\n *\n * @hidden @internal\n * @param newSelection the item selected\n * @param event the event that triggered the call\n */\n public selectItem(newSelection?: IgxDropDownItemBaseDirective, event?: Event, emit = true) { // eslint-disable-line\n this.selectionChanging.emit({\n newSelection,\n oldSelection: null,\n cancel: false\n });\n }\n\n /**\n * @hidden @internal\n */\n public get focusedItem(): IgxDropDownItemBaseDirective {\n return this._focusedItem;\n }\n\n /**\n * @hidden @internal\n */\n public set focusedItem(item: IgxDropDownItemBaseDirective) {\n this._focusedItem = item;\n }\n\n /**\n * Navigates to the item on the specified index\n *\n * @param newIndex number - the index of the item in the `items` collection\n */\n public navigateItem(newIndex: number) {\n if (newIndex !== -1) {\n const oldItem = this._focusedItem;\n const newItem = this.items[newIndex];\n if (oldItem) {\n oldItem.focused = false;\n }\n this.focusedItem = newItem;\n this.scrollToHiddenItem(newItem);\n this.focusedItem.focused = true;\n }\n }\n\n /**\n * @hidden @internal\n */\n public navigateFirst() {\n this.navigate(Navigate.Down, -1);\n }\n\n /**\n * @hidden @internal\n */\n public navigateLast() {\n this.navigate(Navigate.Up, this.items.length);\n }\n\n /**\n * @hidden @internal\n */\n public navigateNext() {\n this.navigate(Navigate.Down);\n }\n\n /**\n * @hidden @internal\n */\n public navigatePrev() {\n this.navigate(Navigate.Up);\n }\n\n protected scrollToHiddenItem(newItem: IgxDropDownItemBaseDirective) {\n const elementRect = newItem.element.nativeElement.getBoundingClientRect();\n const parentRect = this.scrollContainer.getBoundingClientRect();\n if (parentRect.top > elementRect.top) {\n this.scrollContainer.scrollTop -= (parentRect.top - elementRect.top);\n }\n\n if (parentRect.bottom < elementRect.bottom) {\n this.scrollContainer.scrollTop += (elementRect.bottom - parentRect.bottom);\n }\n }\n\n protected navigate(direction: Navigate, currentIndex?: number) {\n let index = -1;\n if (this._focusedItem) {\n index = currentIndex ? currentIndex : this.focusedItem.itemIndex;\n }\n const newIndex = this.getNearestSiblingFocusableItemIndex(index, direction);\n this.navigateItem(newIndex);\n }\n\n protected getNearestSiblingFocusableItemIndex(startIndex: number, direction: Navigate): number {\n let index = startIndex;\n const items = this.items;\n while (items[index + direction] && items[index + direction].disabled) {\n index += direction;\n }\n\n index += direction;\n if (index >= 0 && index < items.length) {\n return index;\n } else {\n return -1;\n }\n }\n}\n","import {\n Component,\n ContentChildren,\n ElementRef,\n forwardRef,\n QueryList,\n OnChanges,\n Input,\n OnDestroy,\n ViewChild,\n ContentChild,\n AfterViewInit,\n Output,\n EventEmitter,\n SimpleChanges,\n booleanAttribute,\n inject} from '@angular/core';\nimport { IgxToggleDirective, ToggleViewEventArgs } from 'igniteui-angular/directives';\nimport { IgxDropDownItemComponent } from './drop-down-item.component';\nimport { IgxDropDownBaseDirective } from './drop-down.base';\nimport { DropDownActionKey, Navigate } from './drop-down.common';\nimport { IGX_DROPDOWN_BASE, IDropDownBase } from './drop-down.common';\nimport { ISelectionEventArgs } from './drop-down.common';\nimport { IBaseCancelableBrowserEventArgs, IBaseEventArgs } from 'igniteui-angular/core';\nimport { IgxSelectionAPIService } from 'igniteui-angular/core';\nimport { Subject } from 'rxjs';\nimport { IgxDropDownItemBaseDirective } from './drop-down-item.base';\nimport { IgxForOfToken } from 'igniteui-angular/directives';\nimport { take } from 'rxjs/operators';\nimport { OverlaySettings } from 'igniteui-angular/core';\nimport { ConnectedPositioningStrategy } from 'igniteui-angular/core';\n\n/**\n * **Ignite UI for Angular DropDown** -\n * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/drop-down)\n *\n * The Ignite UI for Angular Drop Down displays a scrollable list of items which may be visually grouped and\n * supports selection of a single item. Clicking or tapping an item selects it and closes the Drop Down\n *\n * Example:\n * ```html\n * <igx-drop-down>\n * <igx-drop-down-item *ngFor=\"let item of items\" disabled={{item.disabled}} isHeader={{item.header}}>\n * {{ item.value }}\n * </igx-drop-down-item>\n * </igx-drop-down>\n * ```\n */\n\n@Component({\n selector: 'igx-drop-down',\n templateUrl: './drop-down.component.html',\n providers: [{ provide: IGX_DROPDOWN_BASE, useExisting: IgxDropDownComponent }],\n imports: [IgxToggleDirective]\n})\nexport class IgxDropDownComponent extends IgxDropDownBaseDirective implements IDropDownBase, OnChanges, AfterViewInit, OnDestroy {\n protected selection = inject(IgxSelectionAPIService);\n\n /**\n * @hidden\n * @internal\n */\n @ContentChildren(forwardRef(() => IgxDropDownItemComponent), { descendants: true })\n public override children: QueryList<IgxDropDownItemBaseDirective>;\n\n /**\n * Emitted before the dropdown is opened\n *\n * ```html\n * <igx-drop-down (opening)='handleOpening($event)'></igx-drop-down>\n * ```\n */\n @Output()\n public opening = new EventEmitter<IBaseCancelableBrowserEventArgs>();\n\n /**\n * Emitted after the dropdown is opened\n *\n * ```html\n * <igx-drop-down (opened)='handleOpened($event)'></igx-drop-down>\n * ```\n */\n @Output()\n public opened = new EventEmitter<IBaseEventArgs>();\n\n /**\n * Emitted before the dropdown is closed\n *\n * ```html\n * <igx-drop-down (closing)='handleClosing($event)'></igx-drop-down>\n * ```\n */\n @Output()\n public closing = new EventEmitter<IBaseCancelableBrowserEventArgs>();\n\n /**\n * Emitted after the dropdown is closed\n *\n * ```html\n * <igx-drop-down (closed)='handleClosed($event)'></igx-drop-down>\n * ```\n */\n @Output()\n public closed = new EventEmitter<IBaseEventArgs>();\n\n /**\n * Gets/sets whether items take focus. Disabled by default.\n * When enabled, drop down items gain tab index and are focused when active -\n * this includes activating the selected item when opening the drop down and moving with keyboard navigation.\n *\n * Note: Keep that focus shift in mind when using the igxDropDownItemNavigation directive\n * and ensure it's placed either on each focusable item or a common ancestor to allow it to handle keyboard events.\n *\n * ```typescript\n * // get\n * let dropDownAllowsItemFocus = this.dropdown.allowItemsFocus;\n * ```\n *\n * ```html\n * <!--set-->\n * <igx-drop-down [allowItemsFocus]='true'></igx-drop-down>\n * ```\n */\n @Input({ transform: booleanAttribute })\n public allowItemsFocus = false;\n\n /**\n * Sets aria-labelledby attribute value.\n * ```html\n * <igx-drop-down [labelledby]=\"labelId\"></igx-drop-down>\n * ```\n */\n @Input()\n public labelledBy: string;\n\n /**\n * Gets/sets the `role` attribute of the drop down. Default is 'listbox'.\n *\n * ```html\n * <igx-drop-down [role]=\"customRole\"></igx-drop-down-item>\n * ```\n */\n @Input()\n public role = 'listbox';\n\n @ContentChild(IgxForOfToken)\n protected virtDir: IgxForOfToken<any>;\n\n @ViewChild(IgxToggleDirective, { static: true })\n protected toggleDirective: IgxToggleDirective;\n\n @ViewChild('scrollContainer', { static: true })\n protected scrollContainerRef: ElementRef;\n\n /**\n * @hidden @internal\n */\n public override get focusedItem(): IgxDropDownItemBaseDirective {\n if (this.virtDir) {\n return this._focusedItem && this._focusedItem.index !== -1 ?\n (this.children.find(e => e.index === this._focusedItem.index) || null) :\n null;\n }\n return this._focusedItem;\n }\n\n public override set focusedItem(value: IgxDropDownItemBaseDirective) {\n if (!value) {\n this.selection.clear(`${this.id}-active`);\n this._focusedItem = null;\n return;\n }\n this._focusedItem = value;\n if (this.virtDir) {\n this._focusedItem = {\n value: value.value,\n index: value.index\n } as IgxDropDownItemBaseDirective;\n }\n this.selection.set(`${this.id}-active`, new Set([this._focusedItem]));\n }\n\n public override get id(): string {\n return this._id;\n }\n public override set id(value: string) {\n this.selection.set(value, this.selection.get(this.id));\n this.selection.clear(this.id);\n this.selection.set(value, this.selection.get(`${this.id}-active`));\n this.selection.clear(`${this.id}-active`);\n this._id = value;\n }\n\n /** Id of the internal listbox of the drop down */\n public get listId() {\n return this.id + '-list';\n }\n\n /**\n * Get currently selected item\n *\n * ```typescript\n * let currentItem = this.dropdown.selectedItem;\n * ```\n */\n public get selectedItem(): IgxDropDownItemBaseDirective {\n const selectedItem = this.selection.first_item(this.id);\n if (selectedItem) {\n return selectedItem;\n }\n return null;\n }\n\n /**\n * Gets if the dropdown is collapsed\n *\n * ```typescript\n * let isCollapsed = this.dropdown.collapsed;\n * ```\n */\n public get collapsed(): boolean {\n return this.toggleDirective.collapsed;\n }\n\n /** @hidden @internal */\n public override get scrollContainer(): HTMLElement {\n return this.scrollContainerRef.nativeElement;\n }\n\n protected get collectionLength() {\n if (this.virtDir) {\n return this.virtDir.totalItemCount || this.virtDir.igxForOf.length;\n }\n }\n\n protected destroy$ = new Subject<boolean>();\n protected _scrollPosition: number;\n\n /**\n * Opens the dropdown\n *\n * ```typescript\n * this.dropdown.open();\n * ```\n */\n public open(overlaySettings?: OverlaySettings) {\n const settings = { ... {}, ...this.getDefaultOverlaySettings(), ...overlaySettings };\n this.toggleDirective.open(settings);\n this.updateScrollPosition();\n }\n\n /**\n * @hidden @internal\n */\n public getDefaultOverlaySettings(): OverlaySettings {\n return {\n closeOnOutsideClick: true,\n modal: false,\n positionStrategy: new ConnectedPositioningStrategy()\n };\n }\n\n /**\n * Closes the dropdown\n *\n * ```typescript\n * this.dropdown.close();\n * ```\n */\n public close(event?: Event) {\n this.toggleDirective.close(event);\n }\n\n /**\n * Toggles the dropdown\n *\n * ```typescript\n * this.dropdown.toggle();\n * ```\n */\n public toggle(overlaySettings?: OverlaySettings) {\n if (this.collapsed || this.toggleDirective.isClosing) {\n this.open(overlaySettings);\n } else {\n this.close();\n }\n }\n\n /**\n * Select an item by index\n *\n * @param index of the item to select; If the drop down uses *igxFor, pass the index in data\n */\n public setSelectedItem(index: number) {\n if (index < 0 || index >= this.items.length) {\n return;\n }\n let newSelection: IgxDropDownItemBaseDirective;\n if (this.virtDir) {\n newSelection = {\n value: this.virtDir.igxForOf[index],\n index\n } as IgxDropDownItemBaseDirective;\n } else {\n newSelection = this.items[index];\n }\n this.selectItem(newSelection);\n }\n\n /**\n * Navigates to the item on the specified index\n * If the data in the drop-down is virtualized, pass the index of the item in the virtualized data.\n *\n * @param newIndex number\n */\n public override navigateItem(index: number) {\n if (this.virtDir) {\n if (index === -1 || index >= this.collectionLength) {\n return;\n }\n const direction = index > (this.focusedItem ? this.focusedItem.index : -1) ? Navigate.Down : Navigate.Up;\n const subRequired = this.isIndexOutOfBounds(index, direction);\n this.focusedItem = {\n value: this.virtDir.igxForOf[index],\n index\n } as IgxDropDownItemBaseDirective;\n if (subRequired) {\n this.virtDir.scrollTo(index);\n }\n if (subRequired) {\n this.virtDir.chunkLoad.pipe(take(1)).subscribe(() => {\n this.skipHeader(direction);\n });\n } else {\n this.skipHeader(direction);\n }\n } else {\n super.navigateItem(index);\n }\n if (this.allowItemsFocus && this.focusedItem) {\n this.focusedItem.element.nativeElement.focus();\n this.cdr.markForCheck();\n }\n }\n\n /**\n * @hidden @internal\n */\n public updateScrollPosition() {\n if (!this.virtDir) {\n return;\n }\n if (!this.selectedItem) {\n this.virtDir.scrollTo(0);\n return;\n }\n let targetScroll = this.virtDir.getScrollForIndex(this.selectedItem.index);\n // TODO: This logic _cannot_ be right, those are optional user-provided inputs that can be strings with units, refactor:\n const itemsInView = this.virtDir.igxForContainerSize / this.virtDir.igxForItemSize;\n targetScroll -= (itemsInView / 2 - 1) * this.virtDir.igxForItemSize;\n this.virtDir.getScroll().scrollTop = targetScroll;\n }\n\n /**\n * @hidden @internal\n */\n public onToggleOpening(e: IBaseCancelableBrowserEventArgs) {\n const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: false };\n this.opening.emit(args);\n e.cancel = args.cancel;\n if (e.cancel) {\n return;\n }\n\n if (this.virtDir) {\n this.virtDir.scrollPosition = this._scrollPosition;\n }\n }\n\n /**\n * @hidden @internal\n */\n public onToggleContentAppended(_event: ToggleViewEventArgs) {\n if (!this.virtDir && this.selectedItem) {\n this.scrollToItem(this.selectedItem);\n }\n }\n\n /**\n * @hidden @internal\n */\n public onToggleOpened() {\n this.updateItemFocus();\n this.opened.emit({ owner: this });\n }\n\n /**\n * @hidden @internal\n */\n public onToggleClosing(e: IBaseCancelableBrowserEventArgs) {\n const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: false };\n this.closing.emit(args);\n e.cancel = args.cancel;\n if (e.cancel) {\n return;\n }\n if (this.virtDir) {\n this._scrollPosition = this.virtDir.scrollPosition;\n }\n }\n\n /**\n * @hidden @internal\n */\n public onToggleClosed() {\n this.focusItem(false);\n this.closed.emit({ owner: this });\n }\n\n /**\n * @hidden @internal\n */\n public ngOnDestroy() {\n this.destroy$.next(true);\n this.destroy$.complete();\n this.selection.delete(this.id);\n this.selection.delete(`${this.id}-active`);\n }\n\n /** @hidden @internal */\n public calculateScrollPosition(item: IgxDropDownItemBaseDirective): number {\n if (!item) {\n return 0;\n }\n\n const elementRect = item.element.nativeElement.getBoundingClientRect();\n const parentRect = this.scrollContainer.getBoundingClientRect();\n const scrollDelta = parentRect.top - elementRect.top;\n let scrollPosition = this.scrollContainer.scrollTop - scrollDelta;\n\n const dropDownHeight = this.scrollContainer.clientHeight;\n scrollPosition -= dropDownHeight / 2;\n scrollPosition += item.elementHeight / 2;\n\n return Math.floor(scrollPosition);\n }\n\n /**\n * @hidden @internal\n */\n public ngOnChanges(changes: SimpleChanges) {\n if (changes.id) {\n // temp workaround until fix --> https://github.com/angular/angular/issues/34992\n this.toggleDirective.id = changes.id.currentValue;\n }\n }\n\n public ngAfterViewInit() {\n if (this.virtDir) {\n this.virtDir.igxForItemSize = 28;\n }\n }\n\n /** Keydown Handler */\n public override onItemActionKey(key: DropDownActionKey, event?: Event) {\n super.onItemActionKey(key, event);\n this.close(event);\n }\n\n /**\n * Virtual scroll implementation\n *\n * @hidden @internal\n */\n public override navigateFirst() {\n if (this.virtDir) {\n this.navigateItem(0);\n } else {\n super.navigateFirst();\n }\n }\n\n /**\n * @hidden @internal\n */\n public override navigateLast() {\n if (this.virtDir) {\n this.navigateItem(this.virtDir.totalItemCount ? this.virtDir.totalItemCount - 1 : this.virtDir.igxForOf.length - 1);\n } else {\n super.navigateLast();\n }\n }\n\n /**\n * @hidden @internal\n */\n public override navigateNext() {\n if (this.virtDir) {\n this.navigateItem(this._focusedItem ? this._focusedItem.index + 1 : 0);\n } else {\n super.navigateNext();\n }\n }\n\n /**\n * @hidden @internal\n */\n public override navigatePrev() {\n if (this.virtDir) {\n this.navigateItem(this._focusedItem ? this._focusedItem.index - 1 : 0);\n } else {\n super.navigatePrev();\n }\n }\n\n /**\n * Handles the `selectionChanging` emit and the drop down toggle when selection changes\n *\n * @hidden\n * @internal\n * @param newSelection\n * @param emit\n * @param event\n */\n public override selectItem(newSelection?: IgxDropDownItemBaseDirective, event?: Event, emit = true) {\n const oldSelection = this.selectedItem;\n if (!newSelection) {\n newSelection = this.focusedItem;\n }\n if (newSelection === null) {\n return;\n }\n if (newSelection instanceof IgxDropDownItemBaseDirective && newSelection.isHeader) {\n return;\n }\n if (this.virtDir) {\n newSelection = {\n value: newSelection.value,\n index: newSelection.index\n } as IgxDropDownItemBaseDirective;\n }\n const args: ISelectionEventArgs = { oldSelection, newSelection, cancel: false, owner: this };\n\n if (emit) {\n this.selectionChanging.emit(args);\n }\n\n if (!args.cancel) {\n if (this.isSelectionValid(args.newSelection)) {\n this.selection.set(this.id, new Set([args.newSelection]));\n if (!this.virtDir) {\n if (oldSelection) {\n oldSelection.selected = false;\n }\n if (args.newSelection) {\n args.newSelection.selected = true;\n }\n }\n if (event) {\n this.toggleDirective.close(event);\n }\n } else {\n throw new Error('Please provide a valid drop-down item for the selection!');\n }\n }\n }\n\n /**\n * Clears the selection of the dropdown\n * ```typescript\n * this.dropdown.clearSelection();\n * ```\n */\n public clearSelection() {\n const oldSelection = this.selectedItem;\n const newSelection: IgxDropDownItemBaseDirective = null;\n const args: ISelectionEventArgs = { oldSelection, newSelection, cancel: false, owner: this };\n this.selectionChanging.emit(args);\n if (this.selectedItem && !args.cancel) {\