primeng
Version:
PrimeNG is a premium UI library for Angular featuring a rich set of 90+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeBlock,
1 lines • 137 kB
Source Map (JSON)
{"version":3,"file":"primeng-listbox.mjs","sources":["../../src/listbox/style/listboxstyle.ts","../../src/listbox/listbox.ts","../../src/listbox/primeng-listbox.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { style as listbox_style } from '@primeuix/styles/listbox';\nimport { BaseStyle } from 'primeng/base';\n\nconst style = /*css*/ `\n ${listbox_style}\n\n /* For PrimeNG */\n .p-listbox.ng-invalid.ng-dirty {\n border-color: dt('listbox.invalid.border.color');\n }\n\n .p-listbox-header {\n display: flex;\n align-items: center;\n }\n\n .p-listbox-header > .p-iconfield {\n flex-grow: 1;\n }\n\n .p-listbox-list-container {\n height: 100%;\n }\n\n /* scrollHeight=\"flex\": the root fills its flex-constrained parent and lays out\n as a column so the header/filter keep their natural height and the scrollable\n list shrinks to the remaining space instead of overflowing the container. */\n .p-listbox-flex-scrollable {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .p-listbox-flex-scrollable .p-listbox-list-container {\n flex: 1;\n }\n\n /* CDK Drag & Drop styles */\n /* The single-item ghost and the rows inside the multi-selection preview both\n use the plain list background, so a multi-drag looks like several copies of\n the single-drag ghost rather than a block of highlighted (selected) rows. */\n .p-listbox-option.cdk-drag-preview,\n .p-listbox-drag-preview .p-listbox-option {\n background: dt('listbox.background');\n }\n\n /* Multi-selection drag preview: dragging an option that is part of a\n multi-selection previews the whole selection. The preview mirrors the\n listbox DOM so the theme styles the rows, but the container card chrome\n (background, border, shadow) is stripped so the floating copy reads as a\n bare silhouette of the dragged rows rather than a detached panel. */\n .p-listbox.p-listbox-drag-preview {\n max-width: 20rem;\n pointer-events: none;\n background: transparent;\n border: 0 none;\n box-shadow: none;\n }\n\n .p-listbox-drag-preview .p-listbox-list {\n padding: 0;\n }\n\n .p-listbox-drag-preview-more {\n opacity: 0.7;\n }\n\n .p-listbox-dragging .p-listbox-option:not(.cdk-drag-preview) {\n pointer-events: none !important;\n }\n\n .p-listbox-dragging .p-listbox-option:not(.cdk-drag-preview):hover {\n background: inherit !important;\n color: inherit !important;\n }\n\n .cdk-drag-placeholder {\n pointer-events: none;\n }\n`;\n\nconst classes = {\n root: ({ instance }) => [\n 'p-listbox p-component',\n {\n 'p-listbox-striped': instance.striped(),\n 'p-disabled': instance.$disabled(),\n 'p-invalid': instance.invalid(),\n 'p-listbox-fluid': instance.fluid(),\n 'p-listbox-dragging': instance.isDragging(),\n 'p-listbox-flex-scrollable': instance.scrollHeight() === 'flex'\n }\n ],\n header: 'p-listbox-header',\n pcFilter: 'p-listbox-filter',\n listContainer: 'p-listbox-list-container',\n list: 'p-listbox-list',\n optionGroup: 'p-listbox-option-group',\n option: ({ instance, option, i, scrollerOptions }) => [\n 'p-listbox-option',\n {\n 'p-listbox-option-selected': instance.isSelected(option) && instance.highlightOnSelect(),\n 'p-focus': instance.focusedOptionIndex() === instance.getOptionIndex(i, scrollerOptions),\n 'p-disabled': instance.isOptionDisabled(option)\n }\n ],\n optionCheckIcon: 'p-listbox-option-check-icon',\n optionBlankIcon: 'p-listbox-option-blank-icon',\n emptyMessage: 'p-listbox-empty-message',\n // Root + list + option classes mirror the listbox DOM so the theme styles the preview.\n // Options are intentionally NOT marked selected, so the preview rows match the plain\n // single-item drag ghost instead of the highlighted selected background.\n dragPreview: 'p-listbox p-component p-listbox-drag-preview',\n dragPreviewOption: 'p-listbox-option',\n dragPreviewMore: 'p-listbox-option p-listbox-drag-preview-more'\n};\n\n@Injectable()\nexport class ListBoxStyle extends BaseStyle {\n name = 'listbox';\n\n style = style;\n\n classes = classes;\n}\n\n/**\n *\n * ListBox is used to select one or more values from a list of items.\n *\n * [Live Demo](https://www.primeng.org/listbox/)\n *\n * @module listboxstyle\n *\n */\nexport enum ListboxClasses {\n /**\n * Class name of the root element\n */\n root = 'p-listbox',\n /**\n * Class name of the header element\n */\n header = 'p-listbox-header',\n /**\n * Class name of the filter element\n */\n pcFilter = 'p-listbox-filter',\n /**\n * Class name of the list container element\n */\n listContainer = 'p-listbox-list-container',\n /**\n * Class name of the list element\n */\n list = 'p-listbox-list',\n /**\n * Class name of the option group element\n */\n optionGroup = 'p-listbox-option-group',\n /**\n * Class name of the option element\n */\n option = 'p-listbox-option',\n /**\n * Class name of the option check icon element\n */\n optionCheckIcon = 'p-listbox-option-check-icon',\n /**\n * Class name of the option blank icon element\n */\n optionBlankIcon = 'p-listbox-option-blank-icon',\n /**\n * Class name of the empty message element\n */\n emptyMessage = 'p-listbox-empty-message',\n /**\n * Class name of the multi-selection drag preview container\n */\n dragPreview = 'p-listbox-drag-preview',\n /**\n * Class name of the overflow row in the multi-selection drag preview\n */\n dragPreviewMore = 'p-listbox-drag-preview-more'\n}\n\nexport interface ListboxStyle extends BaseStyle {}\n","import { CDK_DRAG_CONFIG, CdkDragDrop, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n effect,\n ElementRef,\n forwardRef,\n HostListener,\n inject,\n InjectionToken,\n input,\n NgModule,\n numberAttribute,\n output,\n signal,\n TemplateRef,\n viewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { Blank as BlankIcon } from '@primeicons/angular/blank';\nimport { Check as CheckIcon } from '@primeicons/angular/check';\nimport { Search as SearchIcon } from '@primeicons/angular/search';\nimport { equals, findLastIndex, findSingle, focus, getFirstFocusableElement, isEmpty, isFunction, isNotEmpty, isPrintableCharacter, resolveFieldData, uuid } from '@primeuix/utils';\nimport { FilterMatchModeType, FilterService, Footer, Header, ScrollerOptions, SharedModule } from 'primeng/api';\nimport { PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { BaseEditableHolder } from 'primeng/baseeditableholder';\nimport { Bind, BindModule } from 'primeng/bind';\nimport { Checkbox } from 'primeng/checkbox';\nimport { IconField } from 'primeng/iconfield';\nimport { InputIcon } from 'primeng/inputicon';\nimport { InputText } from 'primeng/inputtext';\nimport { Ripple } from 'primeng/ripple';\nimport { Scroller, ScrollerLazyLoadEvent } from 'primeng/scroller';\nimport type {\n ListboxChangeEvent,\n ListboxCheckIconTemplateContext,\n ListboxCheckmarkTemplateContext,\n ListboxClickEvent,\n ListboxDoubleClickEvent,\n ListboxFilterEvent,\n ListboxFilterOptions,\n ListboxFilterTemplateContext,\n ListboxFooterTemplateContext,\n ListboxGroupTemplateContext,\n ListboxHeaderTemplateContext,\n ListboxItemTemplateContext,\n ListboxLoaderTemplateContext,\n ListBoxPassThrough,\n ListboxSelectAllChangeEvent\n} from 'primeng/types/listbox';\nimport type { CSSProperties } from 'primeng/types/shared';\nimport { ListBoxStyle } from './style/listboxstyle';\n\nconst LISTBOX_INSTANCE = new InjectionToken<Listbox>('LISTBOX_INSTANCE');\n\nexport const LISTBOX_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => Listbox),\n multi: true\n};\n/**\n * ListBox is used to select one or more values from a list of items.\n * @group Components\n */\n@Component({\n selector: 'p-listbox, p-list-box',\n standalone: true,\n imports: [NgTemplateOutlet, Ripple, Scroller, InputIcon, SearchIcon, Checkbox, CheckIcon, IconField, InputText, BlankIcon, FormsModule, SharedModule, DragDropModule, BindModule],\n template: `\n <span\n #firstHiddenFocusableElement\n role=\"presentation\"\n class=\"p-hidden-accessible p-hidden-focusable\"\n [tabindex]=\"hiddenFocusableTabindex()\"\n (focus)=\"onFirstHiddenFocus($event)\"\n [attr.data-p-hidden-focusable]=\"true\"\n [pBind]=\"ptm('hiddenFirstFocusableElement')\"\n >\n </span>\n @if (hasHeader()) {\n <div [class]=\"cx('header')\" [pBind]=\"ptm('header')\">\n <ng-content select=\"p-header\"></ng-content>\n <ng-container *ngTemplateOutlet=\"headerTemplate(); context: headerTemplateContext()\"></ng-container>\n </div>\n }\n @if (hasCheckboxOrFilter()) {\n <div [class]=\"cx('header')\" [pBind]=\"ptm('header')\">\n @if (showCheckboxToggleAll()) {\n <p-checkbox\n #headerchkbox\n (onChange)=\"onToggleAll($event)\"\n [class]=\"cx('optionCheckIcon')\"\n [ngModel]=\"allSelected()\"\n [disabled]=\"$disabled()\"\n [tabindex]=\"-1\"\n [variant]=\"checkboxVariant()\"\n [binary]=\"true\"\n [attr.aria-label]=\"toggleAllAriaLabel()\"\n [pt]=\"ptm('pcCheckbox')\"\n [unstyled]=\"unstyled()\"\n >\n @if (checkIconTemplate()) {\n <ng-template #icon>\n <ng-container *ngTemplateOutlet=\"checkIconTemplate(); context: allSelectedContext()\"></ng-container>\n </ng-template>\n }\n </p-checkbox>\n }\n @if (filterTemplate()) {\n <ng-container *ngTemplateOutlet=\"filterTemplate(); context: getFilterTemplateContext()\"></ng-container>\n } @else {\n @if (filter()) {\n <p-iconfield [pt]=\"ptm('pcFilterContainer')\" hostName=\"listbox\" [unstyled]=\"unstyled()\">\n <input\n #filterInput\n pInputText\n type=\"text\"\n [class]=\"cx('pcFilter')\"\n role=\"searchbox\"\n [value]=\"filterInputValue()\"\n [attr.disabled]=\"disabledAttr()\"\n [attr.aria-owns]=\"listId()\"\n [attr.aria-activedescendant]=\"focusedOptionId()\"\n [attr.placeholder]=\"filterPlaceHolder()\"\n [attr.aria-label]=\"ariaFilterLabel()\"\n [attr.tabindex]=\"filterTabindex()\"\n (input)=\"onFilterChange($event)\"\n (keydown)=\"onFilterKeyDown($event)\"\n (blur)=\"onFilterBlur($event)\"\n [pt]=\"ptm('pcFilter')\"\n [unstyled]=\"unstyled()\"\n hostName=\"listbox\"\n />\n <p-inputicon [pt]=\"ptm('pcFilterIconContainer')\" [unstyled]=\"unstyled()\">\n @if (showDefaultFilterIcon()) {\n <svg data-p-icon=\"search\" [attr.aria-hidden]=\"true\" [pBind]=\"ptm('filterIcon')\" />\n } @else {\n <span [attr.aria-hidden]=\"true\">\n <ng-container *ngTemplateOutlet=\"filterIconTemplate()\"></ng-container>\n </span>\n }\n </p-inputicon>\n </p-iconfield>\n }\n <span role=\"status\" [pBind]=\"ptm('hiddenFilterResult')\" [attr.aria-live]=\"'polite'\" class=\"p-hidden-accessible\" [attr.data-p-hidden-accessible]=\"true\">\n {{ filterResultMessageText() }}\n </span>\n }\n </div>\n }\n <div\n #container\n [class]=\"cn(cx('listContainer'), listStyleClass())\"\n [style]=\"listContainerStyle()\"\n cdkDropList\n [cdkDropListData]=\"cdkDropData()\"\n (cdkDropListDropped)=\"drop($event)\"\n (cdkDropListEntered)=\"onDragEntered()\"\n (cdkDropListExited)=\"onDragExited()\"\n [pBind]=\"ptm('listContainer')\"\n >\n @if (showFilteredEmptyMessage()) {\n <div [class]=\"cx('emptyMessage')\" [pBind]=\"ptm('emptyMessage')\">\n @if (showDefaultFilterEmptyMessage()) {\n {{ emptyFilterMessageText() }}\n } @else {\n <ng-container *ngTemplateOutlet=\"emptyOrFilterTemplate()\"></ng-container>\n }\n </div>\n } @else if (showEmptyMessage()) {\n <div [class]=\"cx('emptyMessage')\" [pBind]=\"ptm('emptyMessage')\">\n @if (!emptyTemplate()) {\n {{ emptyMessage() }}\n } @else {\n <ng-container *ngTemplateOutlet=\"emptyTemplate()\"></ng-container>\n }\n </div>\n } @else {\n @if (virtualScroll()) {\n <p-scroller\n [pt]=\"ptm('virtualScroller')\"\n hostName=\"listbox\"\n #scroller\n [items]=\"visibleOptions()\"\n [style]=\"scrollerStyle()\"\n [itemSize]=\"virtualScrollItemSize()\"\n [autoSize]=\"true\"\n [lazy]=\"lazy()\"\n [options]=\"virtualScrollOptions()\"\n (onLazyLoad)=\"onLazyLoad.emit($event)\"\n [tabindex]=\"scrollerTabIndex\"\n >\n <ng-template #content let-items let-scrollerOptions=\"options\">\n <ng-container *ngTemplateOutlet=\"buildInItems; context: getBuildInItemsContext(items, scrollerOptions)\"></ng-container>\n </ng-template>\n @if (loaderTemplate()) {\n <ng-template #loader let-scrollerOptions=\"options\">\n <ng-container *ngTemplateOutlet=\"loaderTemplate(); context: getLoaderTemplateContext(scrollerOptions)\"></ng-container>\n </ng-template>\n }\n </p-scroller>\n } @else {\n <ng-container *ngTemplateOutlet=\"buildInItems; context: buildInItemsContext()\"></ng-container>\n }\n\n <ng-template #buildInItems let-items let-scrollerOptions=\"options\">\n <ul\n #list\n [id]=\"listId()\"\n [class]=\"cn(cx('list'), scrollerOptions.contentStyleClass)\"\n role=\"listbox\"\n [tabindex]=\"-1\"\n [attr.aria-multiselectable]=\"true\"\n [style]=\"scrollerOptions.contentStyle\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-disabled]=\"$disabled()\"\n (focus)=\"onListFocus($event)\"\n (blur)=\"onListBlur($event)\"\n (keydown)=\"onListKeyDown($event)\"\n [pBind]=\"ptm('list')\"\n >\n @for (option of items; track getOptionTrackKey(option, $index); let i = $index) {\n @if (isOptionGroup(option)) {\n <li\n [attr.id]=\"getOptionId(i, scrollerOptions)\"\n [class]=\"cx('optionGroup')\"\n [pBind]=\"getPTOptions(option.optionGroup, scrollerOptions, i, 'optionGroup')\"\n [style.height.px]=\"scrollerOptions.itemSize\"\n role=\"option\"\n cdkDrag\n [cdkDragData]=\"option\"\n [cdkDragDisabled]=\"dragdropDisabled()\"\n (cdkDragStarted)=\"isDragging.set(true)\"\n (cdkDragEnded)=\"isDragging.set(false)\"\n >\n @if (showDefaultGroupLabel()) {\n <span>{{ getOptionGroupLabel(option.optionGroup) }}</span>\n }\n <ng-container *ngTemplateOutlet=\"groupTemplate(); context: getGroupTemplateContext(option.optionGroup)\"></ng-container>\n </li>\n } @else {\n <li\n pRipple\n [class]=\"cx('option', { option, i, scrollerOptions })\"\n role=\"option\"\n [attr.id]=\"getOptionId(i, scrollerOptions)\"\n [style.height.px]=\"scrollerOptions.itemSize\"\n [attr.aria-label]=\"getOptionLabel(option)\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option)\"\n [attr.aria-setsize]=\"ariaSetSize()\"\n [attr.ariaPosInset]=\"getAriaPosInset(getOptionIndex(i, scrollerOptions))\"\n [attr.data-p-selected]=\"isSelected(option)\"\n [attr.data-p-focused]=\"isOptionFocused(i, scrollerOptions)\"\n [attr.data-p-disabled]=\"isOptionDisabled(option)\"\n [pBind]=\"getPTOptions(option, scrollerOptions, i, 'option')\"\n (click)=\"onOptionSelect($event, option, getOptionIndex(i, scrollerOptions))\"\n (dblclick)=\"onOptionDoubleClick($event, option)\"\n (mousedown)=\"onOptionMouseDown($event, getOptionIndex(i, scrollerOptions))\"\n (mouseenter)=\"onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))\"\n (touchend)=\"onOptionTouchEnd()\"\n cdkDrag\n [cdkDragData]=\"option\"\n [cdkDragDisabled]=\"dragdropDisabled()\"\n (cdkDragStarted)=\"isDragging.set(true)\"\n (cdkDragEnded)=\"isDragging.set(false)\"\n >\n @if (showOptionCheckbox()) {\n <p-checkbox\n [class]=\"cx('optionCheckIcon')\"\n [ngModel]=\"isSelected(option)\"\n [readonly]=\"true\"\n [disabled]=\"getOptionCheckboxDisabled(option)\"\n [tabindex]=\"-1\"\n [variant]=\"checkboxVariant()\"\n [binary]=\"true\"\n [pt]=\"ptm('pcCheckbox')\"\n hostName=\"listbox\"\n [unstyled]=\"unstyled()\"\n >\n @if (checkIconTemplate()) {\n <ng-template #icon>\n <ng-container *ngTemplateOutlet=\"checkIconTemplate(); context: getCheckIconContext(option)\"></ng-container>\n </ng-template>\n }\n </p-checkbox>\n }\n @if (checkmark()) {\n @if (showDefaultCheckmark()) {\n @if (!isSelected(option)) {\n <svg data-p-icon=\"blank\" [class]=\"cx('optionBlankIcon')\" [pBind]=\"ptm('optionBlankIcon')\" />\n } @else {\n <svg data-p-icon=\"check\" [class]=\"cx('optionCheckIcon')\" [pBind]=\"ptm('optionCheckIcon')\" />\n }\n }\n <ng-container *ngTemplateOutlet=\"checkmarkTemplate(); context: getCheckmarkContext(option)\"></ng-container>\n }\n @if (showDefaultItemLabel()) {\n <span>{{ getOptionLabel(option) }}</span>\n }\n <ng-container *ngTemplateOutlet=\"itemTemplate(); context: getItemTemplateContext(option, i, scrollerOptions)\"></ng-container>\n @if (isMultiDrag(option)) {\n <!-- Mirrors the listbox DOM (root > list > option) so the theme's\n scoped option styles apply to the body-appended CDK preview. -->\n <ng-template cdkDragPreview>\n <div [class]=\"cx('dragPreview')\" [pBind]=\"ptm('dragPreview')\">\n <ul [class]=\"cx('list')\">\n @for (dragOption of dragPreviewOptions().slice(0, dragPreviewLimit); track getOptionTrackKey(dragOption, $index)) {\n <li [class]=\"cx('dragPreviewOption')\" [pBind]=\"ptm('dragPreviewOption')\">\n @if (showDefaultItemLabel()) {\n <span>{{ getOptionLabel(dragOption) }}</span>\n }\n <ng-container *ngTemplateOutlet=\"itemTemplate(); context: getDragPreviewTemplateContext(dragOption)\"></ng-container>\n </li>\n }\n @if (dragPreviewOptions().length > dragPreviewLimit) {\n <li [class]=\"cx('dragPreviewMore')\" [pBind]=\"ptm('dragPreviewMore')\">+{{ dragPreviewOptions().length - dragPreviewLimit }}</li>\n }\n </ul>\n </div>\n </ng-template>\n }\n </li>\n }\n }\n </ul>\n </ng-template>\n }\n </div>\n @if (hasFooter()) {\n <ng-content select=\"p-footer\"></ng-content>\n <ng-container *ngTemplateOutlet=\"footerTemplate(); context: footerTemplateContext()\"></ng-container>\n }\n @if (isEmpty()) {\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" [pBind]=\"ptm('hiddenEmptyMessage')\">\n {{ emptyMessage() }}\n </span>\n }\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" [pBind]=\"ptm('hiddenSelectedMessage')\">\n {{ selectedMessageText() }}\n </span>\n <span\n #lastHiddenFocusableElement\n role=\"presentation\"\n class=\"p-hidden-accessible p-hidden-focusable\"\n [tabindex]=\"hiddenFocusableTabindex()\"\n (focus)=\"onLastHiddenFocus($event)\"\n [attr.data-p-hidden-focusable]=\"true\"\n [pBind]=\"ptm('hiddenLastFocusableEl')\"\n >\n </span>\n `,\n providers: [\n LISTBOX_VALUE_ACCESSOR,\n ListBoxStyle,\n {\n provide: CDK_DRAG_CONFIG,\n useValue: {\n zIndex: 1200\n }\n },\n { provide: LISTBOX_INSTANCE, useExisting: Listbox },\n { provide: PARENT_INSTANCE, useExisting: Listbox }\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[attr.id]': '$id()',\n '[class]': \"cx('root')\",\n '[attr.data-p]': 'containerDataP'\n },\n hostDirectives: [Bind]\n})\nexport class Listbox extends BaseEditableHolder<ListBoxPassThrough> {\n componentName = 'Listbox';\n\n hostName = input<string>('');\n\n bindDirectiveInstance = inject(Bind, { self: true });\n\n $pcListbox: Listbox | undefined = inject(LISTBOX_INSTANCE, { optional: true, skipSelf: true }) ?? undefined;\n\n filterService = inject(FilterService);\n\n onAfterViewChecked() {\n this.bindDirectiveInstance.setAttrs(this.ptms(['host', 'root']));\n }\n\n /**\n * Unique identifier of the component.\n * @group Props\n */\n id = input<string>();\n /**\n * Text to display when the search is active. Defaults to global value in i18n translation configuration.\n * @group Props\n * @defaultValue '{0} results are available'\n */\n searchMessage = input<string>();\n /**\n * Text to display when filtering does not return any results. Defaults to global value in i18n translation configuration.\n * @group Props\n * @defaultValue 'No selected item'\n */\n emptySelectionMessage = input<string>();\n /**\n * Text to be displayed in hidden accessible field when options are selected. Defaults to global value in i18n translation configuration.\n * @group Props\n * @defaultValue '{0} items selected'\n */\n selectionMessage = input<string>();\n /**\n * Whether to focus on the first visible or selected element when the overlay panel is shown.\n * @group Props\n */\n autoOptionFocus = input(true, { transform: booleanAttribute });\n /**\n * Defines a string that labels the input for accessibility.\n * @group Props\n */\n ariaLabel = input<string>();\n /**\n * When enabled, the focused option is selected.\n * @group Props\n */\n selectOnFocus = input(undefined, { transform: booleanAttribute });\n /**\n * Locale to use in searching. The default locale is the host environment's current locale.\n * @group Props\n */\n searchLocale = input(undefined, { transform: booleanAttribute });\n /**\n * When enabled, the hovered option will be focused.\n * @group Props\n */\n focusOnHover = input(true, { transform: booleanAttribute });\n /**\n * Text to display when filtering.\n * @group Props\n */\n filterMessage = input<string>();\n /**\n * Fields used when filtering the options, defaults to optionLabel.\n * @group Props\n */\n filterFields = input<any[]>();\n /**\n * Defines if data is loaded and interacted with in lazy manner.\n * @group Props\n */\n lazy = input(false, { transform: booleanAttribute });\n /**\n * Whether the data should be loaded on demand during scroll.\n * @group Props\n */\n virtualScroll = input(undefined, { transform: booleanAttribute });\n /**\n * Height of an item in the list for VirtualScrolling.\n * @group Props\n */\n virtualScrollItemSize = input(undefined, { transform: numberAttribute });\n /**\n * Whether to use the scroller feature. The properties of scroller component can be used like an object in it.\n * @group Props\n */\n virtualScrollOptions = input<ScrollerOptions>();\n /**\n * Height of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.\n * @group Props\n */\n scrollHeight = input<string>('14rem');\n /**\n * Index of the element in tabbing order.\n * @group Props\n */\n tabindex = input(0, { transform: numberAttribute });\n /**\n * When specified, allows selecting multiple values.\n * @group Props\n */\n multiple = input(undefined, { transform: booleanAttribute });\n /**\n * Inline style of the list element.\n * @group Props\n */\n listStyle = input<CSSProperties>();\n /**\n * Style class of the list element.\n * @group Props\n */\n listStyleClass = input<string>();\n /**\n * When present, it specifies that the element value cannot be changed.\n * @group Props\n */\n readonly = input(undefined, { transform: booleanAttribute });\n /**\n * When specified, allows selecting items with checkboxes.\n * @group Props\n */\n checkbox = input(false, { transform: booleanAttribute });\n /**\n * When specified, displays a filter input at header.\n * @group Props\n */\n filter = input(false, { transform: booleanAttribute });\n /**\n * When filtering is enabled, filterBy decides which field or fields (comma separated) to search against.\n * @group Props\n */\n filterBy = input<string>();\n /**\n * Defines how the items are filtered.\n * @group Props\n */\n filterMatchMode = input<FilterMatchModeType>('contains');\n /**\n * Locale to use in filtering. The default locale is the host environment's current locale.\n * @group Props\n */\n filterLocale = input<string>();\n /**\n * Defines how multiple items can be selected, when true metaKey needs to be pressed to select or unselect an item and when set to false selection of each item can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically.\n * @group Props\n */\n metaKeySelection = input(false, { transform: booleanAttribute });\n /**\n * A property to uniquely identify a value in options.\n * @group Props\n */\n dataKey = input<string>();\n /**\n * Whether header checkbox is shown in multiple mode.\n * @group Props\n */\n showToggleAll = input(true, { transform: booleanAttribute });\n /**\n * Name of the label field of an option.\n * @group Props\n */\n optionLabel = input<string>();\n /**\n * Name of the value field of an option.\n * @group Props\n */\n optionValue = input<string>();\n /**\n * Name of the options field of an option group.\n * @group Props\n */\n optionGroupChildren = input<string>('items');\n /**\n * Name of the label field of an option group.\n * @group Props\n */\n optionGroupLabel = input<string>('label');\n /**\n * Name of the disabled field of an option or function to determine disabled state.\n * @group Props\n */\n optionDisabled = input<string | ((item: any) => boolean)>();\n /**\n * Defines a string that labels the filter input.\n * @group Props\n */\n ariaFilterLabel = input<string>();\n /**\n * Defines placeholder of the filter input.\n * @group Props\n */\n filterPlaceHolder = input<string>();\n /**\n * Text to display when filtering does not return any results.\n * @group Props\n */\n emptyFilterMessage = input<string>();\n /**\n * Text to display when there is no data. Defaults to global value in i18n translation configuration.\n * @group Props\n */\n emptyMessage = input<string>();\n /**\n * Whether to display options as grouped when nested options are provided.\n * @group Props\n */\n group = input(undefined, { transform: booleanAttribute });\n /**\n * An array of selectitems to display as the available options.\n * @group Props\n */\n options = input<any[]>();\n /**\n * When specified, filter displays with this value.\n * @group Props\n */\n filterValue = input<string>();\n /**\n * Whether all data is selected.\n * @group Props\n */\n selectAll = input<boolean>();\n /**\n * Whether to displays rows with alternating colors.\n * @group Props\n * @defaultValue false\n */\n striped = input(false, { transform: booleanAttribute });\n /**\n * Whether the selected option will be add highlight class.\n * @group Props\n * @defaultValue true\n */\n highlightOnSelect = input(true, { transform: booleanAttribute });\n /**\n * Whether the selected option will be shown with a check mark.\n * @group Props\n * @defaultValue false\n */\n checkmark = input(false, { transform: booleanAttribute });\n /**\n * Whether to enable dragdrop based reordering.\n * @group Props\n */\n dragdrop = input(false, { transform: booleanAttribute });\n /**\n * Array to use for CDK drop list data binding. When not provided, uses options array.\n * @group Props\n */\n dropListData = input<any[]>();\n /**\n * Spans 100% width of the container when enabled.\n * @defaultValue undefined\n * @group Props\n */\n fluid = input(undefined, { transform: booleanAttribute });\n /**\n * Callback to invoke on value change.\n * @param {ListboxChangeEvent} event - Custom change event.\n * @group Emits\n */\n onChange = output<ListboxChangeEvent>();\n /**\n * Callback to invoke when option is clicked.\n * @param {ListboxClickEvent} event - Custom click event.\n * @group Emits\n */\n onClick = output<ListboxClickEvent>();\n /**\n * Callback to invoke when option is double clicked.\n * @param {ListboxDoubleClickEvent} event - Custom double click event.\n * @group Emits\n */\n onDblClick = output<ListboxDoubleClickEvent>();\n /**\n * Callback to invoke when data is filtered.\n * @param {ListboxFilterEvent} event - Custom filter event.\n * @group Emits\n */\n onFilter = output<ListboxFilterEvent>();\n /**\n * Callback to invoke when component receives focus.\n * @param {FocusEvent} event - Focus event.\n * @group Emits\n */\n onFocus = output<FocusEvent>();\n /**\n * Callback to invoke when component loses focus.\n * @param {FocusEvent} event - Blur event.\n * @group Emits\n */\n onBlur = output<FocusEvent>();\n /**\n * Callback to invoke when all data is selected.\n * @param {ListboxSelectAllChangeEvent} event - Custom select event.\n * @group Emits\n */\n onSelectAllChange = output<ListboxSelectAllChangeEvent>();\n /**\n * Emits on lazy load.\n * @param {ScrollerLazyLoadEvent} event - Scroller lazy load event.\n * @group Emits\n */\n onLazyLoad = output<ScrollerLazyLoadEvent>();\n /**\n * Emits on item is dropped.\n * @param {CdkDragDrop<string[]>} event - Scroller lazy load event.\n * @group Emits\n */\n onDrop = output<CdkDragDrop<string[]>>();\n\n headerCheckboxViewChild = viewChild<ElementRef>('headerchkbox');\n\n filterViewChild = viewChild<ElementRef>('filterInput');\n\n lastHiddenFocusableElement = viewChild<ElementRef>('lastHiddenFocusableElement');\n\n firstHiddenFocusableElement = viewChild<ElementRef>('firstHiddenFocusableElement');\n\n scroller = viewChild<Scroller>('scroller');\n\n listViewChild = viewChild<ElementRef>('list');\n\n containerViewChild = viewChild<ElementRef>('container');\n\n headerFacet = contentChild(Header, { descendants: false });\n\n footerFacet = contentChild(Footer, { descendants: false });\n\n /**\n * Custom item template.\n * @param {ListboxItemTemplateContext} context - item context.\n * @see {@link ListboxItemTemplateContext}\n * @group Templates\n */\n itemTemplate = contentChild<TemplateRef<ListboxItemTemplateContext>>('item', { descendants: false });\n\n /**\n * Custom group template.\n * @param {ListboxGroupTemplateContext} context - group context.\n * @see {@link ListboxGroupTemplateContext}\n * @group Templates\n */\n groupTemplate = contentChild<TemplateRef<ListboxGroupTemplateContext>>('group', { descendants: false });\n\n /**\n * Custom header template.\n * @param {ListboxHeaderTemplateContext} context - header context.\n * @see {@link ListboxHeaderTemplateContext}\n * @group Templates\n */\n headerTemplate = contentChild<TemplateRef<ListboxHeaderTemplateContext>>('header', { descendants: false });\n\n /**\n * Custom filter template.\n * @param {ListboxFilterTemplateContext} context - filter context.\n * @see {@link ListboxFilterTemplateContext}\n * @group Templates\n */\n filterTemplate = contentChild<TemplateRef<ListboxFilterTemplateContext>>('filter', { descendants: false });\n\n /**\n * Custom footer template.\n * @param {ListboxFooterTemplateContext} context - footer context.\n * @see {@link ListboxFooterTemplateContext}\n * @group Templates\n */\n footerTemplate = contentChild<TemplateRef<ListboxFooterTemplateContext>>('footer', { descendants: false });\n\n /**\n * Custom empty filter message template.\n * @group Templates\n */\n emptyFilterTemplate = contentChild<TemplateRef<void>>('emptyfilter', { descendants: false });\n\n /**\n * Custom empty message template.\n * @group Templates\n */\n emptyTemplate = contentChild<TemplateRef<void>>('empty', { descendants: false });\n\n /**\n * Custom filter icon template.\n * @group Templates\n */\n filterIconTemplate = contentChild<TemplateRef<void>>('filtericon', { descendants: false });\n\n /**\n * Custom check icon template.\n * @param {ListboxCheckIconTemplateContext} context - check icon context.\n * @see {@link ListboxCheckIconTemplateContext}\n * @group Templates\n */\n checkIconTemplate = contentChild<TemplateRef<ListboxCheckIconTemplateContext>>('checkicon', { descendants: false });\n\n /**\n * Custom checkmark icon template.\n * @param {ListboxCheckmarkTemplateContext} context - checkmark context.\n * @see {@link ListboxCheckmarkTemplateContext}\n * @group Templates\n */\n checkmarkTemplate = contentChild<TemplateRef<ListboxCheckmarkTemplateContext>>('checkmark', { descendants: false });\n\n /**\n * Custom loader template.\n * @param {ListboxLoaderTemplateContext} context - loader context.\n * @see {@link ListboxLoaderTemplateContext}\n * @group Templates\n */\n loaderTemplate = contentChild<TemplateRef<ListboxLoaderTemplateContext>>('loader', { descendants: false });\n\n public _filterValue = signal<string | null | undefined>(null);\n\n public _filteredOptions: any[] | undefined | null;\n\n filterOptions: ListboxFilterOptions | undefined;\n\n public filtered: boolean | undefined | null;\n\n public value: any | undefined | null;\n\n public optionTouched: boolean | undefined | null;\n\n public focus: boolean | undefined | null;\n\n public headerCheckboxFocus: boolean | undefined | null;\n\n private translation = toSignal(this.config.translationObserver, { initialValue: this.config.translation });\n\n focused: boolean | undefined;\n\n scrollerTabIndex: string = '0';\n\n _componentStyle = inject(ListBoxStyle);\n\n _options = signal<any>(null);\n\n startRangeIndex = signal<number>(-1);\n\n focusedOptionIndex = signal<number>(-1);\n\n isDragging = signal<boolean>(false);\n\n searchValue: string | undefined;\n\n searchTimeout: any;\n\n private _internalId = uuid('pn_id_');\n\n $id = computed(() => this.id() || this._internalId);\n\n $tabindex = computed(() => this.tabindex() ?? 0);\n\n /**\n * Computed property for hidden focusable element tabindex\n */\n hiddenFocusableTabindex = computed(() => (!this.$disabled() ? this.$tabindex() : -1));\n\n /**\n * Computed property for header section visibility\n */\n hasHeader = computed(() => !!this.headerFacet() || !!this.headerTemplate());\n\n /**\n * Computed property for footer section visibility\n */\n hasFooter = computed(() => !!this.footerFacet() || !!this.footerTemplate());\n\n /**\n * Computed property for checkbox/filter header visibility\n */\n hasCheckboxOrFilter = computed(() => (this.checkbox() && this.multiple() && this.showToggleAll()) || this.filter());\n\n /**\n * Computed property for showing checkbox toggle all\n */\n showCheckboxToggleAll = computed(() => this.checkbox() && this.multiple() && this.showToggleAll());\n\n /**\n * Computed property for showing option checkbox\n */\n showOptionCheckbox = computed(() => this.checkbox() && this.multiple());\n\n /**\n * Computed property for showing filtered empty message\n */\n showFilteredEmptyMessage = computed(() => this.hasFilter() && this.isEmpty());\n\n /**\n * Computed property for showing empty message\n */\n showEmptyMessage = computed(() => !this.hasFilter() && this.isEmpty());\n\n /**\n * Computed property for aria-activedescendant\n */\n ariaActiveDescendant = computed(() => (this.focused ? this.focusedOptionId() : undefined));\n\n /**\n * Computed property for header template context\n */\n headerTemplateContext = computed(() => ({ $implicit: this.modelValue(), options: this.visibleOptions() }));\n\n /**\n * Computed property for footer template context\n */\n footerTemplateContext = computed(() => ({ $implicit: this.modelValue(), options: this.visibleOptions() }));\n\n /**\n * Computed property for filter input value\n */\n filterInputValue = computed(() => this._filterValue() || '');\n\n /**\n * Computed property for disabled attribute\n */\n disabledAttr = computed(() => (this.$disabled() ? '' : undefined));\n\n /**\n * Computed property for list id\n */\n listId = computed(() => `${this.$id()}_list`);\n\n /**\n * Computed property for scroller style\n */\n scrollerStyle = computed(() => ({ height: this.scrollHeight() === 'flex' ? '100%' : this.scrollHeight() }));\n\n /**\n * Computed property for showing default filter empty message\n */\n showDefaultFilterEmptyMessage = computed(() => !this.emptyFilterTemplate() && !this.emptyTemplate());\n\n /**\n * Computed property for empty or filter template\n */\n emptyOrFilterTemplate = computed(() => this.emptyFilterTemplate() || this.emptyTemplate());\n\n /**\n * Computed property for build in items context\n */\n buildInItemsContext = computed(() => ({ $implicit: this.visibleOptions(), options: {} }));\n\n /**\n * Computed property for dragdrop disabled\n */\n dragdropDisabled = computed(() => !this.dragdrop());\n\n /**\n * Computed property for check icon context\n */\n allSelectedContext = computed(() => ({ $implicit: this.allSelected() }));\n\n /**\n * Computed property for showing default checkmark\n */\n showDefaultCheckmark = computed(() => !this.checkmarkTemplate());\n\n /**\n * Computed property for showing default item label\n */\n showDefaultItemLabel = computed(() => !this.itemTemplate());\n\n /**\n * Computed property for showing default group label\n */\n showDefaultGroupLabel = computed(() => !this.groupTemplate());\n\n /**\n * Computed property for showing filter icon\n */\n showDefaultFilterIcon = computed(() => !this.filterIconTemplate());\n\n /**\n * Computed property for stable CDK drop list data reference\n */\n cdkDropData = computed(() => this.dropListData() || this._options());\n\n checkboxVariant = computed(() => (this.config.inputVariant() === 'filled' ? 'filled' : 'outlined'));\n\n listContainerStyle = computed(() => {\n const listStyle = this.listStyle();\n const scrollHeight = this.scrollHeight();\n const maxHeight = this.virtualScroll() || scrollHeight === 'flex' ? 'auto' : scrollHeight || 'auto';\n return { ...listStyle, 'max-height': maxHeight };\n });\n\n focusedOptionId = computed(() => (this.focusedOptionIndex() !== -1 ? `${this.$id()}_${this.focusedOptionIndex()}` : null));\n\n filterTabindex = computed(() => (!this.$disabled() && !this.focused ? this.$tabindex() : -1));\n\n filterResultMessageText = computed(() => {\n return isNotEmpty(this.visibleOptions()) ? this.filterMessageText().replaceAll('{0}', String(this.visibleOptions().length)) : this.emptyFilterMessageText();\n });\n\n filterMessageText = computed(() => {\n const t = this.translation();\n return this.filterMessage() || t?.searchMessage || '';\n });\n\n searchMessageText = computed(() => {\n const t = this.translation();\n return this.searchMessage() || t?.searchMessage || '';\n });\n\n emptyFilterMessageText = computed(() => {\n const t = this.translation();\n return this.emptyFilterMessage() || t?.emptySearchMessage || t?.emptyFilterMessage || '';\n });\n\n selectionMessageText = computed(() => {\n const t = this.translation();\n return this.selectionMessage() || t?.selectionMessage || '';\n });\n\n emptySelectionMessageText = computed(() => {\n const t = this.translation();\n return this.emptySelectionMessage() || t?.emptySelectionMessage || '';\n });\n\n selectedMessageText = computed(() => {\n return this.hasSelectedOption() ? this.selectionMessageText().replaceAll('{0}', this.multiple() ? String(this.modelValue().length) : '1') : this.emptySelectionMessageText();\n });\n\n ariaSetSize = computed(() => this.visibleOptions().filter((option) => !this.isOptionGroup(option)).length);\n\n virtualScrollerDisabled = computed(() => !this.virtualScroll());\n\n searchFields = computed(() => this.filterBy()?.split(',') || this.filterFields() || [this.optionLabel()]);\n\n toggleAllAriaLabel = computed(() => {\n const t = this.translation();\n return t?.aria ? t.aria[this.allSelected() ? 'selectAll' : 'unselectAll'] : undefined;\n });\n\n @HostListener('focusout', ['$event'])\n onHostFocusOut(event: FocusEvent) {\n this.onFocusout(event);\n }\n\n visibleOptions = computed(() => {\n const options = this.group() ? this.flatOptions(this._options()) : this._options() || [];\n return this._filterValue() ? this.filterService.filter(options, this.searchFields(), this._filterValue(), this.filterMatchMode(), this.filterLocale()) : options;\n });\n\n constructor() {\n super();\n\n // Sync options input to internal signal\n effect(() => {\n const opts = this.options();\n if (opts !== undefined) {\n this._options.set(opts);\n }\n });\n\n // Sync filterValue input to internal signal\n effect(() => {\n const fv = this.filterValue();\n if (fv !== undefined) {\n this._filterValue.set(fv);\n }\n });\n }\n\n onInit() {\n this.autoUpdateModel();\n\n if (this.filterBy()) {\n this.filterOptions = {\n filter: (value) => this.onFilterChange(value),\n reset: () => this.resetFilter()\n };\n }\n }\n\n flatOptions(options) {\n return (options || []).reduce((result, option, index) => {\n result.push({ optionGrou