UNPKG

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,186 lines (1,183 loc) 119 kB
export * from 'primeng/types/autocomplete'; import { NgTemplateOutlet } from '@angular/common'; import * as i0 from '@angular/core'; import { Injectable, InjectionToken, forwardRef, inject, input, numberAttribute, booleanAttribute, output, viewChild, contentChild, signal, computed, effect, HostListener, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { ChevronDown } from '@primeicons/angular/chevron-down'; import { Spinner } from '@primeicons/angular/spinner'; import { Times } from '@primeicons/angular/times'; import { TimesCircle } from '@primeicons/angular/times-circle'; import { uuid, equals, isNotEmpty, findLastIndex, resolveFieldData, focus, isEmpty, findSingle } from '@primeuix/utils'; import { OverlayService, TranslationKeys, SharedModule } from 'primeng/api'; import { AutoFocus } from 'primeng/autofocus'; import { PARENT_INSTANCE } from 'primeng/basecomponent'; import { BaseInput } from 'primeng/baseinput'; import * as i1 from 'primeng/bind'; import { Bind, BindModule } from 'primeng/bind'; import { Chip } from 'primeng/chip'; import { InputText } from 'primeng/inputtext'; import { Overlay } from 'primeng/overlay'; import { Ripple } from 'primeng/ripple'; import { Scroller } from 'primeng/scroller'; import { style } from '@primeuix/styles/autocomplete'; import { BaseStyle } from 'primeng/base'; const inlineStyles = { root: { position: 'relative' } }; const classes = { root: ({ instance }) => [ 'p-autocomplete p-component p-inputwrapper', { 'p-invalid': instance.invalid(), 'p-focus': instance.focused(), 'p-inputwrapper-filled': instance.$filled(), 'p-inputwrapper-focus': (instance.focused() && !instance.$disabled()) || instance.autofocus() || instance.overlayVisible(), 'p-autocomplete-open': instance.overlayVisible(), 'p-autocomplete-fluid': instance.hasFluid } ], pcInputText: 'p-autocomplete-input', inputMultiple: ({ instance }) => [ 'p-autocomplete-input-multiple', { 'p-disabled': instance.$disabled(), 'p-variant-filled': instance.$variant() === 'filled' } ], clearIcon: 'p-autocomplete-clear-icon', chipItem: ({ instance, i }) => [ 'p-autocomplete-chip-item', { 'p-focus': instance.focusedMultipleOptionIndex() === i } ], pcChip: 'p-autocomplete-chip', chipIcon: 'p-autocomplete-chip-icon', inputChip: 'p-autocomplete-input-chip', loader: 'p-autocomplete-loader', dropdown: 'p-autocomplete-dropdown', overlay: ({ instance }) => ['p-autocomplete-overlay p-component-overlay p-component', { 'p-input-filled': instance.$variant() === 'filled', 'p-ripple-disabled': instance.config.ripple() === false }], listContainer: 'p-autocomplete-list-container', list: 'p-autocomplete-list', optionGroup: 'p-autocomplete-option-group', option: ({ instance, option, i, scrollerOptions }) => ({ 'p-autocomplete-option': true, 'p-autocomplete-option-selected': instance.isSelected(option), 'p-focus': instance.focusedOptionIndex() === instance.getOptionIndex(i, scrollerOptions), 'p-disabled': instance.isOptionDisabled(option) }), emptyMessage: 'p-autocomplete-empty-message' }; class AutoCompleteStyle extends BaseStyle { name = 'autocomplete'; style = style; classes = classes; inlineStyles = inlineStyles; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: AutoCompleteStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: AutoCompleteStyle }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: AutoCompleteStyle, decorators: [{ type: Injectable }] }); /** * * AutoComplete is an input component that provides real-time suggestions while being typed. * * [Live Demo](https://www.primeng.org/autocomplete/) * * @module autocompletestyle * */ var AutoCompleteClasses; (function (AutoCompleteClasses) { /** * Class name of the root element */ AutoCompleteClasses["root"] = "p-autocomplete"; /** * Class name of the input element */ AutoCompleteClasses["pcInputText"] = "p-autocomplete-input"; /** * Class name of the input multiple element */ AutoCompleteClasses["inputMultiple"] = "p-autocomplete-input-multiple"; /** * Class name of the chip item element */ AutoCompleteClasses["chipItem"] = "p-autocomplete-chip-item"; /** * Class name of the chip element */ AutoCompleteClasses["pcChip"] = "p-autocomplete-chip"; /** * Class name of the chip icon element */ AutoCompleteClasses["chipIcon"] = "p-autocomplete-chip-icon"; /** * Class name of the input chip element */ AutoCompleteClasses["inputChip"] = "p-autocomplete-input-chip"; /** * Class name of the loader element */ AutoCompleteClasses["loader"] = "p-autocomplete-loader"; /** * Class name of the dropdown element */ AutoCompleteClasses["dropdown"] = "p-autocomplete-dropdown"; /** * Class name of the panel element */ AutoCompleteClasses["panel"] = "p-autocomplete-overlay"; /** * Class name of the list element */ AutoCompleteClasses["list"] = "p-autocomplete-list"; /** * Class name of the option group element */ AutoCompleteClasses["optionGroup"] = "p-autocomplete-option-group"; /** * Class name of the option element */ AutoCompleteClasses["option"] = "p-autocomplete-option"; /** * Class name of the empty message element */ AutoCompleteClasses["emptyMessage"] = "p-autocomplete-empty-message"; /** * Class name of the clear icon */ AutoCompleteClasses["clearIcon"] = "p-autocomplete-clear-icon"; })(AutoCompleteClasses || (AutoCompleteClasses = {})); const AUTOCOMPLETE_INSTANCE = new InjectionToken('AUTOCOMPLETE_INSTANCE'); const AUTOCOMPLETE_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AutoComplete), multi: true }; /** * AutoComplete is an input component that provides real-time suggestions when being typed. * @group Components */ class AutoComplete extends BaseInput { componentName = 'AutoComplete'; $pcAutoComplete = inject(AUTOCOMPLETE_INSTANCE, { optional: true, skipSelf: true }) ?? undefined; bindDirectiveInstance = inject(Bind, { self: true }); /** * Minimum number of characters to initiate a search. * @group Props */ minQueryLength = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "minQueryLength" }] : /* istanbul ignore next */ [])); /** * Delay between keystrokes to wait before sending a query. * @group Props */ delay = input(300, { ...(ngDevMode ? { debugName: "delay" } : /* istanbul ignore next */ {}), transform: numberAttribute }); /** * Inline style of the overlay panel element. * @group Props */ panelStyle = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "panelStyle" }] : /* istanbul ignore next */ [])); /** * Style class of the overlay panel element. * @group Props */ panelStyleClass = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "panelStyleClass" }] : /* istanbul ignore next */ [])); /** * Inline style of the input field. * @group Props */ inputStyle = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "inputStyle" }] : /* istanbul ignore next */ [])); /** * Identifier of the focus input to match a label defined for the component. * @group Props */ inputId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "inputId" }] : /* istanbul ignore next */ [])); /** * Inline style of the input field. * @group Props */ inputStyleClass = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "inputStyleClass" }] : /* istanbul ignore next */ [])); /** * Hint text for the input field. * @group Props */ placeholder = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ [])); /** * When present, it specifies that the input cannot be typed. * @group Props */ readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Maximum height of the suggestions panel. * @group Props */ scrollHeight = input('200px', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "scrollHeight" }] : /* istanbul ignore next */ [])); /** * Defines if data is loaded and interacted with in lazy manner. * @group Props */ lazy = input(false, { ...(ngDevMode ? { debugName: "lazy" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Whether the data should be loaded on demand during scroll. * @group Props */ virtualScroll = input(false, { ...(ngDevMode ? { debugName: "virtualScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Height of an item in the list for VirtualScrolling. * @group Props */ virtualScrollItemSize = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "virtualScrollItemSize" }] : /* istanbul ignore next */ [])); /** * Whether to use the scroller feature. The properties of scroller component can be used like an object in it. * @group Props */ virtualScrollOptions = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "virtualScrollOptions" }] : /* istanbul ignore next */ [])); /** * When enabled, highlights the first item in the list by default. * @group Props */ autoHighlight = input(false, { ...(ngDevMode ? { debugName: "autoHighlight" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * When present, autocomplete clears the manual input if it does not match of the suggestions to force only accepting values from the suggestions. * @group Props */ forceSelection = input(false, { ...(ngDevMode ? { debugName: "forceSelection" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Type of the input, defaults to "text". * @group Props */ type = input('text', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ [])); /** * Whether to automatically manage layering. * @group Props */ autoZIndex = input(true, { ...(ngDevMode ? { debugName: "autoZIndex" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Base zIndex value to use in layering. * @group Props */ baseZIndex = input(0, { ...(ngDevMode ? { debugName: "baseZIndex" } : /* istanbul ignore next */ {}), transform: numberAttribute }); /** * Defines a string that labels the input for accessibility. * @group Props */ ariaLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ [])); /** * Defines a string that labels the dropdown button for accessibility. * @group Props */ dropdownAriaLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "dropdownAriaLabel" }] : /* istanbul ignore next */ [])); /** * Specifies one or more IDs in the DOM that labels the input field. * @group Props */ ariaLabelledBy = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "ariaLabelledBy" }] : /* istanbul ignore next */ [])); /** * Icon class of the dropdown icon. * @group Props */ dropdownIcon = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "dropdownIcon" }] : /* istanbul ignore next */ [])); /** * Ensures uniqueness of selected items on multiple mode. * @group Props */ unique = input(true, { ...(ngDevMode ? { debugName: "unique" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Whether to display options as grouped when nested options are provided. * @group Props */ group = input(false, { ...(ngDevMode ? { debugName: "group" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Whether to run a query when input receives focus. * @group Props */ completeOnFocus = input(false, { ...(ngDevMode ? { debugName: "completeOnFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * When enabled, a clear icon is displayed to clear the value. * @group Props */ showClear = input(false, { ...(ngDevMode ? { debugName: "showClear" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Displays a button next to the input field when enabled. * @group Props */ dropdown = input(false, { ...(ngDevMode ? { debugName: "dropdown" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Whether to show the empty message or not. * @group Props */ showEmptyMessage = input(true, { ...(ngDevMode ? { debugName: "showEmptyMessage" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Specifies the behavior dropdown button. Default "blank" mode sends an empty string and "current" mode sends the input value. * @group Props */ dropdownMode = input('blank', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "dropdownMode" }] : /* istanbul ignore next */ [])); /** * Specifies if multiple values can be selected. * @deprecated Use InputTags component instead for chips/tags input functionality. * @group Props */ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * When enabled, the input value is added to the selected items on tab key press when multiple is true and typeahead is false. * @deprecated Use InputTags component instead for chips/tags input functionality. * @group Props */ addOnTab = input(false, { ...(ngDevMode ? { debugName: "addOnTab" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Index of the element in tabbing order. * @group Props */ tabindex = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "tabindex" }] : /* istanbul ignore next */ [])); /** * A property to uniquely identify a value in options. * @group Props */ dataKey = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "dataKey" }] : /* istanbul ignore next */ [])); /** * Text to display when there is no data. Defaults to global value in i18n translation configuration. * @group Props */ emptyMessage = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "emptyMessage" }] : /* istanbul ignore next */ [])); /** * When present, it specifies that the component should automatically get focus on load. * @group Props */ autofocus = input(false, { ...(ngDevMode ? { debugName: "autofocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Used to define a string that autocomplete attribute the current element. * @group Props */ autocomplete = input('off', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ [])); /** * Name of the options field of an option group. * @group Props */ optionGroupChildren = input('items', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "optionGroupChildren" }] : /* istanbul ignore next */ [])); /** * Name of the label field of an option group. * @group Props */ optionGroupLabel = input('label', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "optionGroupLabel" }] : /* istanbul ignore next */ [])); /** * Options for the overlay element. * @group Props */ overlayOptions = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "overlayOptions" }] : /* istanbul ignore next */ [])); /** * An array of suggestions to display. * @group Props */ suggestions = input([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ [])); /** * Property name or getter function to use as the label of an option. * @group Props */ optionLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionLabel" }] : /* istanbul ignore next */ [])); /** * Property name or getter function to use as the value of an option. * @group Props */ optionValue = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionValue" }] : /* istanbul ignore next */ [])); /** * Unique identifier of the component. * @group Props */ id = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "id" }] : /* istanbul ignore next */ [])); /** * Text to display when the search is active. Defaults to global value in i18n translation configuration. * @group Props * @defaultValue '{0} results are available' */ searchMessage = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "searchMessage" }] : /* istanbul ignore next */ [])); /** * Text to display when filtering does not return any results. Defaults to global value in i18n translation configuration. * @group Props * @defaultValue 'No selected item' */ emptySelectionMessage = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "emptySelectionMessage" }] : /* istanbul ignore next */ [])); /** * Text to be displayed in hidden accessible field when options are selected. Defaults to global value in i18n translation configuration. * @group Props * @defaultValue '{0} items selected' */ selectionMessage = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "selectionMessage" }] : /* istanbul ignore next */ [])); /** * Whether to focus on the first visible or selected element when the overlay panel is shown. * @group Props */ autoOptionFocus = input(false, { ...(ngDevMode ? { debugName: "autoOptionFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * When enabled, the focused option is selected. * @group Props */ selectOnFocus = input(false, { ...(ngDevMode ? { debugName: "selectOnFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Locale to use in searching. The default locale is the host environment's current locale. * @group Props */ searchLocale = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "searchLocale" }] : /* istanbul ignore next */ [])); /** * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined. * @group Props */ optionDisabled = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionDisabled" }] : /* istanbul ignore next */ [])); /** * When enabled, the hovered option will be focused. * @group Props */ focusOnHover = input(true, { ...(ngDevMode ? { debugName: "focusOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Whether typeahead is active or not. * @defaultValue true * @group Props */ typeahead = input(true, { ...(ngDevMode ? { debugName: "typeahead" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Whether to add an item on blur event if the input has value and typeahead is false with multiple mode. * @deprecated Use InputTags component instead for chips/tags input functionality. * @defaultValue false * @group Props */ addOnBlur = input(false, { ...(ngDevMode ? { debugName: "addOnBlur" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Separator char to add item when typeahead is false and multiple mode is enabled. * @deprecated Use InputTags component instead for chips/tags input functionality. * @group Props */ separator = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "separator" }] : /* istanbul ignore next */ [])); /** * Target element to attach the overlay, valid values are "body" or a local ng-template variable of another element (note: use binding with brackets for template variables, e.g. [appendTo]="mydiv" for a div element having #mydiv as variable name). * @defaultValue 'self' * @group Props */ appendTo = input(undefined, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "appendTo" }] : /* istanbul ignore next */ [])); /** * The motion options. * @group Props */ motionOptions = input(undefined, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "motionOptions" }] : /* istanbul ignore next */ [])); /** * Callback to invoke to search for suggestions. * @param {AutoCompleteCompleteEvent} event - Custom complete event. * @group Emits */ completeMethod = output(); /** * Callback to invoke when a suggestion is selected. * @param {AutoCompleteSelectEvent} event - custom select event. * @group Emits */ onSelect = output(); /** * Callback to invoke when a selected value is removed. * @param {AutoCompleteUnselectEvent} event - custom unselect event. * @group Emits */ onUnselect = output(); /** * Callback to invoke when an item is added via addOnBlur or separator features. * @param {AutoCompleteAddEvent} event - Custom add event. * @group Emits */ onAdd = output(); /** * Callback to invoke when the component receives focus. * @param {Event} event - Browser event. * @group Emits */ onFocus = output(); /** * Callback to invoke when the component loses focus. * @param {Event} event - Browser event. * @group Emits */ onBlur = output(); /** * Callback to invoke to when dropdown button is clicked. * @param {AutoCompleteDropdownClickEvent} event - custom dropdown click event. * @group Emits */ onDropdownClick = output(); /** * Callback to invoke when clear button is clicked. * @group Emits */ onClear = output(); /** * Callback to invoke on input key down. * @param {KeyboardEvent} event - Keyboard event. * @group Emits */ onInputKeydown = output(); /** * Callback to invoke on input key up. * @param {KeyboardEvent} event - Keyboard event. * @group Emits */ onKeyUp = output(); /** * Callback to invoke on overlay is shown. * @group Emits */ onShow = output(); /** * Callback to invoke on overlay is hidden. * @group Emits */ onHide = output(); /** * Callback to invoke on lazy load data. * @param {AutoCompleteLazyLoadEvent} event - Lazy load event. * @group Emits */ onLazyLoad = output(); inputEL = viewChild('focusInput', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "inputEL" }] : /* istanbul ignore next */ [])); multiInputEl = viewChild('multiIn', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "multiInputEl" }] : /* istanbul ignore next */ [])); multiContainerEL = viewChild('multiContainer', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "multiContainerEL" }] : /* istanbul ignore next */ [])); dropdownButton = viewChild('ddBtn', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "dropdownButton" }] : /* istanbul ignore next */ [])); itemsViewChild = viewChild('items', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "itemsViewChild" }] : /* istanbul ignore next */ [])); scroller = viewChild('scroller', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "scroller" }] : /* istanbul ignore next */ [])); overlayViewChild = viewChild('overlay', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "overlayViewChild" }] : /* istanbul ignore next */ [])); itemsWrapper; /** * Custom item template. * @group Templates */ itemTemplate = contentChild('item', { ...(ngDevMode ? { debugName: "itemTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom empty message template. * @group Templates */ emptyTemplate = contentChild('empty', { ...(ngDevMode ? { debugName: "emptyTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom header template. * @group Templates */ headerTemplate = contentChild('header', { ...(ngDevMode ? { debugName: "headerTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom footer template. * @group Templates */ footerTemplate = contentChild('footer', { ...(ngDevMode ? { debugName: "footerTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom selected item template. * @group Templates */ selectedItemTemplate = contentChild('selecteditem', { ...(ngDevMode ? { debugName: "selectedItemTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom group template. * @group Templates */ groupTemplate = contentChild('group', { ...(ngDevMode ? { debugName: "groupTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom loader template. * @group Templates */ loaderTemplate = contentChild('loader', { ...(ngDevMode ? { debugName: "loaderTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom remove icon template. * @group Templates */ removeIconTemplate = contentChild('removeicon', { ...(ngDevMode ? { debugName: "removeIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom loading icon template. * @group Templates */ loadingIconTemplate = contentChild('loadingicon', { ...(ngDevMode ? { debugName: "loadingIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom clear icon template. * @group Templates */ clearIconTemplate = contentChild('clearicon', { ...(ngDevMode ? { debugName: "clearIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom dropdown icon template. * @group Templates */ dropdownIconTemplate = contentChild('dropdownicon', { ...(ngDevMode ? { debugName: "dropdownIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); onHostClick(event) { this.onContainerClick(event); } value; timeout = null; overlayVisible = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "overlayVisible" }] : /* istanbul ignore next */ [])); suggestionsUpdated; highlightOption; highlightOptionChanged; focused = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ [])); loading = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ [])); scrollHandler; listId; searchTimeout = null; dirty = false; focusedMultipleOptionIndex = signal(-1, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusedMultipleOptionIndex" }] : /* istanbul ignore next */ [])); focusedOptionIndex = signal(-1, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusedOptionIndex" }] : /* istanbul ignore next */ [])); _componentStyle = inject(AutoCompleteStyle); overlayService = inject(OverlayService); _internalId = uuid('pn_id_'); $id = computed(() => this.id() || this._internalId, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$id" }] : /* istanbul ignore next */ [])); $appendTo = computed(() => this.appendTo() || this.config.overlayAppendTo(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$appendTo" }] : /* istanbul ignore next */ [])); get $overlayTarget() { return this.el?.nativeElement?.closest('[data-pc-name="inputtags"]') ? '@grandparent' : '@parent'; } visibleOptions = computed(() => { return this.group() ? this.flatOptions(this.suggestions()) : this.suggestions() || []; }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "visibleOptions" }] : /* istanbul ignore next */ [])); inputValue = computed(() => { const modelValue = this.modelValue(); const selectedOption = this.optionValueSelected ? (this.suggestions() || []).find((option) => equals(option, modelValue, this.equalityKey())) : modelValue; if (isNotEmpty(modelValue)) { if (typeof modelValue === 'object' || this.optionValueSelected) { const label = this.getOptionLabel(selectedOption); return label != null ? label : modelValue; } else { return modelValue; } } else { return ''; } }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "inputValue" }] : /* istanbul ignore next */ [])); $showClear = computed(() => this.$filled() && !this.$disabled() && this.showClear() && !this.loading(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$showClear" }] : /* istanbul ignore next */ [])); scrollerStyle = computed(() => ({ height: this.scrollHeight() }), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "scrollerStyle" }] : /* istanbul ignore next */ [])); $tabindex = computed(() => (!this.$disabled() ? this.tabindex() : -1), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$tabindex" }] : /* istanbul ignore next */ [])); requiredAttr = computed(() => (this.required() ? '' : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "requiredAttr" }] : /* istanbul ignore next */ [])); readonlyAttr = computed(() => (this.readonly() ? '' : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "readonlyAttr" }] : /* istanbul ignore next */ [])); disabledAttr = computed(() => (this.$disabled() ? '' : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "disabledAttr" }] : /* istanbul ignore next */ [])); $listId = computed(() => this.$id() + '_list', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$listId" }] : /* istanbul ignore next */ [])); get focusedMultipleOptionId() { return this.focusedMultipleOptionIndex() !== -1 ? `${this.$id()}_multiple_option_${this.focusedMultipleOptionIndex()}` : null; } get focusedOptionId() { return this.focusedOptionIndex() !== -1 ? `${this.$id()}_${this.focusedOptionIndex()}` : null; } get searchResultMessageText() { return isNotEmpty(this.visibleOptions()) && this.overlayVisible() ? this.searchMessageText.replaceAll('{0}', this.visibleOptions().length) : this.emptySearchMessageText; } get searchMessageText() { return this.searchMessage() || this.config.translation.searchMessage || ''; } get emptySearchMessageText() { return this.emptyMessage() || this.config.translation.emptySearchMessage || ''; } get selectionMessageText() { return this.selectionMessage() || this.config.translation.selectionMessage || ''; } get emptySelectionMessageText() { return this.emptySelectionMessage() || this.config.translation.emptySelectionMessage || ''; } get selectedMessageText() { return this.hasSelectedOption() ? this.selectionMessageText.replaceAll('{0}', this.multiple() ? this.modelValue()?.length : '1') : this.emptySelectionMessageText; } get ariaSetSize() { return this.visibleOptions().filter((option) => !this.isOptionGroup(option)).length; } get listLabel() { return this.translate(TranslationKeys.ARIA, 'listLabel'); } get virtualScrollerDisabled() { return !this.virtualScroll(); } get optionValueSelected() { return typeof this.modelValue() === 'string' && this.optionValue(); } chipItemClass(index) { return this._componentStyle.classes.chipItem({ instance: this, i: index }); } constructor() { super(); effect(() => { const value = this.suggestions(); if (value) { this.handleSuggestionsChange(); } }); } onAfterViewChecked() { this.bindDirectiveInstance.setAttrs(this.ptms(['host', 'root'])); //Use timeouts as since Angular 4.2, AfterViewChecked is broken and not called after panel is updated if (this.suggestionsUpdated && this.overlayViewChild()) { setTimeout(() => { if (this.overlayViewChild()) { this.overlayViewChild().alignOverlay(); } }, 1); this.suggestionsUpdated = false; } } handleSuggestionsChange() { if (this.loading()) { this.suggestions()?.length > 0 || this.showEmptyMessage() || !!this.emptyTemplate() ? this.show() : this.hide(); const focusedOptionIndex = this.overlayVisible() && this.autoOptionFocus() ? this.findFirstFocusedOptionIndex() : -1; this.focusedOptionIndex.set(focusedOptionIndex); this.suggestionsUpdated = true; this.loading.set(false); } } flatOptions(options) { return (options || []).reduce((result, option, index) => { result.push({ optionGroup: option, group: true, index }); const optionGroupChildren = this.getOptionGroupChildren(option); optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o)); return result; }, []); } isOptionGroup(option) { return this.optionGroupLabel() && option.optionGroup && option.group; } findFirstOptionIndex() { return this.visibleOptions().findIndex((option) => this.isValidOption(option)); } findLastOptionIndex() { return findLastIndex(this.visibleOptions(), (option) => this.isValidOption(option)); } findFirstFocusedOptionIndex() { const selectedIndex = this.findSelectedOptionIndex(); return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex; } findLastFocusedOptionIndex() { const selectedIndex = this.findSelectedOptionIndex(); return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex; } findSelectedOptionIndex() { return this.hasSelectedOption() ? this.visibleOptions().findIndex((option) => this.isValidSelectedOption(option)) : -1; } findNextOptionIndex(index) { const matchedOptionIndex = index < this.visibleOptions().length - 1 ? this.visibleOptions() .slice(index + 1) .findIndex((option) => this.isValidOption(option)) : -1; return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index; } findPrevOptionIndex(index) { const matchedOptionIndex = index > 0 ? findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidOption(option)) : -1; return matchedOptionIndex > -1 ? matchedOptionIndex : index; } isValidSelectedOption(option) { return this.isValidOption(option) && this.isSelected(option); } isValidOption(option) { return option && !(this.isOptionDisabled(option) || this.isOptionGroup(option)); } isOptionDisabled(option) { return this.optionDisabled() ? resolveFieldData(option, this.optionDisabled()) : false; } isSelected(option) { if (this.multiple()) { return this.unique() ? this.modelValue()?.some((model) => equals(model, option, this.equalityKey())) : false; } return equals(this.modelValue(), option, this.equalityKey()); } isOptionMatched(option, value) { return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.searchLocale()) === value.toLocaleLowerCase(this.searchLocale()); } isInputClicked(event) { return event.target === this.inputEL()?.nativeElement; } isDropdownClicked(event) { return this.dropdownButton()?.nativeElement ? event.target === this.dropdownButton().nativeElement || this.dropdownButton().nativeElement.contains(event.target) : false; } equalityKey() { return this.optionValue() ? undefined : this.dataKey(); } onContainerClick(event) { if (this.$disabled() || this.loading() || this.isInputClicked(event) || this.isDropdownClicked(event)) { return; } if (!this.overlayViewChild() || !this.overlayViewChild().overlayViewChild()?.nativeElement.contains(event.target)) { focus(this.inputEL()?.nativeElement); } } handleDropdownClick(event) { let query = undefined; if (this.overlayVisible()) { this.hide(true); } else { focus(this.inputEL()?.nativeElement); query = this.inputEL()?.nativeElement?.value; if (this.dropdownMode() === 'blank') this.search(event, '', 'dropdown'); else if (this.dropdownMode() === 'current') this.search(event, query, 'dropdown'); } this.onDropdownClick.emit({ originalEvent: event, query }); } onInput(event) { if (this.typeahead()) { const _minLength = this.minQueryLength() || 1; if (this.searchTimeout) { clearTimeout(this.searchTimeout); } let query = event.target.value; const maxLen = this.maxlength(); if (maxLen != null) { query = query.split('').slice(0, maxLen).join(''); } if (!this.multiple() && !this.forceSelection()) { this.updateModel(query); } if (query.length === 0 && !this.multiple()) { this.onClear.emit(); setTimeout(() => { this.hide(); }, this.delay() / 2); } else { if (query.length >= _minLength) { this.focusedOptionIndex.set(-1); this.searchTimeout = setTimeout(() => { this.search(event, query, 'input'); }, this.delay()); } else { this.hide(); } } } } onInputChange(event) { this.updateInputWithForceSelection(event); } onInputFocus(event) { if (this.$disabled()) { // For ScreenReaders return; } if (!this.dirty && this.completeOnFocus()) { this.search(event, event.target.value, 'focus'); } this.dirty = true; this.focused.set(true); const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.overlayVisible() && this.autoOptionFocus() ? this.findFirstFocusedOptionIndex() : -1; this.focusedOptionIndex.set(focusedOptionIndex); this.overlayVisible() && this.scrollInView(this.focusedOptionIndex()); this.onFocus.emit(event); } onMultipleContainerFocus(event) { if (this.$disabled()) { // For ScreenReaders return; } this.focused.set(true); } onMultipleContainerBlur(event) { this.focusedMultipleOptionIndex.set(-1); this.focused.set(false); } onMultipleContainerKeyDown(event) { if (this.$disabled()) { event.preventDefault(); return; } switch (event.code) { case 'ArrowLeft': this.onArrowLeftKeyOnMultiple(event); break; case 'ArrowRight': this.onArrowRightKeyOnMultiple(event); break; case 'Backspace': this.onBackspaceKeyOnMultiple(event); break; default: break; } } onInputBlur(event) { this.dirty = false; this.focused.set(false); this.focusedOptionIndex.set(-1); if (this.addOnBlur() && this.multiple() && !this.typeahead()) { const inputValue = (this.multiInputEl()?.nativeElement?.value || event.target.value || '').trim(); if (inputValue && !this.isSelected(inputValue)) { this.updateModel([...(this.modelValue() || []), inputValue]); this.onAdd.emit({ originalEvent: event, value: inputValue }); if (this.multiInputEl()?.nativeElement) { this.multiInputEl().nativeElement.value = ''; } else { event.target.value = ''; } } } this.onModelTouched(); this.onBlur.emit(event); } onInputPaste(event) { if (this.separator() && this.multiple() && !this.typeahead()) { const pastedData = event.clipboardData?.getData('Text'); if (pastedData) { const values = pastedData.split(this.separator()); const newValues = [...(this.modelValue() || [])]; values.forEach((value) => { const trimmedValue = value.trim(); if (trimmedValue && !this.isSelected(trimmedValue)) { newValues.push(trimmedValue); } }); if (newValues.length > (this.modelValue() || []).length) { const addedValues = newValues.slice((this.modelValue() || []).length); this.updateModel(newValues); addedValues.forEach((addedValue) => { this.onAdd.emit({ originalEvent: event, value: addedValue }); }); if (this.multiInputEl()?.nativeElement) { this.multiInputEl().nativeElement.value = ''; } else { event.target.value = ''; } event.preventDefault(); } } } else { this.onKeyDown(event); } } onInputKeyUp(event) { this.onKeyUp.emit(event); } onKeyDown(event) { if (this.$disabled()) { event.preventDefault(); return; } // Emit keydown event for external handling this.onInputKeydown.emit(event); switch (event.code) { case 'ArrowDown': this.onArrowDownKey(event); break; case 'ArrowUp': this.onArrowUpKey(event); break; case 'ArrowLeft': this.onArrowLeftKey(event); break; case 'ArrowRight': this.onArrowRightKey(event); break; case 'Home': this.onHomeKey(event); break; case 'End': this.onEndKey(event); break; case 'PageDown': this.onPageDownKey(event); break; case 'PageUp': this.onPageUpKey(event); break; case 'Enter': case 'NumpadEnter': this.onEnterKey(event); break; case 'Escape': this.onEscapeKey(event); if (this.overlayVisible()) { event.stopPropagation(); } break; case 'Tab': this.onTabKey(event); break; case 'Backspace': this.onBackspaceKey(event); break; case 'ShiftLeft': case 'ShiftRight': //NOOP break; default: this.handleSeparatorKey(event); break; } } handleSeparatorKey(event) { const separator = this.separator(); if (separator && this.multiple() && !this.typeahead()) { if (separator === event.key || (typeof separator === 'string' && event.key === separator) || (separator instanceof RegExp && event.key.match(separator))) { const inputValue = (this.multiInputEl()?.nativeElement?.value || event.target.value || '').trim(); if (inputValue && !this.isSelected(inputValue)) { this.updateModel([...(this.modelValue() || []), inputValue]); this.onAdd.emit({ originalEvent: event, value: inputValue }); if (this.multiInputEl()?.nativeElement) { this.multiInputEl().nativeElement.value = ''; } else { event.target.value = ''; } event.preventDefault(); } } } } onArrowDownKey(event) { if (!this.overlayVisible()) { return; } const optionIndex = this.focusedOptionIndex() !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex()) : this.findFirstFocusedOptionIndex(); this.changeFocusedOptionIndex(event, optionIndex); event.preventDefault(); event.stopPropagation(); } onArrowUpKey(event) { if (!this.overlayVisible()) { return; } if (event.altKey) { if (this.focusedOptionIndex() !== -1) { this.onOptionSelect(event, this.visibleOptions()[this.focusedOptionIndex()]); } this.overlayVisible() && this.hide(); event.preventDefault(); } else { const optionIndex = this.focusedOptionIndex() !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex()) : this.findLastFocusedOptionIndex(); this.changeFocusedOptionIndex(event, optionIndex); event.preventDefault(); event.stopPropagation(); } } onArrowLeftKey(event) { const target = event.currentTarget; this.focusedOptionIndex.set(-1); if (this.multiple()) { if (isEmpty(target.value) && this.hasSelectedOption()) { focus(this.multiContainerEL()?.nativeElement); this.focusedMultipleOptionIndex.set(this.modelValue().length); } else { event.stopPropagation(); // To prevent onArrowLeftKeyOnMultiple method } } } onArrowRightKey(event) { this.focusedOptionIndex.set(-1); this.multiple() && event.stopPropagation(); // To prevent onArrowRightKeyOnMultiple method } onHomeKey(event) { const currentTarget = event.currentTarget; const len = currentTarget.value.length; currentTarget.setSelectionRange(0, event.shiftKey ? len : 0); this.focusedOptionIndex.set(-1); event.preventDefault(); } onEndKey(event) { const currentTarget = event.currentTarget; const len = currentTarget.value.length; currentTarget.setSelectionRange(event.shiftKey ? 0 : len, len); this.focusedOptionIndex.set(-1); event.preventDefault(); } onPageDownKey(event) { this.scrollInView(this.visibleOptions().length - 1); event.preventDefault(); } onPageUpKey(event) { this.scrollInView(0); event.preventDefault(); } onEnterKey(event) { if (!this.typeahead() && !this.forceSelection()) { if (this.multiple()) { const inputValue = event.target.value?.trim(); if (inputValue && !this.isSelected(inputValue)) { this.updateModel([...(this.modelValue() || []), inputValue]); this.onAdd.emit({ originalEvent: event, value: inputValue }); this.inputEL()?.nativeElement && (this.inputEL().nativeElement.value = ''); } } } if (!this.overlayVisible()) { return; } else { if (this.focusedOptionIndex() !== -1) { this.onOptionSelect(event, this.visibleOptions()[this.focusedOptionIndex()]); } this.hide(); } event.preventDefault(); } onEscapeKey(event) { this.overlayVisible() && this.hide(true); event.preventDefault(); } onTabKey(event) { // If there's a focused option in the dropdown, select it if (this.focusedOptionIndex() !== -1) { this.onOptionSelect(event, this.visibleOptions()[this.focusedOptionIndex()]); return; } // Handle tab key behavior for multiple mode without typeahead if (this.multiple() && !this.typeahead()) { const inputValue = (this.multiInputEl()?.nativeElement?.value || this.inputEL()?.nativeElement?.value || '').trim(); if (this.addOnTab()) { if (inputValue && !this.isSelected(inputValue)) {