primeng
Version:
PrimeNG is an open source UI library for Angular featuring a rich set of 80+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeB
1 lines • 121 kB
Source Map (JSON)
{"version":3,"file":"primeng-cascadeselect.mjs","sources":["../../src/cascadeselect/style/cascadeselectstyle.ts","../../src/cascadeselect/cascadeselect.ts","../../src/cascadeselect/primeng-cascadeselect.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BaseStyle } from 'primeng/base';\n\nconst theme = ({ dt }) => `\n.p-cascadeselect {\n display: inline-flex;\n cursor: pointer;\n position: relative;\n user-select: none;\n background: ${dt('cascadeselect.background')};\n border: 1px solid ${dt('cascadeselect.border.color')};\n transition: background ${dt('cascadeselect.transition.duration')}, color ${dt('cascadeselect.transition.duration')}, border-color ${dt('cascadeselect.transition.duration')}, outline-color ${dt(\n 'cascadeselect.transition.duration'\n )}, box-shadow ${dt('cascadeselect.transition.duration')};\n border-radius: ${dt('cascadeselect.border.radius')};\n outline-color: transparent;\n box-shadow: ${dt('cascadeselect.shadow')};\n}\n\np-cascadeselect.ng-invalid.ng-dirty .p-cascadeselect {\n border-color: ${dt('cascadeselect.invalid.border.color')};\n}\n\np-cascadeselect.ng-invalid.ng-dirty .p-cascadeselect.p-focus {\n border-color: ${dt('cascadeselect.focus.border.color')};\n}\n\n.p-cascadeselect:not(.p-disabled):hover {\n border-color: ${dt('cascadeselect.hover.border.color')};\n}\n\n.p-cascadeselect:not(.p-disabled).p-focus {\n border-color: ${dt('cascadeselect.focus.border.color')};\n box-shadow: ${dt('cascadeselect.focus.ring.shadow')};\n outline: ${dt('cascadeselect.focus.ring.width')} ${dt('cascadeselect.focus.ring.style')} ${dt('cascadeselect.focus.ring.color')};\n outline-offset: ${dt('multiscascadeselectelect.focus.ring.offset')};\n}\n\n.p-cascadeselect.p-variant-filled {\n background: ${dt('cascadeselect.filled.background')};\n}\n\n.p-cascadeselect.p-variant-filled:not(.p-disabled):hover {\n background: ${dt('cascadeselect.filled.hover.background')};\n}\n\n.p-cascadeselect.p-variant-filled.p-focus {\n background: ${dt('cascadeselect.filled.focus.background')};\n}\n\n.p-cascadeselect.p-disabled {\n opacity: 1;\n background: ${dt('cascadeselect.disabled.background')};\n}\n\n.p-cascadeselect-dropdown {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: transparent;\n color: ${dt('cascadeselect.dropdown.color')};\n width: ${dt('cascadeselect.dropdown.width')};\n border-start-end-radius: ${dt('border.radius.md')};\n border-end-end-radius: ${dt('border.radius.md')};\n}\n\n.p-cascadeselect-label {\n display: block;\n white-space: nowrap;\n overflow: hidden;\n flex: 1 1 auto;\n width: 1%;\n text-overflow: ellipsis;\n cursor: pointer;\n padding: ${dt('cascadeselect.padding.y')} ${dt('cascadeselect.padding.x')};\n background: transparent;\n border: 0 none;\n outline: 0 none;\n}\n\n.p-cascadeselect-label.p-placeholder {\n color: ${dt('cascadeselect.placeholder.color')};\n}\n\np-cascadeselect.ng-invalid.ng-dirty .p-cascadeselect-label.p-placeholder {\n color: ${dt('cascadeselect.invalid.placeholder.color')};\n}\n\n.p-cascadeselect.p-disabled .p-cascadeselect-label {\n color: ${dt('cascadeselect.disabled.color')};\n}\n\n.p-cascadeselect-label-empty {\n overflow: hidden;\n visibility: hidden;\n}\n\n.p-cascadeselect-fluid {\n display: flex;\n}\n\n.p-cascadeselect-fluid .p-cascadeselect-label {\n width: 1%;\n}\n\n.p-cascadeselect-overlay {\n background: ${dt('cascadeselect.overlay.background')};\n color: ${dt('cascadeselect.overlay.color')};\n border: 1px solid ${dt('cascadeselect.overlay.border.color')};\n border-radius: ${dt('cascadeselect.overlay.border.radius')};\n box-shadow: ${dt('cascadeselect.overlay.shadow')};\n}\n\n.p-cascadeselect .p-cascadeselect-overlay {\n min-width: 100%;\n}\n\n.p-cascadeselect-option-list {\n display: none;\n min-width: 100%;\n position: absolute;\n z-index: 1;\n}\n\n.p-cascadeselect-list {\n min-width: 100%;\n margin: 0;\n padding: 0;\n list-style-type: none;\n padding: ${dt('cascadeselect.list.padding')};\n display: flex;\n flex-direction: column;\n gap: ${dt('cascadeselect.list.gap')}\n}\n\n.p-cascadeselect-option {\n cursor: pointer;\n font-weight: normal;\n white-space: nowrap;\n border: 0 none;\n color: ${dt('cascadeselect.option.color')};\n background: transparent;\n transition: background ${dt('cascadeselect.transition.duration')}, color ${dt('cascadeselect.transition.duration')}, border-color ${dt('cascadeselect.transition.duration')}, box-shadow ${dt(\n 'cascadeselect.transition.duration'\n )}, outline-color ${dt('cascadeselect.transition.duration')};\n border-radius: ${dt('cascadeselect.option.border.radius')};\n}\n\n.p-cascadeselect-option-active {\n overflow: visible;\n}\n\n.p-cascadeselect-option-active > .p-cascadeselect-option-content {\n background: ${dt('cascadeselect.option.focus.background')};\n color: ${dt('cascadeselect.option.focus.color')};\n}\n\n.p-cascadeselect-option:not(.p-cascadeselect-option-selected):not(.p-disabled).p-focus > .p-cascadeselect-option-content {\n background: ${dt('cascadeselect.option.focus.background')};\n color: ${dt('cascadeselect.option.focus.color')};\n}\n\n.p-cascadeselect-option:not(.p-cascadeselect-option-selected):not(.p-disabled).p-focus > .p-cascadeselect-option-content > .p-cascadeselect-group-icon-container > .p-cascadeselect-group-icon {\n color: ${dt('cascadeselect.option.icon.focus.color')};\n}\n\n.p-cascadeselect-option-selected > .p-cascadeselect-option-content {\n background: ${dt('cascadeselect.option.selected.background')};\n color: ${dt('cascadeselect.option.selected.color')};\n}\n\n.p-cascadeselect-option-selected.p-focus > .p-cascadeselect-option-content {\n background: ${dt('cascadeselect.option.selected.focus.background')};\n color: ${dt('cascadeselect.option.selected.focus.color')};\n}\n\n.p-cascadeselect-option-active > .p-cascadeselect-option-list {\n inset-inline-start: 100%;\n top: 0;\n}\n\n.p-cascadeselect-option-content {\n display: flex;\n align-items: center;\n justify-content: space-between;\n overflow: hidden;\n position: relative;\n padding: ${dt('cascadeselect.option.padding')};\n border-radius: ${dt('cascadeselect.option.border.radius')};\n transition: background ${dt('cascadeselect.transition.duration')}, color ${dt('cascadeselect.transition.duration')}, border-color ${dt('cascadeselect.transition.duration')}, box-shadow ${dt(\n 'cascadeselect.transition.duration'\n )}, outline-color ${dt('cascadeselect.transition.duration')};\n}\n\n.p-cascadeselect-group-icon {\n font-size: ${dt('cascadeselect.option.icon.size')};\n width: ${dt('cascadeselect.option.icon.size')};\n height: ${dt('cascadeselect.option.icon.size')};\n color: ${dt('cascadeselect.option.icon.color')};\n}\n\n.p-cascadeselect-group-icon:dir(rtl) {\n transform: rotate(180deg);\n}\n\n.p-cascadeselect-mobile-active .p-cascadeselect-option-list {\n position: static;\n box-shadow: none;\n border: 0 none;\n padding-inline-start: 1rem;\n padding-inline-end: 0;\n}\n\n.p-cascadeselect-mobile-active .p-cascadeselect-group-icon {\n transition: transform 0.2s;\n transform: rotate(90deg);\n}\n\n.p-cascadeselect-mobile-active .p-cascadeselect-option-active > .p-cascadeselect-option-content .p-cascadeselect-group-icon {\n transform: rotate(-90deg);\n}\n\n.p-cascadeselect-sm .p-cascadeselect-label {\n font-size: ${dt('cascadeselect.sm.font.size')};\n padding-block: ${dt('cascadeselect.sm.padding.y')};\n padding-inline: ${dt('cascadeselect.sm.padding.x')};\n}\n\n.p-cascadeselect-sm .p-cascadeselect-dropdown .p-icon {\n font-size: ${dt('cascadeselect.sm.font.size')};\n width: ${dt('cascadeselect.sm.font.size')};\n height: ${dt('cascadeselect.sm.font.size')};\n}\n\n.p-cascadeselect-lg .p-cascadeselect-label {\n font-size: ${dt('cascadeselect.lg.font.size')};\n padding-block: ${dt('cascadeselect.lg.padding.y')};\n padding-inline: ${dt('cascadeselect.lg.padding.x')};\n}\n\n.p-cascadeselect-lg .p-cascadeselect-dropdown .p-icon {\n font-size: ${dt('cascadeselect.lg.font.size')};\n width: ${dt('cascadeselect.lg.font.size')};\n height: ${dt('cascadeselect.lg.font.size')};\n}\n\n/* For PrimeNG */\n.p-cascadeselect-clear-icon {\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: transparent;\n color: ${dt('cascadeselect.clear.icon.color')};\n}`;\n\nconst inlineStyles = {\n root: ({ props }) => ({ position: props.appendTo === 'self' ? 'relative' : undefined })\n};\n\nconst classes = {\n root: ({ instance, props }) => [\n 'p-cascadeselect p-component p-inputwrapper',\n {\n 'p-cascadeselect-mobile': instance.queryMatches(),\n 'p-disabled': props.disabled,\n 'p-invalid': props.invalid,\n 'p-variant-filled': props.variant ? props.variant === 'filled' : instance.config.inputStyle === 'filled' || instance.config.inputVariant === 'filled',\n 'p-focus': instance.focused,\n 'p-inputwrapper-filled': props.modelValue,\n 'p-inputwrapper-focus': instance.focused || instance.overlayVisible,\n 'p-cascadeselect-open': instance.overlayVisible,\n 'p-cascadeselect-fluid': props.fluid,\n 'p-cascadeselect-sm p-inputfield-sm': props.size === 'small',\n 'p-cascadeselect-lg p-inputfield-lg': props.size === 'large'\n }\n ],\n label: ({ instance, props }) => [\n 'p-cascadeselect-label',\n {\n 'p-placeholder': instance.label === props.placeholder,\n 'p-cascadeselect-label-empty': !instance.$slots['value'] && (instance.label === 'p-emptylabel' || instance.label.length === 0)\n }\n ],\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',\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-overlay p-cascadeselect-option-list'\n};\n\n@Injectable()\nexport class CascadeSelectStyle extends BaseStyle {\n name = 'cascadeselect';\n\n theme = theme;\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 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 { CommonModule } from '@angular/common';\nimport {\n AfterContentInit,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ContentChild,\n ContentChildren,\n effect,\n ElementRef,\n EventEmitter,\n forwardRef,\n inject,\n Input,\n NgModule,\n numberAttribute,\n OnInit,\n Output,\n QueryList,\n signal,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { calculateScrollbarWidth, equals, findLastIndex, findSingle, focus, getHiddenElementOuterWidth, getOffset, getOuterWidth, getViewport, isEmpty, isNotEmpty, isPrintableCharacter, resolveFieldData, uuid } from '@primeuix/utils';\nimport { OverlayOptions, OverlayService, PrimeTemplate, SharedModule, TranslationKeys } from 'primeng/api';\nimport { AutoFocus } from 'primeng/autofocus';\nimport { BaseComponent } from 'primeng/basecomponent';\nimport { AngleRightIcon, ChevronDownIcon, TimesIcon } from 'primeng/icons';\nimport { Overlay } from 'primeng/overlay';\nimport { Ripple } from 'primeng/ripple';\nimport { Nullable, VoidListener } from 'primeng/ts-helpers';\nimport { CascadeSelectBeforeHideEvent, CascadeSelectBeforeShowEvent, CascadeSelectChangeEvent, CascadeSelectHideEvent, CascadeSelectShowEvent } from './cascadeselect.interface';\nimport { CascadeSelectStyle } from './style/cascadeselectstyle';\n\nexport const CASCADESELECT_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => CascadeSelect),\n multi: true\n};\n\n@Component({\n selector: 'p-cascadeSelectSub, p-cascadeselect-sub',\n standalone: true,\n imports: [CommonModule, Ripple, AngleRightIcon, SharedModule],\n template: `\n <ul class=\"p-cascadeselect-list\" [attr.role]=\"role\" aria-orientation=\"horizontal\" [attr.data-pc-section]=\"level === 0 ? 'list' : 'sublist'\" [attr.aria-label]=\"listLabel\">\n <ng-template ngFor let-processedOption [ngForOf]=\"options\" let-i=\"index\">\n <li\n [ngClass]=\"getItemClass(processedOption)\"\n role=\"treeitem\"\n [attr.aria-level]=\"level + 1\"\n [attr.aria-setsize]=\"options.length\"\n [attr.data-pc-section]=\"'item'\"\n [id]=\"getOptionId(processedOption)\"\n [attr.aria-label]=\"getOptionLabelToRender(processedOption)\"\n [attr.aria-selected]=\"isOptionGroup(processedOption) ? undefined : isOptionSelected(processedOption)\"\n [attr.aria-posinset]=\"i + 1\"\n >\n <div\n class=\"p-cascadeselect-option-content\"\n (click)=\"onOptionClick($event, processedOption)\"\n (mouseenter)=\"onOptionMouseEnter($event, processedOption)\"\n (mousemove)=\"onOptionMouseMove($event, processedOption)\"\n pRipple\n [attr.data-pc-section]=\"'content'\"\n >\n <ng-container *ngIf=\"optionTemplate; else defaultOptionTemplate\">\n <ng-container *ngTemplateOutlet=\"optionTemplate; context: { $implicit: processedOption?.option }\"></ng-container>\n </ng-container>\n <ng-template #defaultOptionTemplate>\n <span class=\"p-cascadeselect-option-text\" [attr.data-pc-section]=\"'text'\">{{ getOptionLabelToRender(processedOption) }}</span>\n </ng-template>\n <span class=\"p-cascadeselect-group-icon\" *ngIf=\"isOptionGroup(processedOption)\" [attr.data-pc-section]=\"'groupIcon'\">\n <AngleRightIcon *ngIf=\"!groupicon\" />\n <ng-template *ngTemplateOutlet=\"groupicon\"></ng-template>\n </span>\n </div>\n <p-cascadeselect-sub\n *ngIf=\"isOptionGroup(processedOption) && isOptionActive(processedOption)\"\n [role]=\"'group'\"\n class=\"p-cascadeselect-list p-cascadeselect-overlay p-cascadeselect-option-list\"\n [selectId]=\"selectId\"\n [focusedOptionId]=\"focusedOptionId\"\n [activeOptionPath]=\"activeOptionPath\"\n [options]=\"getOptionGroupChildren(processedOption)\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [level]=\"level + 1\"\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 >\n </p-cascadeselect-sub>\n </li>\n </ng-template>\n </ul>\n `,\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class CascadeSelectSub extends BaseComponent implements OnInit {\n @Input() role: string | undefined;\n\n @Input() selectId: string | undefined;\n\n @Input() activeOptionPath: any[];\n\n @Input() optionDisabled: any[];\n\n @Input() focusedOptionId: string | undefined;\n\n @Input() options: any[] | string[] | string | undefined | null;\n\n @Input() optionGroupChildren: string[] | string | undefined | null;\n\n @Input() optionTemplate: Nullable<TemplateRef<any>>;\n\n @Input() groupicon: Nullable<TemplateRef<any>>;\n\n @Input({ transform: numberAttribute }) level: number = 0;\n\n @Input() optionLabel: string | undefined;\n\n @Input() optionValue: string | undefined;\n\n @Input() optionGroupLabel: string | undefined;\n\n @Input({ transform: booleanAttribute }) dirty: boolean | undefined;\n\n @Input({ transform: booleanAttribute }) root: boolean | undefined;\n\n @Output() onChange: EventEmitter<any> = new EventEmitter();\n\n @Output() onFocusChange: EventEmitter<any> = new EventEmitter();\n\n @Output() onFocusEnterChange: EventEmitter<any> = new EventEmitter();\n\n get listLabel(): string {\n return this.config.getTranslation(TranslationKeys.ARIA)['listLabel'];\n }\n\n constructor(public cascadeselect: CascadeSelect) {\n super();\n }\n\n ngOnInit() {\n super.ngOnInit();\n if (!this.root) {\n this.position();\n }\n }\n\n onOptionClick(event, processedOption: any) {\n this.onChange.emit({\n originalEvent: event,\n processedOption,\n isFocus: true\n });\n }\n\n onOptionMouseEnter(event, processedOption) {\n this.onFocusEnterChange.emit({ originalEvent: event, processedOption });\n }\n\n onOptionMouseMove(event, processedOption) {\n this.onFocusChange.emit({ originalEvent: event, processedOption });\n }\n\n getOptionId(processedOption) {\n return `${this.selectId}_${processedOption.key}`;\n }\n\n getOptionLabel(processedOption) {\n return this.optionLabel ? resolveFieldData(processedOption.option, this.optionLabel) : processedOption.option;\n }\n\n getOptionValue(processedOption) {\n return this.optionValue ? resolveFieldData(processedOption.option, this.optionValue) : processedOption.option;\n }\n\n getOptionLabelToRender(processedOption) {\n return this.isOptionGroup(processedOption) ? this.getOptionGroupLabel(processedOption) : this.getOptionLabel(processedOption);\n }\n\n isOptionDisabled(processedOption) {\n return this.optionDisabled ? resolveFieldData(processedOption.option, this.optionDisabled) : false;\n }\n\n getOptionGroupLabel(processedOption) {\n return this.optionGroupLabel ? resolveFieldData(processedOption.option, this.optionGroupLabel) : null;\n }\n\n getOptionGroupChildren(processedOption) {\n return processedOption.children;\n }\n\n isOptionGroup(processedOption) {\n return isNotEmpty(processedOption.children);\n }\n\n isOptionSelected(processedOption) {\n return equals(this.cascadeselect?.modelValue(), processedOption?.option);\n }\n\n isOptionActive(processedOption) {\n return this.activeOptionPath.some((path) => path.key === processedOption.key);\n }\n\n isOptionFocused(processedOption) {\n return this.focusedOptionId === this.getOptionId(processedOption);\n }\n\n getItemClass(option: string | string[]) {\n return {\n 'p-cascadeselect-option': true,\n 'p-cascadeselect-option-group': this.isOptionGroup(option),\n 'p-cascadeselect-option-active': this.isOptionActive(option),\n 'p-cascadeselect-option-selected': this.isOptionSelected(option),\n 'p-focus': this.isOptionFocused(option),\n 'p-disabled': this.isOptionDisabled(option)\n };\n }\n\n position() {\n const parentItem = this.el.nativeElement.parentElement;\n const containerOffset = <any>getOffset(parentItem);\n const viewport = <any>getViewport();\n const sublistWidth = this.el.nativeElement.children[0].offsetParent ? this.el.nativeElement.children[0].offsetWidth : getHiddenElementOuterWidth(this.el.nativeElement.children[0]);\n const itemOuterWidth = <any>getOuterWidth(parentItem.children[0]);\n if (parseInt(containerOffset.left, 10) + itemOuterWidth + sublistWidth > viewport.width - calculateScrollbarWidth()) {\n this.el.nativeElement.children[0].style.left = '-200%';\n }\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-cascadeselect, p-cascade-select',\n standalone: true,\n imports: [CommonModule, Overlay, AutoFocus, CascadeSelectSub, ChevronDownIcon, TimesIcon, SharedModule],\n template: ` <div #container [ngClass]=\"containerClass\" [class]=\"styleClass\" [ngStyle]=\"style\" (click)=\"onContainerClick($event)\" [attr.data-pc-name]=\"'cascadeselect'\" [attr.data-pc-section]=\"'root'\">\n <div class=\"p-hidden-accessible\" [attr.data-pc-section]=\"'hiddenInputWrapper'\">\n <input\n #focusInput\n readonly\n type=\"text\"\n role=\"combobox\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n [tabindex]=\"!disabled ? tabindex : -1\"\n [attr.id]=\"inputId\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledBy\"\n aria-haspopup=\"tree\"\n [attr.aria-expanded]=\"overlayVisible ?? false\"\n [attr.aria-controls]=\"overlayVisible ? id + '_tree' : null\"\n [attr.aria-activedescendant]=\"focused ? focusedOptionId : undefined\"\n (focus)=\"onInputFocus($event)\"\n (blur)=\"onInputBlur($event)\"\n (keydown)=\"onInputKeyDown($event)\"\n [pAutoFocus]=\"autofocus\"\n />\n </div>\n <span [ngClass]=\"labelClass\" [attr.data-pc-section]=\"'label'\">\n <ng-container *ngIf=\"valueTemplate || _valueTemplate; else defaultValueTemplate\">\n <ng-container *ngTemplateOutlet=\"valueTemplate || _valueTemplate; context: { $implicit: value, placeholder: placeholder }\"></ng-container>\n </ng-container>\n <ng-template #defaultValueTemplate>\n {{ label() }}\n </ng-template>\n </span>\n\n <ng-container *ngIf=\"filled && !disabled && showClear\">\n <TimesIcon *ngIf=\"!clearIconTemplate && !_clearIconTemplate\" class=\"p-cascadeselect-clear-icon\" (click)=\"clear($event)\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"clearIconTemplate || _clearIconTemplate\" class=\"p-cascadeselect-clear-icon\" (click)=\"clear($event)\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"clearIconTemplate || _clearIconTemplate\"></ng-template>\n </span>\n </ng-container>\n\n <div class=\"p-cascadeselect-dropdown\" role=\"button\" aria-haspopup=\"listbox\" [attr.aria-expanded]=\"overlayVisible ?? false\" [attr.data-pc-section]=\"'dropdownIcon'\" [attr.aria-hidden]=\"true\">\n <ng-container *ngIf=\"loading; else elseBlock\">\n <ng-container *ngIf=\"loadingIconTemplate || _loadingIconTemplate\">\n <ng-container *ngTemplateOutlet=\"loadingIconTemplate || _loadingIconTemplate\"></ng-container>\n </ng-container>\n <ng-container *ngIf=\"!loadingIconTemplate && !_loadingIconTemplate\">\n <span *ngIf=\"loadingIcon\" [ngClass]=\"'p-cascadeselect-loading-icon pi-spin ' + loadingIcon\" aria-hidden=\"true\"></span>\n <span *ngIf=\"!loadingIcon\" [class]=\"'p-cascadeselect-loading-icon pi pi-spinner pi-spin'\" aria-hidden=\"true\"></span>\n </ng-container>\n </ng-container>\n <ng-template #elseBlock>\n <ChevronDownIcon *ngIf=\"!triggerIconTemplate && !_triggerIconTemplate\" [styleClass]=\"'p-cascadeselect-dropdown-icon'\" />\n <span *ngIf=\"triggerIconTemplate || _triggerIconTemplate\" class=\"p-cascadeselect-dropdown-icon\">\n <ng-template *ngTemplateOutlet=\"triggerIconTemplate || _triggerIconTemplate\"></ng-template>\n </span>\n </ng-template>\n </div>\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\">\n {{ searchResultMessageText }}\n </span>\n <p-overlay\n #overlay\n [(visible)]=\"overlayVisible\"\n [options]=\"overlayOptions\"\n [target]=\"'@parent'\"\n [appendTo]=\"appendTo\"\n [showTransitionOptions]=\"showTransitionOptions\"\n [hideTransitionOptions]=\"hideTransitionOptions\"\n (onAnimationDone)=\"onOverlayAnimationDone($event)\"\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 [ngClass]=\"{ 'p-cascadeselect-overlay p-component': true, 'p-cascadeselect-mobile-active': queryMatches() }\" [class]=\"panelStyleClass\" [ngStyle]=\"panelStyle\" [attr.data-pc-section]=\"'panel'\">\n <ng-template *ngTemplateOutlet=\"headerTemplate || _headerTemplate\"></ng-template>\n <div class=\"p-cascadeselect-list-container\" [attr.data-pc-section]=\"'wrapper'\">\n <p-cascadeselect-sub\n [options]=\"processedOptions\"\n [selectId]=\"id\"\n [focusedOptionId]=\"focused ? focusedOptionId : undefined\"\n [activeOptionPath]=\"activeOptionPath()\"\n [optionLabel]=\"optionLabel\"\n [optionValue]=\"optionValue\"\n [level]=\"0\"\n [optionTemplate]=\"optionTemplate || _optionTemplate\"\n [groupicon]=\"groupIconTemplate || groupIconTemplate\"\n [optionGroupLabel]=\"optionGroupLabel\"\n [optionGroupChildren]=\"optionGroupChildren\"\n [optionDisabled]=\"optionDisabled\"\n [root]=\"true\"\n (onChange)=\"onOptionClick($event)\"\n (onFocusChange)=\"onOptionMouseMove($event)\"\n (onFocusEnterChange)=\"onOptionMouseEnter($event)\"\n [dirty]=\"dirty\"\n [role]=\"'tree'\"\n >\n </p-cascadeselect-sub>\n </div>\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\">\n {{ selectedMessageText }}\n </span>\n <ng-template *ngTemplateOutlet=\"footerTemplate || _footerTemplate\"></ng-template>\n </div>\n </ng-template>\n </p-overlay>\n </div>`,\n providers: [CASCADESELECT_VALUE_ACCESSOR, CascadeSelectStyle],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class CascadeSelect extends BaseComponent implements OnInit, AfterContentInit {\n /**\n * Unique identifier of the component\n * @group Props\n */\n @Input() id: string | undefined;\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 @Input() searchMessage: string | undefined;\n /**\n * Text to display when there is no data. Defaults to global value in i18n translation configuration.\n * @group Props\n */\n @Input() emptyMessage: string | undefined;\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 @Input() selectionMessage: string | undefined;\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 @Input() emptySearchMessage: string | undefined;\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 @Input() emptySelectionMessage: string | undefined;\n /**\n * Locale to use in searching. The default locale is the host environment's current locale.\n * @group Props\n */\n @Input() searchLocale: string | undefined;\n /**\n * Name of the disabled field of an option.\n * @group Props\n */\n @Input() optionDisabled: any;\n /**\n * Fields used when filtering the options, defaults to optionLabel.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) focusOnHover: boolean = true;\n /**\n * Determines if the option will be selected on focus.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) selectOnFocus: boolean = false;\n /**\n * Whether to focus on the first visible or selected element when the overlay panel is shown.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autoOptionFocus: boolean = false;\n /**\n * Style class of the component.\n * @group Props\n */\n @Input() styleClass: string | undefined;\n /**\n * Inline style of the component.\n * @group Props\n */\n @Input() style: { [klass: string]: any } | null | undefined;\n /**\n * An array of selectitems to display as the available options.\n * @group Props\n */\n @Input() options: string[] | string | undefined;\n /**\n * Property name or getter function to use as the label of an option.\n * @group Props\n */\n @Input() optionLabel: string | undefined;\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 @Input() optionValue: string | undefined;\n /**\n * Property name or getter function to use as the label of an option group.\n * @group Props\n */\n @Input() optionGroupLabel: string | undefined;\n /**\n * Property name or getter function to retrieve the items of a group.\n * @group Props\n */\n @Input() optionGroupChildren: string[] | string | undefined | null;\n /**\n * Default text to display when no option is selected.\n * @group Props\n */\n @Input() placeholder: string | undefined;\n /**\n * Selected value of the component.\n * @group Props\n */\n @Input() value: string | undefined | null;\n /**\n * A property to uniquely identify an option.\n * @group Props\n */\n @Input() dataKey: string | undefined;\n /**\n * Identifier of the underlying input element.\n * @group Props\n */\n @Input() inputId: string | undefined;\n /**\n * Defines the size of the component.\n * @group Props\n */\n @Input() size: 'large' | 'small';\n /**\n * Index of the element in tabbing order.\n * @group Props\n */\n @Input({ transform: numberAttribute }) tabindex: number | undefined = 0;\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 @Input() ariaLabelledBy: string | undefined;\n /**\n * Label of the input for accessibility.\n * @group Props\n */\n @Input() inputLabel: string | undefined;\n /**\n * Defines a string that labels the input for accessibility.\n * @group Props\n */\n @Input() ariaLabel: string | undefined;\n /**\n * Id of the element or \"body\" for document where the overlay should be appended to.\n * @group Props\n */\n @Input() appendTo: HTMLElement | ElementRef | TemplateRef<any> | string | null | undefined | any;\n /**\n * When present, it specifies that the component should be disabled.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) disabled: boolean | undefined;\n /**\n * When enabled, a clear icon is displayed to clear the value.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showClear: boolean = false;\n /**\n * Style class of the overlay panel.\n * @group Props\n */\n @Input() panelStyleClass: string | undefined;\n /**\n * Inline style of the overlay panel.\n * @group Props\n */\n @Input() panelStyle: { [klass: string]: any } | null | undefined;\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 @Input() overlayOptions: OverlayOptions | undefined;\n /**\n * When present, it specifies that the component should automatically get focus on load.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autofocus: boolean | undefined;\n /**\n * Transition options of the show animation.\n * @group Props\n * @deprecated deprecated since v14.2.0, use overlayOptions property instead.\n */\n @Input() get showTransitionOptions(): string {\n return this._showTransitionOptions;\n }\n set showTransitionOptions(val: string) {\n this._showTransitionOptions = val;\n console.log('The showTransitionOptions property is deprecated since v14.2.0, use overlayOptions property instead.');\n }\n /**\n * Specifies the input variant of the component.\n * @group Props\n */\n @Input() variant: 'filled' | 'outlined';\n /**\n * Whether the dropdown is in loading state.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) loading: boolean | undefined = false;\n /**\n * Icon to display in loading state.\n * @group Props\n */\n @Input() loadingIcon: string | undefined;\n /**\n * Transition options of the hide animation.\n * @group Props\n * @deprecated deprecated since v14.2.0, use overlayOptions property instead.\n */\n @Input() get hideTransitionOptions(): string {\n return this._hideTransitionOptions;\n }\n set hideTransitionOptions(val: string) {\n this._hideTransitionOptions = val;\n console.log('The hideTransitionOptions property is deprecated since v14.2.0, use overlayOptions property instead.');\n }\n /**\n * Spans 100% width of the container when enabled.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) fluid: boolean = false;\n /**\n * The breakpoint to define the maximum width boundary.\n * @group Props\n */\n @Input() breakpoint: string = '960px';\n /**\n * Callback to invoke on value change.\n * @param {CascadeSelectChangeEvent} event - Custom change event.\n * @group Emits\n */\n @Output() onChange: EventEmitter<CascadeSelectChangeEvent> = new EventEmitter<CascadeSelectChangeEvent>();\n /**\n * Callback to invoke when a group changes.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onGroupChange: EventEmitter<Event> = new EventEmitter<Event>();\n /**\n * Callback to invoke when the overlay is shown.\n * @param {CascadeSelectShowEvent} event - Custom overlay show event.\n * @group Emits\n */\n @Output() onShow: EventEmitter<CascadeSelectShowEvent> = new EventEmitter<CascadeSelectShowEvent>();\n /**\n * Callback to invoke when the overlay is hidden.\n * @param {CascadeSelectHideEvent} event - Custom overlay hide event.\n * @group Emits\n */\n @Output() onHide: EventEmitter<CascadeSelectHideEvent> = new EventEmitter<CascadeSelectHideEvent>();\n /**\n * Callback to invoke when the clear token is clicked.\n * @group Emits\n */\n @Output() onClear: EventEmitter<any> = new EventEmitter();\n /**\n * Callback to invoke before overlay is shown.\n * @param {CascadeSelectBeforeShowEvent} event - Custom overlay show event.\n * @group Emits\n */\n @Output() onBeforeShow: EventEmitter<CascadeSelectBeforeShowEvent> = new EventEmitter<CascadeSelectBeforeShowEvent>();\n /**\n * Callback to invoke before overlay is hidden.\n * @param {CascadeSelectBeforeHideEvent} event - Custom overlay hide event.\n * @group Emits\n */\n @Output() onBeforeHide: EventEmitter<CascadeSelectBeforeHideEvent> = new EventEmitter<CascadeSelectBeforeHideEvent>();\n /**\n * Callback to invoke when input receives focus.\n * @param {FocusEvent} event - Focus event.\n * @group Emits\n */\n @Output() onFocus: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();\n /**\n * Callback to invoke when input loses focus.\n * @param {FocusEvent} event - Focus event.\n * @group Emits\n */\n @Output() onBlur: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();\n\n @ViewChild('focusInput') focusInputViewChild: Nullable<ElementRef>;\n\n @ViewChild('container') containerViewChild: Nullable<ElementRef>;\n\n @ViewChild('panel') panelViewChild: Nullable<ElementRef>;\n\n @ViewChild('overlay') overlayViewChild: Nullable<Overlay>;\n /**\n * Content template for displaying the selected value.\n * @group Templates\n */\n @ContentChild('value', { descendants: false }) valueTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the option display.\n * @group Templates\n */\n @ContentChild('option', { descendants: false }) optionTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the header.\n * @group Templates\n */\n @ContentChild('header', { descendants: false }) headerTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the footer.\n * @group Templates\n */\n @ContentChild('footer', { descendants: false }) footerTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the trigger icon.\n * @group Templates\n */\n @ContentChild('triggericon', { descendants: false }) triggerIconTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the loading icon.\n * @group Templates\n */\n @ContentChild('loadingicon', { descendants: false }) loadingIconTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the group icon.\n * @group Templates\n */\n @ContentChild('optiongroupicon', { descendants: false }) groupIconTemplate: Nullable<TemplateRef<any>>;\n\n /**\n * Content template for customizing the clear icon.\n * @group Templates\n */\n @ContentChild('clearicon', { descendants: false }) clearIconTemplate: Nullable<TemplateRef<any>>;\n\n _valueTemplate: TemplateRef<any> | undefined;\n\n _optionTemplate: TemplateRef<any> | undefined;\n\n _headerTemplate: TemplateRef<any> | undefined;\n\n _footerTemplate: TemplateRef<any> | undefined;\n\n _triggerIconTemplate: TemplateRef<any> | undefined;\n\n _loadingIconTemplate: TemplateRef<any> | undefined;\n\n _groupIconTemplate: TemplateRef<any> | undefined;\n\n _clearIconTemplate: TemplateRef<any> | undefined;\n\n _showTransitionOptions: string = '';\n\n _hideTransitionOptions: string = '';\n\n selectionPath: any = null;\n\n focused: boolean = false;\n\n overlayVisible: boolean = false;\n\n clicked: boolean = false;\n\n dirty: boolean = false;\n\n searchValue: string | undefined;\n\n searchTimeout: any;\n\n onModelChange: Function = () => {};\n\n onModelTouched: Function = () => {};\n\n focusedOptionInfo = signal<any>({ index: -1, level: 0, parentKey: '' });\n\n activeOptionPath = signal<any>([]);\n\n modelValue = signal<any>(null);\n\n processedOptions: string[] | string | undefined = [];\n\n _componentStyle = inject(CascadeSelectStyle);\n\n get containerClass() {\n return {\n 'p-cascadeselect p-component p-inputwrapper': true,\n 'p-cascadeselect-clearable': this.showClear && !this.disabled,\n 'p-cascadeselect-mobile': this.queryMatches(),\n 'p-disabled': this.disabled,\n 'p-focus': this.focused,\n 'p-inputwrapper-filled': this.modelValue(),\n 'p-variant-filled': this.variant === 'filled' || this.config.inputStyle() === 'filled' || this.config.inputVariant() === 'filled',\n 'p-inputwrapper-focus': this.focused || this.overlayVisible,\n 'p-cascadeselect-open': this.overlayVisible,\n 'p-cascadeselect-fluid': this.hasFluid,\n 'p-cascadeselect-sm p-inputfield-sm': this.size === 'small',\n 'p-cascadeselect-lg p-inputfield-lg': this.size === 'large'\n };\n }\n\n get labelClass() {\n return {\n 'p-cascadeselect-label': true,\n 'p-placeholder': this.label() === this.placeholder,\n 'p-cascadeselect-label-empty': !this.value && (this.label() === 'p-emptylabel' || this.label().length === 0)\n };\n }\n\n get hasFluid() {\n const nativeElement = this.el.nativeElement;\n const fluidComponent = nativeElement.closest('p-fluid');\n return this.fluid || !!fluidComponent;\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 filled(): boolean {\n if (typeof this.modelValue() === 'string') return !!this.modelValue();\n\n return this.modelValue() || this.modelValue() != null || this.modelValue() != undefined;\n }\n\n get searchResultMessageText() {\n return isNotEmpty(this.visibleOptions()) ? this.searchMessageText.replaceAll('{0}', 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 get _label() {\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 @ContentChildren(PrimeTemplate) templates!: QueryList<PrimeTemplate>;\n\n ngAfterContentInit() {\n this.templates.forEach((item) => {\n switch (item.getType()) {\n case 'value':\n this._valueTemplate = item.template;\n break;\n\n case 'option':\n this._optionTemplate = item.template;\n break;\n\n case 'triggericon':\n this._triggerIconTemplate = item.template;\n break;\n\n case 'loadingicon':\n this._loadingIconTemplate = item.template;\n break;\n\n case 'clearicon':\n this._clearIconTemplate = item.template;\n break;\n\n case 'optiongroupicon':\n this._groupIconTemplate = item.template;\n break;\n }\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n super.ngOnChanges(changes);\n if (changes.options) {\n this.processedOptions = this.createProcessedOptions(changes.options.currentValue || []);\n this.updateModel(null);\n }\n }\n\n hasSelectedOption() {\n return isNotEmpty(this.modelValue());\n }\n\n createProcessedOptions(options, level = 0, parent = {}, parentKey = '') {\n const processedOptions = [];\n\n options &&\n options.forEach((option, index) => {\n const key = (parentKey !== '' ? parentKey + '_' : '') + index;\n const newOption = {\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 = true;\n this.onFocus.emit(event);\n }\n\n onInputBlur(event: FocusEvent) {\n this.focused = false;\n this.focusedOptionInfo.set({ indeX: -1, level: 0, parentKey: '' });\n this.searchValue = '';\n t