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 • 125 kB
Source Map (JSON)
{"version":3,"file":"primeng-cascadeselect.mjs","sources":["../../src/cascadeselect/cascadeselect-token.ts","../../src/cascadeselect/style/cascadeselectstyle.ts","../../src/cascadeselect/cascadeselect-sub.ts","../../src/cascadeselect/cascadeselect.ts","../../src/cascadeselect/primeng-cascadeselect.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport const CASCADESELECT_INSTANCE = new InjectionToken<any>('CASCADESELECT_INSTANCE');\n","import { Injectable } from '@angular/core';\nimport { style } from '@primeuix/styles/cascadeselect';\nimport { BaseStyle } from 'primeng/base';\n\nconst inlineStyles = {\n root: ({ instance }) => ({ position: instance.$appendTo() === 'self' ? 'relative' : undefined })\n};\n\nconst classes = {\n root: ({ instance }) => [\n 'p-cascadeselect p-component p-inputwrapper',\n {\n 'p-cascadeselect-mobile': instance.queryMatches(),\n 'p-disabled': instance.$disabled(),\n 'p-invalid': instance.invalid(),\n 'p-variant-filled': instance.$variant() === 'filled',\n 'p-focus': instance.focused(),\n 'p-inputwrapper-filled': instance.modelValue(),\n 'p-inputwrapper-focus': instance.focused() || instance.overlayVisible(),\n 'p-cascadeselect-open': instance.overlayVisible(),\n 'p-cascadeselect-fluid': instance.hasFluid,\n 'p-cascadeselect-sm p-inputfield-sm': instance.size() === 'small',\n 'p-cascadeselect-lg p-inputfield-lg': instance.size() === 'large'\n }\n ],\n label: ({ instance }) => [\n 'p-cascadeselect-label',\n {\n 'p-placeholder': instance.label() === instance.placeholder(),\n 'p-cascadeselect-label-empty': !instance.value() && (instance.label() === 'p-emptylabel' || instance.label().length === 0)\n }\n ],\n clearIcon: 'p-cascadeselect-clear-icon',\n dropdown: 'p-cascadeselect-dropdown',\n loadingIcon: 'p-cascadeselect-loading-icon',\n dropdownIcon: 'p-cascadeselect-dropdown-icon',\n overlay: ({ instance }) => [\n 'p-cascadeselect-overlay p-component-overlay p-component',\n {\n 'p-cascadeselect-mobile-active': instance.queryMatches()\n }\n ],\n listContainer: 'p-cascadeselect-list-container',\n list: 'p-cascadeselect-list',\n option: ({ instance, processedOption }) => [\n 'p-cascadeselect-option',\n {\n 'p-cascadeselect-option-active': instance.isOptionActive(processedOption),\n 'p-cascadeselect-option-selected': instance.isOptionSelected(processedOption),\n 'p-focus': instance.isOptionFocused(processedOption),\n 'p-disabled': instance.isOptionDisabled(processedOption)\n }\n ],\n optionContent: 'p-cascadeselect-option-content',\n optionText: 'p-cascadeselect-option-text',\n groupIcon: 'p-cascadeselect-group-icon',\n optionList: 'p-cascadeselect-list p-cascadeselect-overlay p-cascadeselect-option-list'\n};\n\n@Injectable()\nexport class CascadeSelectStyle extends BaseStyle {\n name = 'cascadeselect';\n\n style = style;\n\n classes = classes;\n\n inlineStyles = inlineStyles;\n}\n\n/**\n *\n * CascadeSelect is a form component to select a value from a nested structure of options.\n *\n * [Live Demo](https://www.primeng.org/cascadeselect/)\n *\n * @module cascadeselectstyle\n *\n */\nexport enum CascadeSelectClasses {\n /**\n * Class name of the root element\n */\n root = 'p-cascadeselect',\n /**\n * Class name of the label element\n */\n label = 'p-cascadeselect-label',\n /**\n * Class name of the dropdown element\n */\n dropdown = 'p-cascadeselect-dropdown',\n /**\n * Class name of the loading icon element\n */\n loadingIcon = 'p-cascadeselect-loading-icon',\n /**\n * Class name of the dropdown icon element\n */\n clearIcon = 'p-cascadeselect-clear-icon',\n /**\n * Class name of the dropdown icon element\n */\n dropdownIcon = 'p-cascadeselect-dropdown-icon',\n /**\n * Class name of the overlay element\n */\n overlay = 'p-cascadeselect-overlay',\n /**\n * Class name of the list container element\n */\n listContainer = 'p-cascadeselect-list-container',\n /**\n * Class name of the list element\n */\n list = 'p-cascadeselect-list',\n /**\n * Class name of the item element\n */\n item = 'p-cascadeselect-item',\n /**\n * Class name of the item content element\n */\n itemContent = 'p-cascadeselect-item-content',\n /**\n * Class name of the item text element\n */\n itemText = 'p-cascadeselect-item-text',\n /**\n * Class name of the group icon element\n */\n groupIcon = 'p-cascadeselect-group-icon',\n /**\n * Class name of the item list element\n */\n itemList = 'p-cascadeselect-item-list'\n}\n\nexport interface CascadeSelectStyle extends BaseStyle {}\n","import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';\nimport { booleanAttribute, ChangeDetectionStrategy, Component, effect, inject, input, numberAttribute, output, signal, TemplateRef, untracked, ViewEncapsulation } from '@angular/core';\nimport { MotionEvent, MotionOptions } from '@primeuix/motion';\nimport { equals, isNotEmpty, nestedPosition, resolveFieldData } from '@primeuix/utils';\nimport { SharedModule } from 'primeng/api';\nimport { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { Bind } from 'primeng/bind';\nimport { AngleRight as AngleRightIcon } from '@primeicons/angular/angle-right';\nimport { MotionModule } from 'primeng/motion';\nimport { Ripple } from 'primeng/ripple';\nimport type { CascadeSelectOptionTemplateContext, CascadeSelectProcessedOption, CascadeSelectSubChangeEvent, CascadeSelectSubFocusChangeEvent } from 'primeng/types/cascadeselect';\nimport type { CascadeSelect } from './cascadeselect';\nimport { CASCADESELECT_INSTANCE } from './cascadeselect-token';\nimport { CascadeSelectStyle } from './style/cascadeselectstyle';\n\n@Component({\n selector: 'ul[pCascadeSelectSub]',\n standalone: true,\n imports: [NgTemplateOutlet, Ripple, AngleRightIcon, SharedModule, Bind, MotionModule],\n template: `\n @for (processedOption of options(); track processedOption.key; let i = $index) {\n <li\n [class]=\"cx('option', { processedOption })\"\n role=\"treeitem\"\n [attr.aria-level]=\"level() + 1\"\n [attr.aria-setsize]=\"options()?.length\"\n [pBind]=\"getPTOptions(processedOption, i, 'option')\"\n [id]=\"getOptionId(processedOption)\"\n [attr.aria-label]=\"getOptionLabelToRender(processedOption)\"\n [attr.aria-selected]=\"getAriaSelected(processedOption)\"\n [attr.aria-posinset]=\"i + 1\"\n >\n <div\n [class]=\"cx('optionContent')\"\n (click)=\"onOptionClick($event, processedOption)\"\n (mouseenter)=\"onOptionMouseEnter($event, processedOption)\"\n (mousemove)=\"onOptionMouseMove($event, processedOption)\"\n pRipple\n [pBind]=\"getPTOptions(processedOption, i, 'optionContent')\"\n >\n @if (optionTemplate()) {\n <ng-container *ngTemplateOutlet=\"optionTemplate(); context: getOptionTemplateContext(processedOption)\"></ng-container>\n } @else {\n <span [class]=\"cx('optionText')\" [pBind]=\"getPTOptions(processedOption, i, 'optionText')\">{{ getOptionLabelToRender(processedOption) }}</span>\n }\n @if (isOptionGroup(processedOption)) {\n <span [class]=\"cx('groupIcon')\" [pBind]=\"getPTOptions(processedOption, i, 'groupIcon')\">\n @if (!groupicon()) {\n <svg data-p-icon=\"angle-right\" [pBind]=\"getPTOptions(processedOption, i, 'groupIcon')\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"groupicon()\"></ng-container>\n }\n </span>\n }\n </div>\n @if (shouldRenderSubList(processedOption)) {\n <ul\n pCascadeSelectSub\n [attr.role]=\"'group'\"\n [class]=\"cx('optionList')\"\n [selectId]=\"selectId()\"\n [focusedOptionId]=\"focusedOptionId()\"\n [activeOptionPath]=\"activeOptionPath()\"\n [options]=\"getOptionGroupChildren(processedOption)\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [level]=\"level() + 1\"\n [pMotion]=\"isOptionActive(processedOption)\"\n [pMotionDisabled]=\"cascadeselect.queryMatches()\"\n [pMotionName]=\"'p-anchored-overlay'\"\n [pMotionAppear]=\"true\"\n [pMotionOptions]=\"motionOptions()\"\n (pMotionOnBeforeEnter)=\"onBeforeEnter($event)\"\n (pMotionOnAfterLeave)=\"onNestedAfterLeave(processedOption)\"\n (onChange)=\"onChange.emit($event)\"\n (onFocusChange)=\"onFocusChange.emit($event)\"\n (onFocusEnterChange)=\"onFocusEnterChange.emit($event)\"\n [optionGroupLabel]=\"optionGroupLabel()\"\n [optionGroupChildren]=\"optionGroupChildren()\"\n [dirty]=\"dirty()\"\n [optionTemplate]=\"optionTemplate()\"\n [motionOptions]=\"motionOptions()\"\n [pBind]=\"ptm('optionList')\"\n [pt]=\"pt\"\n [unstyled]=\"unstyled()\"\n ></ul>\n }\n </li>\n }\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [CascadeSelectStyle, { provide: PARENT_INSTANCE, useExisting: CascadeSelectSub }]\n})\nexport class CascadeSelectSub extends BaseComponent {\n selectId = input<string>();\n\n activeOptionPath = input<CascadeSelectProcessedOption[]>();\n\n optionDisabled = input<string>();\n\n focusedOptionId = input<string>();\n\n options = input<CascadeSelectProcessedOption[]>();\n\n optionGroupChildren = input<string[] | string | null>();\n\n optionTemplate = input<TemplateRef<CascadeSelectOptionTemplateContext>>();\n\n groupicon = input<TemplateRef<void>>();\n\n level = input(0, { transform: numberAttribute });\n\n optionLabel = input<string>();\n\n optionValue = input<string>();\n\n optionGroupLabel = input<string>();\n\n dirty = input(false, { transform: booleanAttribute });\n\n root = input(false, { transform: booleanAttribute });\n\n motionOptions = input<MotionOptions>();\n\n renderedKeys = signal<Set<string>>(new Set());\n\n onChange = output<CascadeSelectSubChangeEvent>();\n\n onFocusChange = output<CascadeSelectSubFocusChangeEvent>();\n\n onFocusEnterChange = output<CascadeSelectSubFocusChangeEvent>();\n\n _componentStyle = inject(CascadeSelectStyle);\n\n cascadeselect = inject<CascadeSelect>(CASCADESELECT_INSTANCE);\n\n constructor() {\n super();\n effect(() => {\n const activeKeys = (this.options() ?? []).filter((option) => this.isOptionGroup(option) && this.isOptionActive(option)).map((option) => option.key);\n if (!activeKeys.length) {\n return;\n }\n const current = untracked(this.renderedKeys);\n const added = activeKeys.filter((key) => !current.has(key));\n if (added.length) {\n this.renderedKeys.set(new Set([...current, ...added]));\n }\n });\n }\n\n getPTOptions(processedOption: CascadeSelectProcessedOption, index: number, key: string) {\n return this.ptm(key, {\n context: {\n option: processedOption,\n index,\n level: this.level(),\n optionGroup: this.isOptionGroup(processedOption),\n active: this.isOptionActive(processedOption),\n focused: this.isOptionFocused(processedOption),\n disabled: this.isOptionDisabled(processedOption)\n }\n });\n }\n\n onBeforeEnter(event: MotionEvent) {\n this.positionSubList(event.element as HTMLElement);\n }\n\n onNestedAfterLeave(processedOption: CascadeSelectProcessedOption) {\n if (!this.isOptionActive(processedOption)) {\n const next = new Set(this.renderedKeys());\n next.delete(processedOption.key);\n this.renderedKeys.set(next);\n }\n }\n\n onOptionClick(event: Event, processedOption: CascadeSelectProcessedOption) {\n this.onChange.emit({\n originalEvent: event,\n processedOption,\n isFocus: true\n });\n }\n\n onOptionMouseEnter(event: MouseEvent, processedOption: CascadeSelectProcessedOption) {\n this.onFocusEnterChange.emit({ originalEvent: event, processedOption });\n }\n\n onOptionMouseMove(event: MouseEvent, processedOption: CascadeSelectProcessedOption) {\n this.onFocusChange.emit({ originalEvent: event, processedOption });\n }\n\n getOptionId(processedOption: CascadeSelectProcessedOption) {\n return `${this.selectId()}_${processedOption.key}`;\n }\n\n getOptionLabel(processedOption: CascadeSelectProcessedOption) {\n const optionLabel = this.optionLabel();\n return optionLabel ? resolveFieldData(processedOption.option, optionLabel) : processedOption.option;\n }\n\n getOptionValue(processedOption: CascadeSelectProcessedOption) {\n const optionValue = this.optionValue();\n return optionValue ? resolveFieldData(processedOption.option, optionValue) : processedOption.option;\n }\n\n getOptionLabelToRender(processedOption: CascadeSelectProcessedOption) {\n return this.isOptionGroup(processedOption) ? this.getOptionGroupLabel(processedOption) : this.getOptionLabel(processedOption);\n }\n\n isOptionDisabled(processedOption: CascadeSelectProcessedOption) {\n const optionDisabled = this.optionDisabled();\n return optionDisabled ? resolveFieldData(processedOption.option, optionDisabled) : false;\n }\n\n getOptionGroupLabel(processedOption: CascadeSelectProcessedOption) {\n const optionGroupLabel = this.optionGroupLabel();\n return optionGroupLabel ? resolveFieldData(processedOption.option, optionGroupLabel) : null;\n }\n\n getOptionGroupChildren(processedOption: CascadeSelectProcessedOption) {\n return processedOption.children;\n }\n\n isOptionGroup(processedOption: CascadeSelectProcessedOption) {\n return isNotEmpty(processedOption.children);\n }\n\n isOptionSelected(processedOption: CascadeSelectProcessedOption) {\n return equals(this.cascadeselect?.modelValue(), processedOption?.option);\n }\n\n isOptionActive(processedOption: CascadeSelectProcessedOption) {\n return this.activeOptionPath()?.some((path) => path.key === processedOption.key);\n }\n\n isOptionFocused(processedOption: CascadeSelectProcessedOption) {\n return this.focusedOptionId() === this.getOptionId(processedOption);\n }\n\n shouldRenderSubList(processedOption: CascadeSelectProcessedOption) {\n return this.isOptionGroup(processedOption) && (this.isOptionActive(processedOption) || this.renderedKeys().has(processedOption.key));\n }\n\n getAriaSelected(processedOption: CascadeSelectProcessedOption) {\n return this.isOptionGroup(processedOption) ? undefined : this.isOptionSelected(processedOption);\n }\n\n getOptionTemplateContext(processedOption: CascadeSelectProcessedOption) {\n return { $implicit: processedOption?.option, level: this.level() };\n }\n\n positionSubList(sublist: HTMLElement) {\n if (isPlatformBrowser(this.platformId) && sublist) {\n nestedPosition(sublist, this.level() + 1);\n }\n }\n}\n","import { 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 input,\n NgModule,\n numberAttribute,\n output,\n Provider,\n signal,\n TemplateRef,\n untracked,\n viewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { MotionOptions } from '@primeuix/motion';\nimport { equals, findLastIndex, findSingle, focus, isEmpty, isNotEmpty, isPrintableCharacter, resolveFieldData, uuid } from '@primeuix/utils';\nimport { OverlayOptions, OverlayService, SharedModule, TranslationKeys } from 'primeng/api';\nimport { AutoFocus } from 'primeng/autofocus';\nimport { PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { BaseEditableHolder } from 'primeng/baseeditableholder';\nimport { Bind } from 'primeng/bind';\nimport { Fluid } from 'primeng/fluid';\nimport { ChevronDown as ChevronDownIcon } from '@primeicons/angular/chevron-down';\nimport { Times as TimesIcon } from '@primeicons/angular/times';\nimport { Overlay } from 'primeng/overlay';\nimport type { AppendTo, CSSProperties, InputSize, InputVariant } from 'primeng/types/shared';\nimport { VoidListener } from 'primeng/ts-helpers';\nimport type {\n CascadeSelectBeforeHideEvent,\n CascadeSelectBeforeShowEvent,\n CascadeSelectChangeEvent,\n CascadeSelectFocusedOptionInfo,\n CascadeSelectOptionChangeEvent,\n CascadeSelectOptionClickEvent,\n CascadeSelectOptionMouseEvent,\n CascadeSelectOptionTemplateContext,\n CascadeSelectPassThrough,\n CascadeSelectProcessedOption,\n CascadeSelectShowEvent,\n CascadeSelectValueTemplateContext\n} from 'primeng/types/cascadeselect';\nimport { CascadeSelectSub } from './cascadeselect-sub';\nimport { CASCADESELECT_INSTANCE } from './cascadeselect-token';\nimport { CascadeSelectStyle } from './style/cascadeselectstyle';\n\nexport const CASCADESELECT_VALUE_ACCESSOR: Provider = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => CascadeSelect),\n multi: true\n};\n\n/**\n * CascadeSelect is a form component to select a value from a nested structure of options.\n * @group Components\n */\n@Component({\n selector: 'p-cascadeselect, p-cascade-select',\n standalone: true,\n imports: [NgTemplateOutlet, Overlay, AutoFocus, CascadeSelectSub, ChevronDownIcon, TimesIcon, SharedModule, Bind],\n template: `\n <div class=\"p-hidden-accessible\" [pBind]=\"ptm('hiddenInputWrapper')\">\n <input\n #focusInput\n readonly\n type=\"text\"\n role=\"combobox\"\n [attr.name]=\"name()\"\n [attr.required]=\"requiredAttr()\"\n [attr.disabled]=\"disabledAttr()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.tabindex]=\"$tabindex()\"\n [attr.id]=\"inputId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-haspopup]=\"'tree'\"\n [attr.aria-expanded]=\"ariaExpanded()\"\n [attr.aria-controls]=\"ariaControls()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n (focus)=\"onInputFocus($event)\"\n (blur)=\"onInputBlur($event)\"\n (keydown)=\"onInputKeyDown($event)\"\n [pAutoFocus]=\"autofocus()\"\n [pBind]=\"ptm('hiddenInput')\"\n />\n </div>\n <span [class]=\"cx('label')\" [pBind]=\"ptm('label')\">\n @if (valueTemplate()) {\n <ng-container *ngTemplateOutlet=\"valueTemplate(); context: valueTemplateContext()\"></ng-container>\n } @else {\n {{ label() }}\n }\n </span>\n\n @if (showClearIcon()) {\n @if (!clearIconTemplate()) {\n <svg data-p-icon=\"times\" [class]=\"cx('clearIcon')\" (click)=\"clear($event)\" [pBind]=\"ptm('clearIcon')\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\" />\n } @else {\n <span [class]=\"cx('clearIcon')\" (click)=\"clear($event)\" [pBind]=\"ptm('clearIcon')\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\">\n <ng-container *ngTemplateOutlet=\"clearIconTemplate()\"></ng-container>\n </span>\n }\n }\n\n <div [class]=\"cx('dropdown')\" role=\"button\" aria-haspopup=\"listbox\" [attr.aria-expanded]=\"ariaExpanded()\" [pBind]=\"ptm('dropdown')\" [attr.aria-hidden]=\"true\">\n @if (loading()) {\n @if (loadingIconTemplate()) {\n <ng-container *ngTemplateOutlet=\"loadingIconTemplate()\"></ng-container>\n } @else {\n <span [class]=\"loadingIconClass()\" aria-hidden=\"true\" [pBind]=\"ptm('loadingIcon')\"></span>\n }\n } @else {\n @if (!triggerIconTemplate()) {\n <svg data-p-icon=\"chevron-down\" [class]=\"cx('dropdownIcon')\" [pBind]=\"ptm('dropdownIcon')\" />\n } @else {\n <span [class]=\"cx('dropdownIcon')\" [pBind]=\"ptm('dropdownIcon')\">\n <ng-container *ngTemplateOutlet=\"triggerIconTemplate()\"></ng-container>\n </span>\n }\n }\n </div>\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" [pBind]=\"ptm('hiddenSearchResult')\">\n {{ searchResultMessageText }}\n </span>\n <p-overlay\n #overlay\n [hostAttrSelector]=\"$attrSelector\"\n [(visible)]=\"overlayVisible\"\n [options]=\"overlayOptions()\"\n [target]=\"'@parent'\"\n [appendTo]=\"$appendTo()\"\n [unstyled]=\"unstyled()\"\n [pt]=\"ptm('pcOverlay')\"\n [motionOptions]=\"motionOptions()\"\n (onAfterLeave)=\"onOverlayAfterLeave()\"\n (onBeforeShow)=\"onBeforeShow.emit($event)\"\n (onShow)=\"show($event)\"\n (onBeforeHide)=\"onBeforeHide.emit($event)\"\n (onHide)=\"hide($event)\"\n >\n <ng-template #content>\n <div #panel [class]=\"cn(cx('overlay'), panelStyleClass())\" [style]=\"panelStyle()\" [pBind]=\"ptm('overlay')\">\n @if (headerTemplate()) {\n <ng-container *ngTemplateOutlet=\"headerTemplate()\"></ng-container>\n }\n <div [class]=\"cx('listContainer')\" [pBind]=\"ptm('listContainer')\">\n <ul\n pCascadeSelectSub\n [class]=\"cx('list')\"\n [options]=\"processedOptions\"\n [selectId]=\"$id()\"\n [focusedOptionId]=\"focusedOptionIdValue()\"\n [activeOptionPath]=\"activeOptionPath()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [level]=\"0\"\n [optionTemplate]=\"optionTemplate()\"\n [groupicon]=\"groupIconTemplate()\"\n [optionGroupLabel]=\"optionGroupLabel()\"\n [optionGroupChildren]=\"optionGroupChildren()\"\n [optionDisabled]=\"optionDisabled()\"\n [root]=\"true\"\n [motionOptions]=\"motionOptions()\"\n (onChange)=\"onOptionClick($event)\"\n (onFocusChange)=\"onOptionMouseMove($event)\"\n (onFocusEnterChange)=\"onOptionMouseEnter($event)\"\n [dirty]=\"dirty()\"\n [attr.role]=\"'tree'\"\n [attr.aria-orientation]=\"'horizontal'\"\n [pBind]=\"ptm('list')\"\n [attr.aria-label]=\"listLabel\"\n [pt]=\"pt\"\n [unstyled]=\"unstyled()\"\n ></ul>\n </div>\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" [pBind]=\"ptm('selectedMessageText')\">\n {{ selectedMessageText }}\n </span>\n @if (footerTemplate()) {\n <ng-container *ngTemplateOutlet=\"footerTemplate()\"></ng-container>\n }\n </div>\n </ng-template>\n </p-overlay>\n `,\n providers: [CASCADESELECT_VALUE_ACCESSOR, CascadeSelectStyle, { provide: PARENT_INSTANCE, useExisting: CascadeSelect }, { provide: CASCADESELECT_INSTANCE, useExisting: CascadeSelect }],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': \"cx('root')\",\n '[style]': \"sx('root')\"\n },\n hostDirectives: [Bind]\n})\nexport class CascadeSelect extends BaseEditableHolder<CascadeSelectPassThrough> {\n componentName = 'CascadeSelect';\n\n $pcCascadeSelect: CascadeSelect | undefined = inject(CASCADESELECT_INSTANCE, { optional: true, skipSelf: true }) ?? undefined;\n\n bindDirectiveInstance = inject(Bind, { self: true });\n\n onAfterViewChecked(): void {\n this.bindDirectiveInstance.setAttrs(this.ptms(['host', 'root']));\n }\n\n private _internalId = uuid('pn_id_');\n\n /**\n * Unique identifier of the component\n * @group Props\n */\n id = input<string>();\n\n $id = computed(() => this.id() || this._internalId);\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 there is no data. Defaults to global value in i18n translation configuration.\n * @group Props\n */\n emptyMessage = 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 * Text to display when filtering does not return any results. Defaults to value from PrimeNG locale configuration.\n * @group Props\n * @defaultValue 'No available options'\n */\n emptySearchMessage = 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 * Locale to use in searching. The default locale is the host environment's current locale.\n * @group Props\n */\n searchLocale = input<string>();\n /**\n * Name of the disabled field of an option.\n * @group Props\n */\n optionDisabled = input<string>();\n /**\n * Fields used when filtering the options, defaults to optionLabel.\n * @group Props\n */\n focusOnHover = input(true, { transform: booleanAttribute });\n /**\n * Determines if the option will be selected on focus.\n * @group Props\n */\n selectOnFocus = input(false, { transform: booleanAttribute });\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(false, { transform: booleanAttribute });\n /**\n * An array of selectitems to display as the available options.\n * @group Props\n */\n options = input<string[] | string>();\n /**\n * Property name or getter function to use as the label of an option.\n * @group Props\n */\n optionLabel = input<string>();\n /**\n * Property name or getter function to use as the value of an option, defaults to the option itself when not defined.\n * @group Props\n */\n optionValue = input<string>();\n /**\n * Property name or getter function to use as the label of an option group.\n * @group Props\n */\n optionGroupLabel = input<string>();\n /**\n * Property name or getter function to retrieve the items of a group.\n * @group Props\n */\n optionGroupChildren = input<string[] | string | null>();\n /**\n * Default text to display when no option is selected.\n * @group Props\n */\n placeholder = input<string>();\n /**\n * Selected value of the component.\n * @group Props\n */\n value = signal<unknown>(undefined);\n /**\n * A property to uniquely identify an option.\n * @group Props\n */\n dataKey = input<string>();\n /**\n * Identifier of the underlying input element.\n * @group Props\n */\n inputId = input<string>();\n /**\n * Index of the element in tabbing order.\n * @group Props\n */\n tabindex = input(0, { transform: numberAttribute });\n /**\n * Establishes relationships between the component and label(s) where its value should be one or more element IDs.\n * @group Props\n */\n ariaLabelledBy = input<string>();\n /**\n * Label of the input for accessibility.\n * @group Props\n */\n inputLabel = input<string>();\n /**\n * Defines a string that labels the input for accessibility.\n * @group Props\n */\n ariaLabel = input<string>();\n /**\n * When enabled, a clear icon is displayed to clear the value.\n * @group Props\n */\n showClear = input(false, { transform: booleanAttribute });\n /**\n * Style class of the overlay panel.\n * @group Props\n */\n panelStyleClass = input<string>();\n /**\n * Inline style of the overlay panel.\n * @group Props\n */\n panelStyle = input<CSSProperties>();\n /**\n * Whether to use overlay API feature. The properties of overlay API can be used like an object in it.\n * @group Props\n */\n overlayOptions = input<OverlayOptions>();\n /**\n * When present, it specifies that the component should automatically get focus on load.\n * @group Props\n */\n autofocus = input(false, { transform: booleanAttribute });\n /**\n * Whether the dropdown is in loading state.\n * @group Props\n */\n loading = input(false, { transform: booleanAttribute });\n /**\n * Icon to display in loading state.\n * @group Props\n */\n loadingIcon = input<string>();\n /**\n * The breakpoint to define the maximum width boundary.\n * @group Props\n */\n breakpoint = input('960px');\n /**\n * Specifies the size of the component.\n * @defaultValue undefined\n * @group Props\n */\n size = input<InputSize>();\n /**\n * Specifies the input variant of the component.\n * @defaultValue undefined\n * @group Props\n */\n variant = input<InputVariant>();\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 * 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).\n * @defaultValue 'self'\n * @group Props\n */\n appendTo = input<AppendTo>(undefined);\n /**\n * The motion options.\n * @group Props\n */\n motionOptions = input<MotionOptions | undefined>(undefined);\n /**\n * Callback to invoke on value change.\n * @param {CascadeSelectChangeEvent} event - Custom change event.\n * @group Emits\n */\n onChange = output<CascadeSelectChangeEvent>();\n /**\n * Callback to invoke when a group changes.\n * @group Emits\n */\n onGroupChange = output<{ originalEvent: Event; value?: unknown; isFocus?: boolean }>();\n /**\n * Callback to invoke when the overlay is shown.\n * @param {CascadeSelectShowEvent} event - Custom overlay show event.\n * @group Emits\n */\n onShow = output<CascadeSelectShowEvent | undefined>();\n /**\n * Callback to invoke when the overlay is hidden.\n * @group Emits\n */\n onHide = output<any>();\n /**\n * Callback to invoke when the clear token is clicked.\n * @group Emits\n */\n onClear = output<MouseEvent | undefined>();\n /**\n * Callback to invoke before overlay is shown.\n * @param {CascadeSelectBeforeShowEvent} event - Custom overlay show event.\n * @group Emits\n */\n onBeforeShow = output<CascadeSelectBeforeShowEvent>();\n /**\n * Callback to invoke before overlay is hidden.\n * @param {CascadeSelectBeforeHideEvent} event - Custom overlay hide event.\n * @group Emits\n */\n onBeforeHide = output<CascadeSelectBeforeHideEvent>();\n /**\n * Callback to invoke when input receives focus.\n * @param {FocusEvent} event - Focus event.\n * @group Emits\n */\n onFocus = output<FocusEvent>();\n /**\n * Callback to invoke when input loses focus.\n * @param {FocusEvent} event - Focus event.\n * @group Emits\n */\n onBlur = output<FocusEvent>();\n\n focusInputViewChild = viewChild<ElementRef>('focusInput');\n\n panelViewChild = viewChild<ElementRef>('panel');\n\n overlayViewChild = viewChild<Overlay>('overlay');\n\n /**\n * Custom value template.\n * @group Templates\n */\n valueTemplate = contentChild<TemplateRef<CascadeSelectValueTemplateContext>>('value', { descendants: false });\n\n /**\n * Custom option template.\n * @group Templates\n */\n optionTemplate = contentChild<TemplateRef<CascadeSelectOptionTemplateContext>>('option', { descendants: false });\n\n /**\n * Custom header template.\n * @group Templates\n */\n headerTemplate = contentChild<TemplateRef<void>>('header', { descendants: false });\n\n /**\n * Custom footer template.\n * @group Templates\n */\n footerTemplate = contentChild<TemplateRef<void>>('footer', { descendants: false });\n\n /**\n * Custom trigger icon template.\n * @group Templates\n */\n triggerIconTemplate = contentChild<TemplateRef<void>>('triggericon', { descendants: false });\n\n /**\n * Custom loading icon template.\n * @group Templates\n */\n loadingIconTemplate = contentChild<TemplateRef<void>>('loadingicon', { descendants: false });\n\n /**\n * Custom option group icon template.\n * @group Templates\n */\n groupIconTemplate = contentChild<TemplateRef<void>>('optiongroupicon', { descendants: false });\n\n /**\n * Custom clear icon template.\n * @group Templates\n */\n clearIconTemplate = contentChild<TemplateRef<void>>('clearicon', { descendants: false });\n\n selectionPath: CascadeSelectProcessedOption[] | null = null;\n\n focused = signal(false);\n\n overlayVisible = signal(false);\n\n clicked = signal(false);\n\n dirty = signal(false);\n\n searchValue: string | undefined;\n\n searchTimeout: ReturnType<typeof setTimeout> | undefined;\n\n hideTimeout: ReturnType<typeof setTimeout> | undefined;\n\n focusedOptionInfo = signal<CascadeSelectFocusedOptionInfo>({ index: -1, level: 0, parentKey: '' });\n\n activeOptionPath = signal<CascadeSelectProcessedOption[]>([]);\n\n processedOptions: CascadeSelectProcessedOption[] = [];\n\n _componentStyle = inject(CascadeSelectStyle);\n\n initialized = signal(false);\n\n $variant = computed(() => this.variant() || this.config.inputVariant());\n\n $appendTo = computed(() => this.appendTo() || this.config.overlayAppendTo());\n\n valueTemplateContext = computed(() => ({ $implicit: this.value(), placeholder: this.placeholder() }));\n\n $tabindex = computed(() => (!this.$disabled() ? this.tabindex() : -1));\n\n requiredAttr = computed(() => (this.required() ? '' : undefined));\n\n disabledAttr = computed(() => (this.$disabled() ? '' : undefined));\n\n ariaExpanded = computed(() => this.overlayVisible() ?? false);\n\n ariaControls = computed(() => (this.overlayVisible() ? this.$id() + '_tree' : null));\n\n ariaActiveDescendant = computed(() => (this.focused() ? this.focusedOptionId : undefined));\n\n showClearIcon = computed(() => this.$filled() && !this.$disabled() && this.showClear());\n\n focusedOptionIdValue = computed(() => (this.focused() ? this.focusedOptionId : undefined));\n\n loadingIconClass = computed(() => {\n const icon = this.loadingIcon();\n if (icon) {\n return this.cn(this.cx('loadingIcon'), icon + 'pi-spin');\n }\n return this.cn(this.cx('loadingIcon'), icon + ' pi pi-spinner pi-spin');\n });\n\n pcFluid: Fluid | null = inject(Fluid, { optional: true, host: true, skipSelf: true });\n\n get hasFluid() {\n return this.fluid() ?? !!this.pcFluid;\n }\n\n @HostListener('mousedown', ['$event'])\n onHostClick(event: MouseEvent) {\n this.onContainerClick(event);\n }\n\n get listLabel(): string {\n return this.translate(TranslationKeys.ARIA, 'listLabel');\n }\n\n get focusedOptionId() {\n return this.focusedOptionInfo().index !== -1 ? `${this.$id()}${isNotEmpty(this.focusedOptionInfo().parentKey) ? '_' + this.focusedOptionInfo().parentKey : ''}_${this.focusedOptionInfo().index}` : null;\n }\n\n get searchResultMessageText() {\n return isNotEmpty(this.visibleOptions()) ? this.searchMessageText.replaceAll('{0}', String(this.visibleOptions().length)) : this.emptySearchMessageText;\n }\n\n get searchMessageText() {\n return this.searchMessage() || this.config.translation.searchMessage || '';\n }\n\n get emptySearchMessageText() {\n return this.emptySearchMessage() || this.config.translation.emptySearchMessage || '';\n }\n\n get emptyMessageText() {\n return this.emptyMessage() || this.config.translation.emptyMessage || '';\n }\n\n get selectionMessageText() {\n return this.selectionMessage() || this.config.translation.selectionMessage || '';\n }\n\n get emptySelectionMessageText() {\n return this.emptySelectionMessage() || this.config.translation.emptySelectionMessage || '';\n }\n\n get selectedMessageText() {\n return this.hasSelectedOption() ? this.selectionMessageText.replaceAll('{0}', '1') : this.emptySelectionMessageText;\n }\n\n visibleOptions = computed(() => {\n const processedOption = this.activeOptionPath().find((p) => p.key === this.focusedOptionInfo().parentKey);\n\n return processedOption ? (processedOption.children ?? []) : this.processedOptions;\n });\n\n label = computed(() => {\n const label = this.placeholder() || 'p-emptylabel';\n\n if (this.hasSelectedOption()) {\n const activeOptionPath = this.findOptionPathByValue(this.modelValue(), null);\n const processedOption = isNotEmpty(activeOptionPath) ? activeOptionPath[activeOptionPath.length - 1] : null;\n\n return processedOption ? this.getOptionLabel(processedOption.option) : label;\n }\n return label;\n });\n\n hasSelectedOption() {\n return isNotEmpty(this.modelValue());\n }\n\n createProcessedOptions(options: unknown[] | undefined, level = 0, parent: CascadeSelectProcessedOption | Record<string, unknown> = {}, parentKey = ''): CascadeSelectProcessedOption[] {\n const processedOptions: CascadeSelectProcessedOption[] = [];\n\n options &&\n options.forEach((option, index) => {\n const key = (parentKey !== '' ? parentKey + '_' : '') + index;\n const newOption: CascadeSelectProcessedOption = {\n option,\n index,\n level,\n key,\n parent,\n parentKey\n };\n\n newOption.children = this.createProcessedOptions(this.getOptionGroupChildren(option, level), level + 1, newOption, key);\n processedOptions.push(newOption);\n });\n\n return processedOptions;\n }\n\n onInputFocus(event: FocusEvent) {\n if (this.$disabled()) {\n // For screenreaders\n return;\n }\n\n this.focused.set(true);\n this.onFocus.emit(event);\n }\n\n onInputBlur(event: FocusEvent) {\n this.focused.set(false);\n this.focusedOptionInfo.set({ index: -1, level: 0, parentKey: '' });\n this.searchValue = '';\n this.onModelTouched();\n this.onBlur.emit(event);\n }\n\n onInputKeyDown(event: KeyboardEvent) {\n if (this.$disabled() || this.loading()) {\n event.preventDefault();\n\n return;\n }\n\n const metaKey = event.metaKey || event.ctrlKey;\n\n switch (event.code) {\n case 'ArrowDown':\n this.onArrowDownKey(event);\n break;\n\n case 'ArrowUp':\n this.onArrowUpKey(event);\n break;\n\n case 'ArrowLeft':\n this.onArrowLeftKey(event);\n break;\n\n case 'ArrowRight':\n this.onArrowRightKey(event);\n break;\n\n case 'Home':\n this.onHomeKey(event);\n break;\n\n case 'End':\n this.onEndKey(event);\n break;\n\n case 'Space':\n this.onSpaceKey(event);\n break;\n\n case 'Enter':\n case 'NumpadEnter':\n this.onEnterKey(event);\n break;\n\n case 'Escape':\n this.onEscapeKey(event);\n break;\n\n case 'Tab':\n this.onTabKey(event);\n break;\n\n case 'Backspace':\n this.onBackspaceKey(event);\n break;\n\n case 'PageDown':\n case 'PageUp':\n case 'ShiftLeft':\n case 'ShiftRight':\n //NOOP\n break;\n\n default:\n if (!metaKey && isPrintableCharacter(event.key)) {\n !this.overlayVisible() && this.show();\n this.searchOptions(event, event.key);\n }\n\n break;\n }\n\n this.clicked.set(false);\n }\n\n onArrowDownKey(event: KeyboardEvent) {\n if (!this.overlayVisible()) {\n this.show();\n } else {\n const optionIndex = this.focusedOptionInfo().index !== -1 ? this.findNextOptionIndex(this.focusedOptionInfo().index) : this.clicked() ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();\n\n this.changeFocusedOptionIndex(event, optionIndex, true);\n }\n\n event.preventDefault();\n }\n\n onArrowUpKey(event: KeyboardEvent) {\n if (event.altKey) {\n if (this.focusedOptionInfo().index !== -1) {\n const processedOption = this.visibleOptions()[this.focusedOptionInfo().index];\n const grouped = this.isProccessedOptionGroup(processedOption);\n\n !grouped && this.onOptionChange({ originalEvent: event, processedOption });\n }\n\n this.overlayVisible() && this.hide();\n event.preventDefault();\n } else {\n const optionIndex = this.focusedOptionInfo().index !== -1 ? this.findPrevOptionIndex(this.focusedOptionInfo().index) : this.clicked() ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();\n\n this.changeFocusedOptionIndex(event, optionIndex, true);\n\n !this.overlayVisible() && this.show();\n event.preventDefault();\n }\n }\n\n onArrowLeftKey(event: KeyboardEvent) {\n if (this.overlayVisible()) {\n const processedOption = this.visibleOptions()[this.focusedOptionInfo().index];\n const parentOption = this.activeOptionPath().find((p) => p.key === processedOption.parentKey);\n const matched = this.focusedOptionInfo().parentKey === '' || (parentOption && parentOption.key === this.focusedOptionInfo().parentKey);\n const root = isEmpty(processedOption.parent);\n\n if (matched) {\n const activeOptionPath = this.activeOptionPath().filter((p) => p.parentKey !== this.focusedOptionInfo().parentKey);\n this.activeOptionPath.set(activeOptionPath);\n }\n\n if (!root) {\n this.focusedOptionInfo.set({ index: -1, parentKey: parentOption ? parentOption.parentKey : '' });\n this.searchValue = '';\n this.onArrowDownKey(event);\n }\n\n event.preventDefault();\n }\n }\n\n onArrowRightKey(event: KeyboardEvent) {\n if (this.overlayVisible()) {\n const processedOption = this.visibleOptions()[this.focusedOptionInfo().index];\n const grouped = this.isProccessedOptionGroup(processedOption);\n\n if (grouped) {\n const matched = this.activeOptionPath().some((p) => processedOption.key === p.key);\n\n if (matched) {\n this.focusedOptionInfo.set({ index: -1, parentKey: processedOption.key });\n this.searchValue = '';\n this.onArrowDownKey(event);\n } else {\n this.onOptionChange({ originalEvent: event, processedOption });\n }\n }\n\n event.preventDefault();\n }\n }\n\n onHomeKey(event: KeyboardEvent) {\n this.changeFocusedOptionIndex(event, this.findFirstOptionIndex());\n\n !this.overlayVisible() && this.show();\n event.preventDefault();\n }\n\n onEndKey(event: KeyboardEvent) {\n this.changeFocusedOptionIndex(event, this.findLastOptionIndex());\n\n !this.overlayVisible() && this.show();\n event.preventDefault();\n }\n\n onEnterKey(event: KeyboardEvent) {\n if (!this.overlayVisible()) {\n this.focusedOptionInfo.set({ ...this.focusedOptionInfo(), index: -1 }); // reset\n this.onArrowDownKey(event);\n } else {\n if (this.focusedOptionInfo().index !== -1) {\n const processedOption = this.visibleOptions()[this.focusedOptionInfo().index];\n const grouped = this.isProccessedOptionGroup(processedOption);\n\n this.onOptionClick({ originalEvent: event, processedOption });\n !grouped && this.hide();\n }\n }\n\n event.preventDefault();\n }\n\n onSpaceKey(event: KeyboardEvent) {\n this.onEnterKey(event);\n }\n\n onEscapeKey(event: KeyboardEvent) {\n this.overlayVisible() && this.hide(event, true);\n event.preventDefault();\n }\n\n onTabKey(event: KeyboardEvent) {\n if (this.focusedOptionInfo().index !== -1) {\n const processedOption = this.visibleOptions()[this.focusedOptionInfo().index];\n const grouped = this.isProccessedOptionGroup(processedOption);\n\n !grouped && this.onOptionChange({ originalEvent: event, processedOption });\n }\n\n this.overlayVisible() && this.hide();\n }\n\n onBackspaceKey(event: KeyboardEvent) {\n if (isNotEmpty(this.modelValue()) && this.showClear()) {\n this.clear();\n }\n\n event.stopPropagation();\n }\n\n equalityKey() {\n return this.optionValue() ? undefined : this.dataKey();\n }\n\n updateModel(value: unknown, event?: Event) {\n this.value.set(value);\n this.onModelChange(value);\n this.writeModelValue(value);\n\n if (this.initialized()) {\n this.onChange.emit({\n originalEvent: event,\n value: value\n });\n }\n }\n\n autoUpdateModel() {\n if (this.selectOnFocus() && this.autoOptionFocus() && !this.hasSelectedOption()) {\n this.focusedOptionInfo().index = this.findFirstFocusedOptionIndex();\n this.onOptionChange({\n originalEvent: null,\n processedOption: this.visibleOptions()[this.focusedOptionInfo().index],\n isHide: false\n });\n\n !this.overlayVisible() && this.focusedOptionInfo.set(