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,

815 lines (807 loc) 99.5 kB
export * from 'primeng/types/panelmenu'; import { NgTemplateOutlet } from '@angular/common'; import * as i0 from '@angular/core'; import { Injectable, InjectionToken, input, numberAttribute, booleanAttribute, output, computed, inject, ElementRef, ViewEncapsulation, Component, viewChild, signal, effect, ChangeDetectionStrategy, contentChild, NgModule } from '@angular/core'; import * as i2 from '@angular/router'; import { RouterModule } from '@angular/router'; import { resolve, isNotEmpty, findLast, findSingle, isPrintableCharacter, isEmpty, uuid, equals, focus, getAttribute } from '@primeuix/utils'; import { SharedModule } from 'primeng/api'; import { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent'; import * as i1 from 'primeng/bind'; import { Bind, BindModule } from 'primeng/bind'; import { ChevronDown } from '@primeicons/angular/chevron-down'; import { ChevronRight } from '@primeicons/angular/chevron-right'; import * as i4 from 'primeng/motion'; import { MotionModule } from 'primeng/motion'; import * as i3 from 'primeng/tooltip'; import { TooltipModule } from 'primeng/tooltip'; import { style as style$1 } from '@primeuix/styles/panelmenu'; import { BaseStyle } from 'primeng/base'; const style = /*css*/ ` ${style$1} /*For PrimeNG*/ .p-panelmenu-root-list, .p-panelmenu-submenu, .p-panelmenu-item-link { outline: 0 none; } `; const classes = { root: () => ['p-panelmenu p-component'], panel: 'p-panelmenu-panel', header: ({ instance, item }) => [ 'p-panelmenu-header', { 'p-panelmenu-header-active': instance.isItemActive(item) && !!item.items, 'p-disabled': instance.isItemDisabled(item) } ], headerContent: 'p-panelmenu-header-content', headerLink: 'p-panelmenu-header-link', headerIcon: 'p-panelmenu-header-icon', headerLabel: 'p-panelmenu-header-label', contentContainer: ({ instance, processedItem }) => ['p-panelmenu-content-container', { 'p-panelmenu-expanded': instance.isItemActive(processedItem) }], contentWrapper: 'p-panelmenu-content-wrapper', content: 'p-panelmenu-content', rootList: 'p-panelmenu-root-list', item: ({ instance, processedItem }) => [ 'p-panelmenu-item', { 'p-focus': instance.isItemFocused(processedItem) && !instance.isItemDisabled(processedItem), 'p-disabled': instance.isItemDisabled(processedItem) } ], itemContent: 'p-panelmenu-item-content', itemLink: 'p-panelmenu-item-link', itemIcon: 'p-panelmenu-item-icon', itemLabel: 'p-panelmenu-item-label', submenuIcon: 'p-panelmenu-submenu-icon', submenu: 'p-panelmenu-submenu', separator: 'p-menuitem-separator', badge: 'p-menuitem-badge' }; class PanelMenuStyle extends BaseStyle { name = 'panelmenu'; style = style; classes = classes; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelMenuStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelMenuStyle }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelMenuStyle, decorators: [{ type: Injectable }] }); /** * * PanelMenu is a hybrid of Accordion and Tree components. * * [Live Demo](https://www.primeng.org/panelmenu/) * * @module panelmenustyle * */ var PanelMenuClasses; (function (PanelMenuClasses) { /** * Class name of the root element */ PanelMenuClasses["root"] = "p-panelmenu"; /** * Class name of the panel element */ PanelMenuClasses["panel"] = "p-panelmenu-panel"; /** * Class name of the header element */ PanelMenuClasses["header"] = "p-panelmenu-header"; /** * Class name of the header content element */ PanelMenuClasses["headerContent"] = "p-panelmenu-header-content"; /** * Class name of the header link element */ PanelMenuClasses["headerLink"] = "p-panelmenu-header-link"; /** * Class name of the header icon element */ PanelMenuClasses["headerIcon"] = "p-panelmenu-header-icon"; /** * Class name of the header label element */ PanelMenuClasses["headerLabel"] = "p-panelmenu-header-label"; /** * Class name of the content container element */ PanelMenuClasses["contentContainer"] = "p-panelmenu-content-container"; /** * Class name of the content element */ PanelMenuClasses["content"] = "p-panelmenu-content"; /** * Class name of the root list element */ PanelMenuClasses["rootList"] = "p-panelmenu-root-list"; /** * Class name of the item element */ PanelMenuClasses["item"] = "p-panelmenu-item"; /** * Class name of the item content element */ PanelMenuClasses["itemContent"] = "p-panelmenu-item-content"; /** * Class name of the item link element */ PanelMenuClasses["itemLink"] = "p-panelmenu-item-link"; /** * Class name of the item icon element */ PanelMenuClasses["itemIcon"] = "p-panelmenu-item-icon"; /** * Class name of the item label element */ PanelMenuClasses["itemLabel"] = "p-panelmenu-item-label"; /** * Class name of the submenu icon element */ PanelMenuClasses["submenuIcon"] = "p-panelmenu-submenu-icon"; /** * Class name of the submenu element */ PanelMenuClasses["submenu"] = "p-panelmenu-submenu"; PanelMenuClasses["separator"] = "p-menuitem-separator"; })(PanelMenuClasses || (PanelMenuClasses = {})); const PANELMENU_INSTANCE = new InjectionToken('PANELMENU_INSTANCE'); const PANELMENUSUB_INSTANCE = new InjectionToken('PANELMENUSUB_INSTANCE'); class PanelMenuSub extends BaseComponent { panelId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "panelId" }] : /* istanbul ignore next */ [])); focusedItemId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "focusedItemId" }] : /* istanbul ignore next */ [])); items = input([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ [])); itemTemplate = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "itemTemplate" }] : /* istanbul ignore next */ [])); level = input(0, { ...(ngDevMode ? { debugName: "level" } : /* istanbul ignore next */ {}), transform: numberAttribute }); activeItemPath = input([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "activeItemPath" }] : /* istanbul ignore next */ [])); root = input(false, { ...(ngDevMode ? { debugName: "root" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); tabindex = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "tabindex" }] : /* istanbul ignore next */ [])); parentExpanded = input(false, { ...(ngDevMode ? { debugName: "parentExpanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); motionOptions = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "motionOptions" }] : /* istanbul ignore next */ [])); itemToggle = output(); menuFocus = output(); menuBlur = output(); menuKeyDown = output(); hostClass = computed(() => (this.root() ? this.cn(this.cx('rootList'), this.cx('submenu')) : this.cx('submenu')), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ [])); listViewChild = inject(ElementRef); panelMenu = inject(PANELMENU_INSTANCE); _componentStyle = inject(PanelMenuStyle); bindDirectiveInstance = inject(Bind, { self: true }); $pcPanelMenu = inject(PANELMENU_INSTANCE, { optional: true, skipSelf: true }) ?? undefined; onAfterViewChecked() { this.bindDirectiveInstance.setAttrs(this.ptm(this.root() ? 'rootList' : 'submenu')); } getPTOptions(processedItem, index, key) { return this.ptm(key, { context: { item: processedItem.item, index, active: this.isItemActive(processedItem), focused: this.isItemFocused(processedItem), disabled: this.isItemDisabled(processedItem) } }); } getItemId(processedItem) { return processedItem.item?.id ?? `${this.panelId()}_${processedItem.key}`; } getItemKey(processedItem) { return this.getItemId(processedItem); } getItemClass(processedItem) { return { 'p-panelmenu-item': true, 'p-disabled': this.isItemDisabled(processedItem), 'p-focus': this.isItemFocused(processedItem) }; } getItemProp(processedItem, name, params) { return processedItem && processedItem.item ? resolve(processedItem.item[name], params) : undefined; } getItemLabel(processedItem) { return this.getItemProp(processedItem, 'label'); } isItemExpanded(processedItem) { return processedItem.expanded; } isItemActive(processedItem) { return this.isItemExpanded(processedItem) || this.activeItemPath().some((path) => path && path.key === processedItem.key); } isItemVisible(processedItem) { return this.getItemProp(processedItem, 'visible') !== false; } isItemDisabled(processedItem) { return this.getItemProp(processedItem, 'disabled'); } isItemFocused(processedItem) { return this.focusedItemId() === this.getItemId(processedItem); } isItemGroup(processedItem) { return isNotEmpty(processedItem.items); } getAriaExpanded(processedItem) { return this.isItemGroup(processedItem) ? this.isItemActive(processedItem) : undefined; } getItemTabindex() { return this.parentExpanded() ? '0' : '-1'; } getRouterLinkActiveOptions(processedItem) { return this.getItemProp(processedItem, 'routerLinkActiveOptions') || { exact: false }; } isSeparator(processedItem) { return processedItem.separator; } hasIcon(processedItem) { return !!processedItem.icon; } hasBadge(processedItem) { return !!processedItem.badge; } shouldEscapeLabel(processedItem) { return processedItem.item?.escape !== false; } getItemTemplateContext(processedItem) { return { $implicit: processedItem.item }; } getAriaSetSize() { return this.items().filter((processedItem) => this.isItemVisible(processedItem) && !this.getItemProp(processedItem, 'separator')).length; } getAriaPosInset(index) { return (index - this.items() .slice(0, index) .filter((processedItem) => this.isItemVisible(processedItem) && this.getItemProp(processedItem, 'separator')).length + 1); } onItemClick(event, processedItem) { if (!this.isItemDisabled(processedItem)) { this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item }); this.itemToggle.emit({ processedItem, expanded: !this.isItemActive(processedItem) }); } } onItemToggle(event) { this.itemToggle.emit(event); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelMenuSub, deps: null, target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: PanelMenuSub, isStandalone: true, selector: "ul[pPanelMenuSub]", inputs: { panelId: { classPropertyName: "panelId", publicName: "panelId", isSignal: true, isRequired: false, transformFunction: null }, focusedItemId: { classPropertyName: "focusedItemId", publicName: "focusedItemId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, activeItemPath: { classPropertyName: "activeItemPath", publicName: "activeItemPath", isSignal: true, isRequired: false, transformFunction: null }, root: { classPropertyName: "root", publicName: "root", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, parentExpanded: { classPropertyName: "parentExpanded", publicName: "parentExpanded", isSignal: true, isRequired: false, transformFunction: null }, motionOptions: { classPropertyName: "motionOptions", publicName: "motionOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemToggle: "itemToggle", menuFocus: "menuFocus", menuBlur: "menuBlur", menuKeyDown: "menuKeyDown" }, host: { attributes: { "role": "tree" }, listeners: { "focusin": "menuFocus.emit($event)", "focusout": "menuBlur.emit($event)", "keydown": "menuKeyDown.emit($event)" }, properties: { "class": "hostClass()", "tabindex": "-1", "attr.aria-activedescendant": "focusedItemId", "attr.aria-hidden": "!parentExpanded" } }, providers: [PanelMenuStyle, { provide: PANELMENUSUB_INSTANCE, useExisting: PanelMenuSub }, { provide: PARENT_INSTANCE, useExisting: PanelMenuSub }], usesInheritance: true, hostDirectives: [{ directive: i1.Bind }], ngImport: i0, template: ` @for (processedItem of items(); track processedItem.key; let index = $index) { @if (isSeparator(processedItem)) { <li [class]="cn(cx('separator'), getItemProp(processedItem, 'styleClass'))" role="separator" [pBind]="ptm('separator')"></li> } @else if (isItemVisible(processedItem)) { <li role="treeitem" [attr.id]="getItemId(processedItem)" [attr.aria-label]="getItemProp(processedItem, 'label')" [attr.aria-expanded]="getAriaExpanded(processedItem)" [attr.aria-level]="level() + 1" [attr.aria-setsize]="getAriaSetSize()" [attr.aria-posinset]="getAriaPosInset(index)" [class]="cn(cx('item', { processedItem }), getItemProp(processedItem, 'styleClass'))" [style]="getItemProp(processedItem, 'style')" [pTooltip]="getItemProp(processedItem, 'tooltip')" [pTooltipUnstyled]="unstyled()" [pBind]="getPTOptions(processedItem, index, 'item')" [attr.data-p-disabled]="isItemDisabled(processedItem)" [attr.data-p-focused]="isItemFocused(processedItem)" [tooltipOptions]="getItemProp(processedItem, 'tooltipOptions')" > <div [class]="cx('itemContent')" [pBind]="getPTOptions(processedItem, index, 'itemContent')" (click)="onItemClick($event, processedItem)"> @if (!itemTemplate()) { @if (!getItemProp(processedItem, 'routerLink')) { <a [attr.href]="getItemProp(processedItem, 'url')" [attr.title]="getItemProp(processedItem, 'title')" [attr.data-automationid]="getItemProp(processedItem, 'automationId')" [class]="cn(cx('itemLink'), getItemProp(processedItem, 'linkClass'))" [style]="getItemProp(processedItem, 'linkStyle')" [target]="getItemProp(processedItem, 'target')" [attr.tabindex]="getItemTabindex()" [pBind]="getPTOptions(processedItem, index, 'itemLink')" > @if (isItemGroup(processedItem)) { @if (!panelMenu.submenuIconTemplate()) { @if (isItemActive(processedItem)) { <svg data-p-icon="chevron-down" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } @else { <svg data-p-icon="chevron-right" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } } <ng-container *ngTemplateOutlet="panelMenu.submenuIconTemplate()"></ng-container> } @if (hasIcon(processedItem)) { <span [class]="cn(cx('itemIcon'), getItemProp(processedItem, 'icon'), getItemProp(processedItem, 'iconClass'))" [style]="getItemProp(processedItem, 'iconStyle')" [pBind]="getPTOptions(processedItem, index, 'itemIcon')" ></span> } @if (shouldEscapeLabel(processedItem)) { <span [class]="cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))" [style]="getItemProp(processedItem, 'labelStyle')" [pBind]="getPTOptions(processedItem, index, 'itemLabel')">{{ getItemProp(processedItem, 'label') }}</span> } @else { <span [class]="cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))" [style]="getItemProp(processedItem, 'labelStyle')" [innerHTML]="getItemProp(processedItem, 'label')" [pBind]="getPTOptions(processedItem, index, 'itemLabel')" ></span> } </a> } @else { <a [routerLink]="getItemProp(processedItem, 'routerLink')" [queryParams]="getItemProp(processedItem, 'queryParams')" [routerLinkActive]="'p-panelmenu-item-link-active'" [routerLinkActiveOptions]="getRouterLinkActiveOptions(processedItem)" [class]="cn(cx('itemLink'), getItemProp(processedItem, 'linkClass'))" [style]="getItemProp(processedItem, 'linkStyle')" [target]="getItemProp(processedItem, 'target')" [attr.title]="getItemProp(processedItem, 'title')" [attr.data-automationid]="getItemProp(processedItem, 'automationId')" [fragment]="getItemProp(processedItem, 'fragment')" [queryParamsHandling]="getItemProp(processedItem, 'queryParamsHandling')" [preserveFragment]="getItemProp(processedItem, 'preserveFragment')" [skipLocationChange]="getItemProp(processedItem, 'skipLocationChange')" [replaceUrl]="getItemProp(processedItem, 'replaceUrl')" [state]="getItemProp(processedItem, 'state')" [attr.tabindex]="getItemTabindex()" [pBind]="getPTOptions(processedItem, index, 'itemLink')" > @if (isItemGroup(processedItem)) { @if (!panelMenu.submenuIconTemplate()) { @if (isItemActive(processedItem)) { <svg data-p-icon="chevron-down" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } @else { <svg data-p-icon="chevron-right" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } } <ng-container *ngTemplateOutlet="panelMenu.submenuIconTemplate()"></ng-container> } @if (hasIcon(processedItem)) { <span [class]="cn(cx('itemIcon'), getItemProp(processedItem, 'icon'), getItemProp(processedItem, 'iconClass'))" [style]="getItemProp(processedItem, 'iconStyle')" [pBind]="getPTOptions(processedItem, index, 'itemIcon')" ></span> } @if (getItemProp(processedItem, 'label')) { <span [class]="cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))" [style]="getItemProp(processedItem, 'labelStyle')" [innerHTML]="getItemProp(processedItem, 'label')" [pBind]="getPTOptions(processedItem, index, 'itemLabel')" ></span> } @if (hasBadge(processedItem)) { <span [class]="cn(cx('badge'), getItemProp(processedItem, 'badgeStyleClass'))">{{ processedItem.badge }}</span> } </a> } } @else { <ng-container *ngTemplateOutlet="itemTemplate(); context: getItemTemplateContext(processedItem)"></ng-container> } </div> <div [class]="cx('contentContainer', { processedItem: processedItem })" pMotionName="p-collapsible" [pMotion]="isItemVisible(processedItem) && isItemGroup(processedItem) && isItemExpanded(processedItem)" [pMotionOptions]="motionOptions()" > <div [class]="cx('contentWrapper')" [pBind]="ptm('contentWrapper')"> <ul pPanelMenuSub [id]="getItemId(processedItem) + '_list'" [panelId]="panelId()" [items]="processedItem?.items" [itemTemplate]="itemTemplate()" [focusedItemId]="focusedItemId()" [activeItemPath]="activeItemPath()" [level]="level() + 1" [pt]="pt()" [unstyled]="unstyled()" [parentExpanded]="parentExpanded() && isItemExpanded(processedItem)" (itemToggle)="onItemToggle($event)" [motionOptions]="motionOptions()" ></ul> </div> </div> </li> } } `, isInline: true, dependencies: [{ kind: "component", type: PanelMenuSub, selector: "ul[pPanelMenuSub]", inputs: ["panelId", "focusedItemId", "items", "itemTemplate", "level", "activeItemPath", "root", "tabindex", "parentExpanded", "motionOptions"], outputs: ["itemToggle", "menuFocus", "menuBlur", "menuKeyDown"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: i1.Bind, selector: "[pBind]", inputs: ["pBind"] }, { kind: "component", type: ChevronDown, selector: "svg[data-p-icon=\"chevron-down\"]" }, { kind: "component", type: ChevronRight, selector: "svg[data-p-icon=\"chevron-right\"]" }, { kind: "ngmodule", type: SharedModule }, { kind: "ngmodule", type: BindModule }, { kind: "ngmodule", type: MotionModule }, { kind: "directive", type: i4.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"] }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PanelMenuSub, decorators: [{ type: Component, args: [{ selector: 'ul[pPanelMenuSub]', imports: [NgTemplateOutlet, RouterModule, TooltipModule, ChevronDown, ChevronRight, SharedModule, BindModule, MotionModule], standalone: true, template: ` @for (processedItem of items(); track processedItem.key; let index = $index) { @if (isSeparator(processedItem)) { <li [class]="cn(cx('separator'), getItemProp(processedItem, 'styleClass'))" role="separator" [pBind]="ptm('separator')"></li> } @else if (isItemVisible(processedItem)) { <li role="treeitem" [attr.id]="getItemId(processedItem)" [attr.aria-label]="getItemProp(processedItem, 'label')" [attr.aria-expanded]="getAriaExpanded(processedItem)" [attr.aria-level]="level() + 1" [attr.aria-setsize]="getAriaSetSize()" [attr.aria-posinset]="getAriaPosInset(index)" [class]="cn(cx('item', { processedItem }), getItemProp(processedItem, 'styleClass'))" [style]="getItemProp(processedItem, 'style')" [pTooltip]="getItemProp(processedItem, 'tooltip')" [pTooltipUnstyled]="unstyled()" [pBind]="getPTOptions(processedItem, index, 'item')" [attr.data-p-disabled]="isItemDisabled(processedItem)" [attr.data-p-focused]="isItemFocused(processedItem)" [tooltipOptions]="getItemProp(processedItem, 'tooltipOptions')" > <div [class]="cx('itemContent')" [pBind]="getPTOptions(processedItem, index, 'itemContent')" (click)="onItemClick($event, processedItem)"> @if (!itemTemplate()) { @if (!getItemProp(processedItem, 'routerLink')) { <a [attr.href]="getItemProp(processedItem, 'url')" [attr.title]="getItemProp(processedItem, 'title')" [attr.data-automationid]="getItemProp(processedItem, 'automationId')" [class]="cn(cx('itemLink'), getItemProp(processedItem, 'linkClass'))" [style]="getItemProp(processedItem, 'linkStyle')" [target]="getItemProp(processedItem, 'target')" [attr.tabindex]="getItemTabindex()" [pBind]="getPTOptions(processedItem, index, 'itemLink')" > @if (isItemGroup(processedItem)) { @if (!panelMenu.submenuIconTemplate()) { @if (isItemActive(processedItem)) { <svg data-p-icon="chevron-down" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } @else { <svg data-p-icon="chevron-right" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } } <ng-container *ngTemplateOutlet="panelMenu.submenuIconTemplate()"></ng-container> } @if (hasIcon(processedItem)) { <span [class]="cn(cx('itemIcon'), getItemProp(processedItem, 'icon'), getItemProp(processedItem, 'iconClass'))" [style]="getItemProp(processedItem, 'iconStyle')" [pBind]="getPTOptions(processedItem, index, 'itemIcon')" ></span> } @if (shouldEscapeLabel(processedItem)) { <span [class]="cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))" [style]="getItemProp(processedItem, 'labelStyle')" [pBind]="getPTOptions(processedItem, index, 'itemLabel')">{{ getItemProp(processedItem, 'label') }}</span> } @else { <span [class]="cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))" [style]="getItemProp(processedItem, 'labelStyle')" [innerHTML]="getItemProp(processedItem, 'label')" [pBind]="getPTOptions(processedItem, index, 'itemLabel')" ></span> } </a> } @else { <a [routerLink]="getItemProp(processedItem, 'routerLink')" [queryParams]="getItemProp(processedItem, 'queryParams')" [routerLinkActive]="'p-panelmenu-item-link-active'" [routerLinkActiveOptions]="getRouterLinkActiveOptions(processedItem)" [class]="cn(cx('itemLink'), getItemProp(processedItem, 'linkClass'))" [style]="getItemProp(processedItem, 'linkStyle')" [target]="getItemProp(processedItem, 'target')" [attr.title]="getItemProp(processedItem, 'title')" [attr.data-automationid]="getItemProp(processedItem, 'automationId')" [fragment]="getItemProp(processedItem, 'fragment')" [queryParamsHandling]="getItemProp(processedItem, 'queryParamsHandling')" [preserveFragment]="getItemProp(processedItem, 'preserveFragment')" [skipLocationChange]="getItemProp(processedItem, 'skipLocationChange')" [replaceUrl]="getItemProp(processedItem, 'replaceUrl')" [state]="getItemProp(processedItem, 'state')" [attr.tabindex]="getItemTabindex()" [pBind]="getPTOptions(processedItem, index, 'itemLink')" > @if (isItemGroup(processedItem)) { @if (!panelMenu.submenuIconTemplate()) { @if (isItemActive(processedItem)) { <svg data-p-icon="chevron-down" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } @else { <svg data-p-icon="chevron-right" [class]="cx('submenuIcon')" [pBind]="getPTOptions(processedItem, index, 'submenuIcon')" /> } } <ng-container *ngTemplateOutlet="panelMenu.submenuIconTemplate()"></ng-container> } @if (hasIcon(processedItem)) { <span [class]="cn(cx('itemIcon'), getItemProp(processedItem, 'icon'), getItemProp(processedItem, 'iconClass'))" [style]="getItemProp(processedItem, 'iconStyle')" [pBind]="getPTOptions(processedItem, index, 'itemIcon')" ></span> } @if (getItemProp(processedItem, 'label')) { <span [class]="cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))" [style]="getItemProp(processedItem, 'labelStyle')" [innerHTML]="getItemProp(processedItem, 'label')" [pBind]="getPTOptions(processedItem, index, 'itemLabel')" ></span> } @if (hasBadge(processedItem)) { <span [class]="cn(cx('badge'), getItemProp(processedItem, 'badgeStyleClass'))">{{ processedItem.badge }}</span> } </a> } } @else { <ng-container *ngTemplateOutlet="itemTemplate(); context: getItemTemplateContext(processedItem)"></ng-container> } </div> <div [class]="cx('contentContainer', { processedItem: processedItem })" pMotionName="p-collapsible" [pMotion]="isItemVisible(processedItem) && isItemGroup(processedItem) && isItemExpanded(processedItem)" [pMotionOptions]="motionOptions()" > <div [class]="cx('contentWrapper')" [pBind]="ptm('contentWrapper')"> <ul pPanelMenuSub [id]="getItemId(processedItem) + '_list'" [panelId]="panelId()" [items]="processedItem?.items" [itemTemplate]="itemTemplate()" [focusedItemId]="focusedItemId()" [activeItemPath]="activeItemPath()" [level]="level() + 1" [pt]="pt()" [unstyled]="unstyled()" [parentExpanded]="parentExpanded() && isItemExpanded(processedItem)" (itemToggle)="onItemToggle($event)" [motionOptions]="motionOptions()" ></ul> </div> </div> </li> } } `, encapsulation: ViewEncapsulation.None, providers: [PanelMenuStyle, { provide: PANELMENUSUB_INSTANCE, useExisting: PanelMenuSub }, { provide: PARENT_INSTANCE, useExisting: PanelMenuSub }], host: { '[class]': 'hostClass()', role: 'tree', '[tabindex]': '-1', '[attr.aria-activedescendant]': 'focusedItemId', '[attr.aria-hidden]': '!parentExpanded', '(focusin)': 'menuFocus.emit($event)', '(focusout)': 'menuBlur.emit($event)', '(keydown)': 'menuKeyDown.emit($event)' }, hostDirectives: [Bind] }] }], propDecorators: { panelId: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelId", required: false }] }], focusedItemId: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusedItemId", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], itemTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], activeItemPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeItemPath", required: false }] }], root: [{ type: i0.Input, args: [{ isSignal: true, alias: "root", required: false }] }], tabindex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabindex", required: false }] }], parentExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentExpanded", required: false }] }], motionOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "motionOptions", required: false }] }], itemToggle: [{ type: i0.Output, args: ["itemToggle"] }], menuFocus: [{ type: i0.Output, args: ["menuFocus"] }], menuBlur: [{ type: i0.Output, args: ["menuBlur"] }], menuKeyDown: [{ type: i0.Output, args: ["menuKeyDown"] }] } }); class PanelMenuList extends BaseComponent { panelId = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "panelId" }] : /* istanbul ignore next */ [])); id = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "id" }] : /* istanbul ignore next */ [])); items = input([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ [])); itemTemplate = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "itemTemplate" }] : /* istanbul ignore next */ [])); parentExpanded = input(false, { ...(ngDevMode ? { debugName: "parentExpanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); root = input(false, { ...(ngDevMode ? { debugName: "root" } : /* istanbul ignore next */ {}), transform: booleanAttribute }); tabindex = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "tabindex" }] : /* istanbul ignore next */ [])); activeItem = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "activeItem" }] : /* istanbul ignore next */ [])); motionOptions = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "motionOptions" }] : /* istanbul ignore next */ [])); itemToggle = output(); headerFocus = output(); subMenuViewChild = viewChild('submenu', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "subMenuViewChild" }] : /* istanbul ignore next */ [])); searchTimeout = null; searchValue = ''; focused = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ [])); focusedItem = signal(null, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusedItem" }] : /* istanbul ignore next */ [])); activeItemPath = signal([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "activeItemPath" }] : /* istanbul ignore next */ [])); processedItems = signal([], /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "processedItems" }] : /* istanbul ignore next */ [])); visibleItems = computed(() => { const processedItems = this.processedItems(); return this.flatItems(processedItems); }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ [])); focusedItemId = computed(() => { const focusedItem = this.focusedItem(); return focusedItem?.item?.id ? focusedItem.item.id : isNotEmpty(focusedItem) ? `${this.panelId()}_${focusedItem.key}` : undefined; }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "focusedItemId" }] : /* istanbul ignore next */ [])); activeFocusedItemId = computed(() => (this.focused() ? this.focusedItemId() : undefined), /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "activeFocusedItemId" }] : /* istanbul ignore next */ [])); constructor() { super(); effect(() => { const items = this.items(); if (items) { this.processedItems.set(this.createProcessedItems(items)); } }); } getItemProp(processedItem, name) { return processedItem && processedItem.item ? resolve(processedItem.item[name]) : undefined; } getItemLabel(processedItem) { return this.getItemProp(processedItem, 'label'); } isItemVisible(processedItem) { return this.getItemProp(processedItem, 'visible') !== false; } isItemDisabled(processedItem) { return this.getItemProp(processedItem, 'disabled'); } isItemActive(processedItem) { return this.activeItemPath().some((path) => path.key === processedItem.parentKey); } isItemGroup(processedItem) { return isNotEmpty(processedItem.items); } isElementInPanel(event, element) { const panel = event.currentTarget?.closest('[data-pc-name="panelmenu"]'); return panel && element && panel.contains(element); } isItemMatched(processedItem) { return this.isValidItem(processedItem) && this.getItemLabel(processedItem)?.toLocaleLowerCase().startsWith(this.searchValue.toLocaleLowerCase()); } isVisibleItem(processedItem) { return !!processedItem && (processedItem.level === 0 || this.isItemActive(processedItem)) && this.isItemVisible(processedItem); } isValidItem(processedItem) { return !!processedItem && !this.isItemDisabled(processedItem) && !processedItem.separator; } findFirstItem() { return this.visibleItems().find((processedItem) => this.isValidItem(processedItem)); } findLastItem() { return findLast(this.visibleItems(), (processedItem) => this.isValidItem(processedItem)); } findItemByEventTarget(target) { let parentNode = target; while (parentNode && parentNode.tagName?.toLowerCase() !== 'li') { parentNode = parentNode?.parentNode; } return parentNode?.id ? this.visibleItems().find((processedItem) => this.isValidItem(processedItem) && `${this.panelId()}_${processedItem.key}` === parentNode.id) : undefined; } createProcessedItems(items, level = 0, parent = {}, parentKey = '') { const processedItems = []; items && items.forEach((item, index) => { const key = (parentKey !== '' ? parentKey + '_' : '') + index; const newItem = { icon: item.icon, expanded: item.expanded, separator: item.separator, item, index, level, key, parent: parent, parentKey }; newItem.items = this.createProcessedItems(item.items || [], level + 1, newItem, key); processedItems.push(newItem); }); return processedItems; } findProcessedItemByItemKey(key, processedItems, level = 0) { processedItems = processedItems || this.processedItems(); if (processedItems && processedItems.length) { for (let i = 0; i < processedItems.length; i++) { const processedItem = processedItems[i]; if (this.getItemProp(processedItem, 'key') === key) return processedItem; const matchedItem = this.findProcessedItemByItemKey(key, processedItem.items, level + 1); if (matchedItem) return matchedItem; } } return undefined; } flatItems(processedItems, processedFlattenItems = []) { processedItems && processedItems.forEach((processedItem) => { if (this.isVisibleItem(processedItem)) { processedFlattenItems.push(processedItem); this.flatItems(processedItem.items || [], processedFlattenItems); } }); return processedFlattenItems; } changeFocusedItem(event) { const { originalEvent, processedItem, focusOnNext, selfCheck, allowHeaderFocus = true } = event; const currentFocusedItem = this.focusedItem(); if (isNotEmpty(currentFocusedItem) && currentFocusedItem.key !== processedItem.key) { this.focusedItem.set(processedItem); this.scrollInView(); } else if (allowHeaderFocus) { this.headerFocus.emit({ originalEvent, focusOnNext, selfCheck }); } } scrollInView() { const element = findSingle(this.subMenuViewChild()?.listViewChild.nativeElement, `li[id="${this.focusedItemId()}"]`); if (element) { element.scrollIntoView?.({ block: 'nearest', inline: 'nearest' }); } } onFocus(event) { if (!this.focused()) { this.focused.set(true); const focusedItem = this.focusedItem() || (this.isElementInPanel(event, event.relatedTarget) ? this.findItemByEventTarget(event.target) || this.findFirstItem() : this.findLastItem()); if (event.relatedTarget !== null) this.focusedItem.set(focusedItem ?? null); } } onBlur(event) { const target = event.relatedTarget; if (this.focused() && !this.el.nativeElement.contains(target)) { this.focused.set(false); this.focusedItem.set(null); this.searchValue = ''; } } onItemToggle(event) { const { processedItem, expanded } = event; // Update the original item object's 'expanded' property if (processedItem.item) { processedItem.item.expanded = expanded; } // Update the expanded property in the existing processedItem processedItem.expanded = expanded; // Trigger signal update without recreating the entire tree this.processedItems.update((items) => [...items]); // Update activeItemPath const activeItemPath = this.activeItemPath().filter((p) => p.parentKey !== processedItem.parentKey); if (expanded) { activeItemPath.push(processedItem); } this.activeItemPath.set(activeItemPath); // Update focusedItem this.focusedItem.set(processedItem); } onKeyDown(event) { 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); bre