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,

953 lines (949 loc) 100 kB
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common'; import * as i0 from '@angular/core'; import { InjectionToken, Injectable, input, numberAttribute, booleanAttribute, signal, output, inject, effect, untracked, ChangeDetectionStrategy, ViewEncapsulation, Component, forwardRef, computed, viewChild, contentChild, HostListener, NgModule } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { resolveFieldData, isNotEmpty, equals, nestedPosition, uuid, isPrintableCharacter, isEmpty, findSingle, findLastIndex, focus } from '@primeuix/utils'; import { SharedModule, TranslationKeys, OverlayService } from 'primeng/api'; import { AutoFocus } from 'primeng/autofocus'; import { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent'; import { BaseEditableHolder } from 'primeng/baseeditableholder'; import * as i1$1 from 'primeng/bind'; import { Bind } from 'primeng/bind'; import { Fluid } from 'primeng/fluid'; import { ChevronDown } from '@primeicons/angular/chevron-down'; import { Times } from '@primeicons/angular/times'; import { Overlay } from 'primeng/overlay'; import { AngleRight } from '@primeicons/angular/angle-right'; import * as i1 from 'primeng/motion'; import { MotionModule } from 'primeng/motion'; import { Ripple } from 'primeng/ripple'; import { style } from '@primeuix/styles/cascadeselect'; import { BaseStyle } from 'primeng/base'; export * from 'primeng/types/cascadeselect'; const CASCADESELECT_INSTANCE = new InjectionToken('CASCADESELECT_INSTANCE'); const inlineStyles = { root: ({ instance }) => ({ position: instance.$appendTo() === 'self' ? 'relative' : undefined }) }; const classes = { root: ({ instance }) => [ 'p-cascadeselect p-component p-inputwrapper', { 'p-cascadeselect-mobile': instance.queryMatches(), 'p-disabled': instance.$disabled(), 'p-invalid': instance.invalid(), 'p-variant-filled': instance.$variant() === 'filled', 'p-focus': instance.focused(), 'p-inputwrapper-filled': instance.modelValue(), 'p-inputwrapper-focus': instance.focused() || instance.overlayVisible(), 'p-cascadeselect-open': instance.overlayVisible(), 'p-cascadeselect-fluid': instance.hasFluid, 'p-cascadeselect-sm p-inputfield-sm': instance.size() === 'small', 'p-cascadeselect-lg p-inputfield-lg': instance.size() === 'large' } ], label: ({ instance }) => [ 'p-cascadeselect-label', { 'p-placeholder': instance.label() === instance.placeholder(), 'p-cascadeselect-label-empty': !instance.value() && (instance.label() === 'p-emptylabel' || instance.label().length === 0) } ], clearIcon: 'p-cascadeselect-clear-icon', dropdown: 'p-cascadeselect-dropdown', loadingIcon: 'p-cascadeselect-loading-icon', dropdownIcon: 'p-cascadeselect-dropdown-icon', overlay: ({ instance }) => [ 'p-cascadeselect-overlay p-component-overlay p-component', { 'p-cascadeselect-mobile-active': instance.queryMatches() } ], listContainer: 'p-cascadeselect-list-container', list: 'p-cascadeselect-list', option: ({ instance, processedOption }) => [ 'p-cascadeselect-option', { 'p-cascadeselect-option-active': instance.isOptionActive(processedOption), 'p-cascadeselect-option-selected': instance.isOptionSelected(processedOption), 'p-focus': instance.isOptionFocused(processedOption), 'p-disabled': instance.isOptionDisabled(processedOption) } ], optionContent: 'p-cascadeselect-option-content', optionText: 'p-cascadeselect-option-text', groupIcon: 'p-cascadeselect-group-icon', optionList: 'p-cascadeselect-list p-cascadeselect-overlay p-cascadeselect-option-list' }; class CascadeSelectStyle extends BaseStyle { name = 'cascadeselect'; style = style; classes = classes; inlineStyles = inlineStyles; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CascadeSelectStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CascadeSelectStyle }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CascadeSelectStyle, decorators: [{ type: Injectable }] }); /** * * CascadeSelect is a form component to select a value from a nested structure of options. * * [Live Demo](https://www.primeng.org/cascadeselect/) * * @module cascadeselectstyle * */ var CascadeSelectClasses; (function (CascadeSelectClasses) { /** * Class name of the root element */ CascadeSelectClasses["root"] = "p-cascadeselect"; /** * Class name of the label element */ CascadeSelectClasses["label"] = "p-cascadeselect-label"; /** * Class name of the dropdown element */ CascadeSelectClasses["dropdown"] = "p-cascadeselect-dropdown"; /** * Class name of the loading icon element */ CascadeSelectClasses["loadingIcon"] = "p-cascadeselect-loading-icon"; /** * Class name of the dropdown icon element */ CascadeSelectClasses["clearIcon"] = "p-cascadeselect-clear-icon"; /** * Class name of the dropdown icon element */ CascadeSelectClasses["dropdownIcon"] = "p-cascadeselect-dropdown-icon"; /** * Class name of the overlay element */ CascadeSelectClasses["overlay"] = "p-cascadeselect-overlay"; /** * Class name of the list container element */ CascadeSelectClasses["listContainer"] = "p-cascadeselect-list-container"; /** * Class name of the list element */ CascadeSelectClasses["list"] = "p-cascadeselect-list"; /** * Class name of the item element */ CascadeSelectClasses["item"] = "p-cascadeselect-item"; /** * Class name of the item content element */ CascadeSelectClasses["itemContent"] = "p-cascadeselect-item-content"; /** * Class name of the item text element */ CascadeSelectClasses["itemText"] = "p-cascadeselect-item-text"; /** * Class name of the group icon element */ CascadeSelectClasses["groupIcon"] = "p-cascadeselect-group-icon"; /** * Class name of the item list element */ CascadeSelectClasses["itemList"] = "p-cascadeselect-item-list"; })(CascadeSelectClasses || (CascadeSelectClasses = {})); class CascadeSelectSub extends BaseComponent { selectId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "selectId" }] : /* istanbul ignore next */ [])); activeOptionPath = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "activeOptionPath" }] : /* istanbul ignore next */ [])); optionDisabled = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionDisabled" }] : /* istanbul ignore next */ [])); focusedOptionId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "focusedOptionId" }] : /* istanbul ignore next */ [])); options = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "options" }] : /* istanbul ignore next */ [])); optionGroupChildren = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionGroupChildren" }] : /* istanbul ignore next */ [])); optionTemplate = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionTemplate" }] : /* istanbul ignore next */ [])); groupicon = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "groupicon" }] : /* istanbul ignore next */ [])); level = input(0, { ...(ngDevMode ? { debugName: "level" } : /* istanbul ignore next */ {}), transform: numberAttribute }); optionLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionLabel" }] : /* istanbul ignore next */ [])); optionValue = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionValue" }] : /* istanbul ignore next */ [])); optionGroupLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionGroupLabel" }] : /* istanbul ignore next */ [])); dirty = input(false, { ...(ngDevMode ? { debugName: "dirty" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); root = input(false, { ...(ngDevMode ? { debugName: "root" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); motionOptions = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "motionOptions" }] : /* istanbul ignore next */ [])); renderedKeys = signal(new Set(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "renderedKeys" }] : /* istanbul ignore next */ [])); onChange = output(); onFocusChange = output(); onFocusEnterChange = output(); _componentStyle = inject(CascadeSelectStyle); cascadeselect = inject(CASCADESELECT_INSTANCE); constructor() { super(); effect(() => { const activeKeys = (this.options() ?? []).filter((option) => this.isOptionGroup(option) && this.isOptionActive(option)).map((option) => option.key); if (!activeKeys.length) { return; } const current = untracked(this.renderedKeys); const added = activeKeys.filter((key) => !current.has(key)); if (added.length) { this.renderedKeys.set(new Set([...current, ...added])); } }); } getPTOptions(processedOption, index, key) { return this.ptm(key, { context: { option: processedOption, index, level: this.level(), optionGroup: this.isOptionGroup(processedOption), active: this.isOptionActive(processedOption), focused: this.isOptionFocused(processedOption), disabled: this.isOptionDisabled(processedOption) } }); } onBeforeEnter(event) { this.positionSubList(event.element); } onNestedAfterLeave(processedOption) { if (!this.isOptionActive(processedOption)) { const next = new Set(this.renderedKeys()); next.delete(processedOption.key); this.renderedKeys.set(next); } } onOptionClick(event, processedOption) { this.onChange.emit({ originalEvent: event, processedOption, isFocus: true }); } onOptionMouseEnter(event, processedOption) { this.onFocusEnterChange.emit({ originalEvent: event, processedOption }); } onOptionMouseMove(event, processedOption) { this.onFocusChange.emit({ originalEvent: event, processedOption }); } getOptionId(processedOption) { return `${this.selectId()}_${processedOption.key}`; } getOptionLabel(processedOption) { const optionLabel = this.optionLabel(); return optionLabel ? resolveFieldData(processedOption.option, optionLabel) : processedOption.option; } getOptionValue(processedOption) { const optionValue = this.optionValue(); return optionValue ? resolveFieldData(processedOption.option, optionValue) : processedOption.option; } getOptionLabelToRender(processedOption) { return this.isOptionGroup(processedOption) ? this.getOptionGroupLabel(processedOption) : this.getOptionLabel(processedOption); } isOptionDisabled(processedOption) { const optionDisabled = this.optionDisabled(); return optionDisabled ? resolveFieldData(processedOption.option, optionDisabled) : false; } getOptionGroupLabel(processedOption) { const optionGroupLabel = this.optionGroupLabel(); return optionGroupLabel ? resolveFieldData(processedOption.option, optionGroupLabel) : null; } getOptionGroupChildren(processedOption) { return processedOption.children; } isOptionGroup(processedOption) { return isNotEmpty(processedOption.children); } isOptionSelected(processedOption) { return equals(this.cascadeselect?.modelValue(), processedOption?.option); } isOptionActive(processedOption) { return this.activeOptionPath()?.some((path) => path.key === processedOption.key); } isOptionFocused(processedOption) { return this.focusedOptionId() === this.getOptionId(processedOption); } shouldRenderSubList(processedOption) { return this.isOptionGroup(processedOption) && (this.isOptionActive(processedOption) || this.renderedKeys().has(processedOption.key)); } getAriaSelected(processedOption) { return this.isOptionGroup(processedOption) ? undefined : this.isOptionSelected(processedOption); } getOptionTemplateContext(processedOption) { return { $implicit: processedOption?.option, level: this.level() }; } positionSubList(sublist) { if (isPlatformBrowser(this.platformId) && sublist) { nestedPosition(sublist, this.level() + 1); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CascadeSelectSub, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: CascadeSelectSub, isStandalone: true, selector: "ul[pCascadeSelectSub]", inputs: { selectId: { classPropertyName: "selectId", publicName: "selectId", isSignal: true, isRequired: false, transformFunction: null }, activeOptionPath: { classPropertyName: "activeOptionPath", publicName: "activeOptionPath", isSignal: true, isRequired: false, transformFunction: null }, optionDisabled: { classPropertyName: "optionDisabled", publicName: "optionDisabled", isSignal: true, isRequired: false, transformFunction: null }, focusedOptionId: { classPropertyName: "focusedOptionId", publicName: "focusedOptionId", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionGroupChildren: { classPropertyName: "optionGroupChildren", publicName: "optionGroupChildren", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, groupicon: { classPropertyName: "groupicon", publicName: "groupicon", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionGroupLabel: { classPropertyName: "optionGroupLabel", publicName: "optionGroupLabel", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, root: { classPropertyName: "root", publicName: "root", isSignal: true, isRequired: false, transformFunction: null }, motionOptions: { classPropertyName: "motionOptions", publicName: "motionOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onChange: "onChange", onFocusChange: "onFocusChange", onFocusEnterChange: "onFocusEnterChange" }, providers: [CascadeSelectStyle, { provide: PARENT_INSTANCE, useExisting: CascadeSelectSub }], usesInheritance: true, ngImport: i0, template: ` @for (processedOption of options(); track processedOption.key; let i = $index) { <li [class]="cx('option', { processedOption })" role="treeitem" [attr.aria-level]="level() + 1" [attr.aria-setsize]="options()?.length" [pBind]="getPTOptions(processedOption, i, 'option')" [id]="getOptionId(processedOption)" [attr.aria-label]="getOptionLabelToRender(processedOption)" [attr.aria-selected]="getAriaSelected(processedOption)" [attr.aria-posinset]="i + 1" > <div [class]="cx('optionContent')" (click)="onOptionClick($event, processedOption)" (mouseenter)="onOptionMouseEnter($event, processedOption)" (mousemove)="onOptionMouseMove($event, processedOption)" pRipple [pBind]="getPTOptions(processedOption, i, 'optionContent')" > @if (optionTemplate()) { <ng-container *ngTemplateOutlet="optionTemplate(); context: getOptionTemplateContext(processedOption)"></ng-container> } @else { <span [class]="cx('optionText')" [pBind]="getPTOptions(processedOption, i, 'optionText')">{{ getOptionLabelToRender(processedOption) }}</span> } @if (isOptionGroup(processedOption)) { <span [class]="cx('groupIcon')" [pBind]="getPTOptions(processedOption, i, 'groupIcon')"> @if (!groupicon()) { <svg data-p-icon="angle-right" [pBind]="getPTOptions(processedOption, i, 'groupIcon')" /> } @else { <ng-container *ngTemplateOutlet="groupicon()"></ng-container> } </span> } </div> @if (shouldRenderSubList(processedOption)) { <ul pCascadeSelectSub [attr.role]="'group'" [class]="cx('optionList')" [selectId]="selectId()" [focusedOptionId]="focusedOptionId()" [activeOptionPath]="activeOptionPath()" [options]="getOptionGroupChildren(processedOption)" [optionLabel]="optionLabel()" [optionValue]="optionValue()" [level]="level() + 1" [pMotion]="isOptionActive(processedOption)" [pMotionDisabled]="cascadeselect.queryMatches()" [pMotionName]="'p-anchored-overlay'" [pMotionAppear]="true" [pMotionOptions]="motionOptions()" (pMotionOnBeforeEnter)="onBeforeEnter($event)" (pMotionOnAfterLeave)="onNestedAfterLeave(processedOption)" (onChange)="onChange.emit($event)" (onFocusChange)="onFocusChange.emit($event)" (onFocusEnterChange)="onFocusEnterChange.emit($event)" [optionGroupLabel]="optionGroupLabel()" [optionGroupChildren]="optionGroupChildren()" [dirty]="dirty()" [optionTemplate]="optionTemplate()" [motionOptions]="motionOptions()" [pBind]="ptm('optionList')" [pt]="pt" [unstyled]="unstyled()" ></ul> } </li> } `, isInline: true, dependencies: [{ kind: "component", type: CascadeSelectSub, selector: "ul[pCascadeSelectSub]", inputs: ["selectId", "activeOptionPath", "optionDisabled", "focusedOptionId", "options", "optionGroupChildren", "optionTemplate", "groupicon", "level", "optionLabel", "optionValue", "optionGroupLabel", "dirty", "root", "motionOptions"], outputs: ["onChange", "onFocusChange", "onFocusEnterChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: Ripple, selector: "[pRipple]" }, { kind: "component", type: AngleRight, selector: "svg[data-p-icon=\"angle-right\"]" }, { kind: "ngmodule", type: SharedModule }, { kind: "directive", type: Bind, selector: "[pBind]", inputs: ["pBind"] }, { kind: "ngmodule", type: MotionModule }, { kind: "directive", type: i1.MotionDirective, selector: "[pMotion]", inputs: ["pMotion", "pMotionName", "pMotionType", "pMotionSafe", "pMotionDisabled", "pMotionAppear", "pMotionEnter", "pMotionLeave", "pMotionDuration", "pMotionHideStrategy", "pMotionEnterFromClass", "pMotionEnterToClass", "pMotionEnterActiveClass", "pMotionLeaveFromClass", "pMotionLeaveToClass", "pMotionLeaveActiveClass", "pMotionOptions"], outputs: ["pMotionOnBeforeEnter", "pMotionOnEnter", "pMotionOnAfterEnter", "pMotionOnEnterCancelled", "pMotionOnBeforeLeave", "pMotionOnLeave", "pMotionOnAfterLeave", "pMotionOnLeaveCancelled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: CascadeSelectSub, decorators: [{ type: Component, args: [{ selector: 'ul[pCascadeSelectSub]', standalone: true, imports: [NgTemplateOutlet, Ripple, AngleRight, SharedModule, Bind, MotionModule], template: ` @for (processedOption of options(); track processedOption.key; let i = $index) { <li [class]="cx('option', { processedOption })" role="treeitem" [attr.aria-level]="level() + 1" [attr.aria-setsize]="options()?.length" [pBind]="getPTOptions(processedOption, i, 'option')" [id]="getOptionId(processedOption)" [attr.aria-label]="getOptionLabelToRender(processedOption)" [attr.aria-selected]="getAriaSelected(processedOption)" [attr.aria-posinset]="i + 1" > <div [class]="cx('optionContent')" (click)="onOptionClick($event, processedOption)" (mouseenter)="onOptionMouseEnter($event, processedOption)" (mousemove)="onOptionMouseMove($event, processedOption)" pRipple [pBind]="getPTOptions(processedOption, i, 'optionContent')" > @if (optionTemplate()) { <ng-container *ngTemplateOutlet="optionTemplate(); context: getOptionTemplateContext(processedOption)"></ng-container> } @else { <span [class]="cx('optionText')" [pBind]="getPTOptions(processedOption, i, 'optionText')">{{ getOptionLabelToRender(processedOption) }}</span> } @if (isOptionGroup(processedOption)) { <span [class]="cx('groupIcon')" [pBind]="getPTOptions(processedOption, i, 'groupIcon')"> @if (!groupicon()) { <svg data-p-icon="angle-right" [pBind]="getPTOptions(processedOption, i, 'groupIcon')" /> } @else { <ng-container *ngTemplateOutlet="groupicon()"></ng-container> } </span> } </div> @if (shouldRenderSubList(processedOption)) { <ul pCascadeSelectSub [attr.role]="'group'" [class]="cx('optionList')" [selectId]="selectId()" [focusedOptionId]="focusedOptionId()" [activeOptionPath]="activeOptionPath()" [options]="getOptionGroupChildren(processedOption)" [optionLabel]="optionLabel()" [optionValue]="optionValue()" [level]="level() + 1" [pMotion]="isOptionActive(processedOption)" [pMotionDisabled]="cascadeselect.queryMatches()" [pMotionName]="'p-anchored-overlay'" [pMotionAppear]="true" [pMotionOptions]="motionOptions()" (pMotionOnBeforeEnter)="onBeforeEnter($event)" (pMotionOnAfterLeave)="onNestedAfterLeave(processedOption)" (onChange)="onChange.emit($event)" (onFocusChange)="onFocusChange.emit($event)" (onFocusEnterChange)="onFocusEnterChange.emit($event)" [optionGroupLabel]="optionGroupLabel()" [optionGroupChildren]="optionGroupChildren()" [dirty]="dirty()" [optionTemplate]="optionTemplate()" [motionOptions]="motionOptions()" [pBind]="ptm('optionList')" [pt]="pt" [unstyled]="unstyled()" ></ul> } </li> } `, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [CascadeSelectStyle, { provide: PARENT_INSTANCE, useExisting: CascadeSelectSub }] }] }], ctorParameters: () => [], propDecorators: { selectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectId", required: false }] }], activeOptionPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeOptionPath", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], focusedOptionId: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusedOptionId", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionGroupChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupChildren", required: false }] }], optionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionTemplate", required: false }] }], groupicon: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupicon", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionGroupLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupLabel", required: false }] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], root: [{ type: i0.Input, args: [{ isSignal: true, alias: "root", required: false }] }], motionOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "motionOptions", required: false }] }], onChange: [{ type: i0.Output, args: ["onChange"] }], onFocusChange: [{ type: i0.Output, args: ["onFocusChange"] }], onFocusEnterChange: [{ type: i0.Output, args: ["onFocusEnterChange"] }] } }); const CASCADESELECT_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CascadeSelect), multi: true }; /** * CascadeSelect is a form component to select a value from a nested structure of options. * @group Components */ class CascadeSelect extends BaseEditableHolder { componentName = 'CascadeSelect'; $pcCascadeSelect = inject(CASCADESELECT_INSTANCE, { optional: true, skipSelf: true }) ?? undefined; bindDirectiveInstance = inject(Bind, { self: true }); onAfterViewChecked() { this.bindDirectiveInstance.setAttrs(this.ptms(['host', 'root'])); } _internalId = uuid('pn_id_'); /** * Unique identifier of the component * @group Props */ id = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "id" }] : /* istanbul ignore next */ [])); $id = computed(() => this.id() || this._internalId, /* @ts-ignore */ ...(ngDevMode ? [{ 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 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 */ [])); /** * 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 */ [])); /** * Text to display when filtering does not return any results. Defaults to value from PrimeNG locale configuration. * @group Props * @defaultValue 'No available options' */ emptySearchMessage = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "emptySearchMessage" }] : /* 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 */ [])); /** * 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 */ [])); /** * Name of the disabled field of an option. * @group Props */ optionDisabled = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionDisabled" }] : /* istanbul ignore next */ [])); /** * Fields used when filtering the options, defaults to optionLabel. * @group Props */ focusOnHover = input(true, { ...(ngDevMode ? { debugName: "focusOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Determines if the option will be selected on focus. * @group Props */ selectOnFocus = input(false, { ...(ngDevMode ? { debugName: "selectOnFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * 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 }); /** * An array of selectitems to display as the available options. * @group Props */ options = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "options" }] : /* 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, defaults to the option itself when not defined. * @group Props */ optionValue = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionValue" }] : /* istanbul ignore next */ [])); /** * Property name or getter function to use as the label of an option group. * @group Props */ optionGroupLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionGroupLabel" }] : /* istanbul ignore next */ [])); /** * Property name or getter function to retrieve the items of a group. * @group Props */ optionGroupChildren = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "optionGroupChildren" }] : /* istanbul ignore next */ [])); /** * Default text to display when no option is selected. * @group Props */ placeholder = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ [])); /** * Selected value of the component. * @group Props */ value = signal(undefined, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ [])); /** * A property to uniquely identify an option. * @group Props */ dataKey = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "dataKey" }] : /* istanbul ignore next */ [])); /** * Identifier of the underlying input element. * @group Props */ inputId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "inputId" }] : /* istanbul ignore next */ [])); /** * Index of the element in tabbing order. * @group Props */ tabindex = input(0, { ...(ngDevMode ? { debugName: "tabindex" } : /* istanbul ignore next */ {}), transform: numberAttribute }); /** * Establishes relationships between the component and label(s) where its value should be one or more element IDs. * @group Props */ ariaLabelledBy = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "ariaLabelledBy" }] : /* istanbul ignore next */ [])); /** * Label of the input for accessibility. * @group Props */ inputLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "inputLabel" }] : /* istanbul ignore next */ [])); /** * Defines a string that labels the input for accessibility. * @group Props */ ariaLabel = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ [])); /** * When enabled, a clear icon is displayed to clear the value. * @group Props */ showClear = input(false, { ...(ngDevMode ? { debugName: "showClear" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Style class of the overlay panel. * @group Props */ panelStyleClass = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "panelStyleClass" }] : /* istanbul ignore next */ [])); /** * Inline style of the overlay panel. * @group Props */ panelStyle = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "panelStyle" }] : /* istanbul ignore next */ [])); /** * Whether to use overlay API feature. The properties of overlay API can be used like an object in it. * @group Props */ overlayOptions = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "overlayOptions" }] : /* 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 }); /** * Whether the dropdown is in loading state. * @group Props */ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * Icon to display in loading state. * @group Props */ loadingIcon = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "loadingIcon" }] : /* istanbul ignore next */ [])); /** * The breakpoint to define the maximum width boundary. * @group Props */ breakpoint = input('960px', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "breakpoint" }] : /* istanbul ignore next */ [])); /** * Specifies the size of the component. * @defaultValue undefined * @group Props */ size = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ [])); /** * Specifies the input variant of the component. * @defaultValue undefined * @group Props */ variant = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "variant" }] : /* istanbul ignore next */ [])); /** * Spans 100% width of the container when enabled. * @defaultValue undefined * @group Props */ fluid = input(undefined, { ...(ngDevMode ? { debugName: "fluid" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); /** * 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 on value change. * @param {CascadeSelectChangeEvent} event - Custom change event. * @group Emits */ onChange = output(); /** * Callback to invoke when a group changes. * @group Emits */ onGroupChange = output(); /** * Callback to invoke when the overlay is shown. * @param {CascadeSelectShowEvent} event - Custom overlay show event. * @group Emits */ onShow = output(); /** * Callback to invoke when the overlay is hidden. * @group Emits */ onHide = output(); /** * Callback to invoke when the clear token is clicked. * @group Emits */ onClear = output(); /** * Callback to invoke before overlay is shown. * @param {CascadeSelectBeforeShowEvent} event - Custom overlay show event. * @group Emits */ onBeforeShow = output(); /** * Callback to invoke before overlay is hidden. * @param {CascadeSelectBeforeHideEvent} event - Custom overlay hide event. * @group Emits */ onBeforeHide = output(); /** * Callback to invoke when input receives focus. * @param {FocusEvent} event - Focus event. * @group Emits */ onFocus = output(); /** * Callback to invoke when input loses focus. * @param {FocusEvent} event - Focus event. * @group Emits */ onBlur = output(); focusInputViewChild = viewChild('focusInput', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusInputViewChild" }] : /* istanbul ignore next */ [])); panelViewChild = viewChild('panel', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "panelViewChild" }] : /* istanbul ignore next */ [])); overlayViewChild = viewChild('overlay', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "overlayViewChild" }] : /* istanbul ignore next */ [])); /** * Custom value template. * @group Templates */ valueTemplate = contentChild('value', { ...(ngDevMode ? { debugName: "valueTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom option template. * @group Templates */ optionTemplate = contentChild('option', { ...(ngDevMode ? { debugName: "optionTemplate" } : /* 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 trigger icon template. * @group Templates */ triggerIconTemplate = contentChild('triggericon', { ...(ngDevMode ? { debugName: "triggerIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom loading icon template. * @group Templates */ loadingIconTemplate = contentChild('loadingicon', { ...(ngDevMode ? { debugName: "loadingIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom option group icon template. * @group Templates */ groupIconTemplate = contentChild('optiongroupicon', { ...(ngDevMode ? { debugName: "groupIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); /** * Custom clear icon template. * @group Templates */ clearIconTemplate = contentChild('clearicon', { ...(ngDevMode ? { debugName: "clearIconTemplate" } : /* istanbul ignore next */ {}), descendants: false }); selectionPath = null; focused = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ [])); overlayVisible = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "overlayVisible" }] : /* istanbul ignore next */ [])); clicked = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "clicked" }] : /* istanbul ignore next */ [])); dirty = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ [])); searchValue; searchTimeout; hideTimeout; focusedOptionInfo = signal({ index: -1, level: 0, parentKey: '' }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusedOptionInfo" }] : /* istanbul ignore next */ [])); activeOptionPath = signal([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "activeOptionPath" }] : /* istanbul ignore next */ [])); processedOptions = []; _componentStyle = inject(CascadeSelectStyle); initialized = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "initialized" }] : /* istanbul ignore next */ [])); $variant = computed(() => this.variant() || this.config.inputVariant(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$variant" }] : /* istanbul ignore next */ [])); $appendTo = computed(() => this.appendTo() || this.config.overlayAppendTo(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "$appendTo" }] : /* istanbul ignore next */ [])); valueTemplateContext = computed(() => ({ $implicit: this.value(), placeholder: this.placeholder() }), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "valueTemplateContext" }] : /* 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 */ [])); disabledAttr = computed(() => (this.$disabled() ? '' : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "disabledAttr" }] : /* istanbul ignore next */ [])); ariaExpanded = computed(() => this.overlayVisible() ?? false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "ariaExpanded" }] : /* istanbul ignore next */ [])); ariaControls = computed(() => (this.overlayVisible() ? this.$id() + '_tree' : null), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "ariaControls" }] : /* istanbul ignore next */ [])); ariaActiveDescendant = computed(() => (this.focused() ? this.focusedOptionId : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "ariaActiveDescendant" }] : /* istanbul ignore next */ [])); showClearIcon = computed(() => this.$filled() && !this.$disabled() && this.showClear(), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "showClearIcon" }] : /* istanbul ignore next */ [])); focusedOptionIdValue = computed(() => (this.focused() ? this.focusedOptionId : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusedOptionIdValue" }] : /* istanbul ignore next */ [])); loadingIconClass = computed(() => { const icon = this.loadingIcon(); if (icon) { return this.cn(this.cx('loadingIcon'), icon + 'pi-spin'); } return this.cn(this.cx('loadingIcon'), icon + ' pi pi-spinner pi-spin'); }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "loadingIconClass" }] : /* istanbul ignore next */ [])); pcFluid = inject(Fluid, { optional: true, host: true, skipSelf: true }); get hasFluid() { return this.fluid() ?? !!this.pcFluid; } onHostClick(event) { this.onContainerClick(event); } get listLabel() { return this.translate(TranslationKeys.ARIA, 'listLabel'); } get focusedOptionId() { return this.focusedOptionInfo().index !== -1 ? `${this.$id()}${isNotEmpty(this.focusedOptionInfo().parentKey) ? '_' + this.focusedOptionInfo().parentKey : ''}_${this.focusedOptionInfo().index}` : null; } get searchResultMessageText() { return isNotEmpty(this.visibleOptions()) ? this.searchMessageText.replaceAll('{0}', String(this.visibleOptions().length)) : this.emptySearchMessageText; } get searchMessageText() { return this.searchMessage() || this.config.translation.searchMessage || ''; } get emptySearchMessageText() { return this.emptySearchMessage() || this.config.translation.emptySearchMessage || ''; } get emptyMessageText() { return this.emptyMessage() || this.config.translation.emptyMessage || ''; } 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}', '1') : this.emptySelectionMessageText; } visibleOptions = computed(() => { const processedOption = this.activeOptionPath().find((p) => p.key === this.focusedOptionInfo().parentKey); return processedOption ? (processedOption.children ?? []) : this.processedOptions; }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "visibleOptions" }] : /* istanbul ignore next */ [])); label = computed(() => { const label = this.placeholder() || 'p-emptylabel'; if (this.hasSelectedOption()) { const activeOptionPath = this.findOptionPathByValue(this.modelValue(), null); const processedOption = isNotEmpty(activeOptionPath) ? activeOptionPath[activeOptionPath.length - 1] : null; return processedOption ? this.getOptionLabel(processedOption.option) : label; } return label; }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ [])); hasSelectedOption() { return isNotEmpty(this.modelValue()); } createProcessedOptions(options, level = 0, parent = {}, parentKey = '') { const processedOptions = []; options && options.forEach((option, index) => { const key = (parentKey !== '' ? parentKey + '_' : '') + index; const newOption = { option, index, level, key, parent, parentKey }; newOption.children = this.createProcessedOptions(this.getOptionGroupChildren(option, level), level + 1, newOption, key); processedOptions.push(newOption); }); return processedOptions; } onInputFocus(event) { if (this.$disabled()) { // For screenreaders return; } this.focused.set(true); this.onFocus.emit(event); } onInputBlur(event) { this.focused.set(false); this.focusedOptionInfo.set({ index: -1, level: 0, parentKey: '' }); this.searchValue = ''; this.onModelTouched(); this.onBlur.emit(event); } onInputKeyDown(event) { if (this.$disabled() || this.loading()) { event.preventDefault(); return; } const metaKey = event.metaKey || event.ctrlKey; 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 'Space': this.onSpaceKey(event); break; case 'Enter': case