UNPKG

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 94.6 kB
{"version":3,"file":"primeng-panelmenu.mjs","sources":["../../src/panelmenu/style/panelmenustyle.ts","../../src/panelmenu/panelmenu.ts","../../src/panelmenu/primeng-panelmenu.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { BaseStyle } from 'primeng/base';\n\nconst theme = ({ dt }) => `\n.p-panelmenu {\n display: flex;\n flex-direction: column;\n gap: ${dt('panelmenu.gap')};\n}\n\n.p-panelmenu-panel {\n background: ${dt('panelmenu.panel.background')};\n border-width: ${dt('panelmenu.panel.border.width')};\n border-style: solid;\n border-color: ${dt('panelmenu.panel.border.color')};\n color: ${dt('panelmenu.panel.color')};\n border-radius: ${dt('panelmenu.panel.border.radius')};\n padding: ${dt('panelmenu.panel.padding')};\n}\n\n.p-panelmenu-panel:first-child {\n border-width: ${dt('panelmenu.panel.first.border.width')};\n border-start-start-radius: ${dt('panelmenu.panel.first.top.border.radius')};\n border-start-end-radius: ${dt('panelmenu.panel.first.top.border.radius')};\n}\n\n.p-panelmenu-panel:last-child {\n border-width: ${dt('panelmenu.panel.last.border.width')};\n border-end-start-radius: ${dt('panelmenu.panel.last.bottom.border.radius')};\n border-end-end-radius: ${dt('panelmenu.panel.last.bottom.border.radius')};\n}\n\n.p-panelmenu-header {\n outline: 0 none;\n}\n\n.p-panelmenu-header-content {\n border-radius: ${dt('panelmenu.item.border.radius')};\n transition: background ${dt('panelmenu.transition.duration')}, color ${dt('panelmenu.transition.duration')}, outline-color ${dt('panelmenu.transition.duration')}, box-shadow ${dt('panelmenu.transition.duration')};\n outline-color: transparent;\n color: ${dt('panelmenu.item.color')};\n}\n\n.p-panelmenu-header-link {\n display: flex;\n gap: ${dt('panelmenu.item.gap')};\n padding: ${dt('panelmenu.item.padding')};\n align-items: center;\n user-select: none;\n cursor: pointer;\n position: relative;\n text-decoration: none;\n color: inherit;\n}\n\n.p-panelmenu-header-icon,\n.p-panelmenu-item-icon {\n color: ${dt('panelmenu.item.icon.color')};\n}\n\n.p-panelmenu-submenu-icon {\n color: ${dt('panelmenu.submenu.icon.color')};\n}\n\n.p-panelmenu-header:not(.p-panelmenu-header-active) .p-panelmenu-header-content .p-panelmenu-submenu-icon:dir(rtl) {\n transform: rotate(180deg);\n}\n\n.p-panelmenu-header:not(.p-disabled):focus-visible .p-panelmenu-header-content {\n background: ${dt('panelmenu.item.focus.background')};\n color: ${dt('panelmenu.item.focus.color')};\n}\n\n.p-panelmenu-header:not(.p-disabled):focus-visible .p-panelmenu-header-content .p-panelmenu-header-icon {\n color: ${dt('panelmenu.item.icon.focus.color')};\n}\n\n.p-panelmenu-header:not(.p-disabled):focus-visible .p-panelmenu-header-content .p-panelmenu-submenu-icon {\n color: ${dt('panelmenu.submenu.icon.focus.color')};\n}\n\n.p-panelmenu-header:not(.p-disabled) .p-panelmenu-header-content:hover {\n background: ${dt('panelmenu.item.focus.background')};\n color: ${dt('panelmenu.item.focus.color')};\n}\n\n.p-panelmenu-header:not(.p-disabled) .p-panelmenu-header-content:hover .p-panelmenu-header-icon {\n color: ${dt('panelmenu.item.icon.focus.color')};\n}\n\n.p-panelmenu-header:not(.p-disabled) .p-panelmenu-header-content:hover .p-panelmenu-submenu-icon {\n color: ${dt('panelmenu.submenu.icon.focus.color')};\n}\n\n.p-panelmenu-submenu {\n margin: 0;\n padding: 0 0 0 ${dt('panelmenu.submenu.indent')};\n outline: 0;\n list-style: none;\n}\n\n.p-panelmenu-submenu:dir(rtl) {\n padding: 0 ${dt('panelmenu.submenu.indent')} 0 0;\n}\n\n.p-panelmenu-item-link {\n display: flex;\n gap: ${dt('panelmenu.item.gap')};\n padding: ${dt('panelmenu.item.padding')};\n align-items: center;\n user-select: none;\n cursor: pointer;\n text-decoration: none;\n color: inherit;\n position: relative;\n overflow: hidden;\n}\n\n.p-panelmenu-item-label {\n line-height: 1;\n}\n\n.p-panelmenu-item-content {\n border-radius: ${dt('panelmenu.item.border.radius')};\n transition: background ${dt('panelmenu.transition.duration')}, color ${dt('panelmenu.transition.duration')}, outline-color ${dt('panelmenu.transition.duration')}, box-shadow ${dt('panelmenu.transition.duration')};\n color: ${dt('panelmenu.item.color')};\n outline-color: transparent;\n}\n\n.p-panelmenu-item.p-focus > .p-panelmenu-item-content {\n background: ${dt('panelmenu.item.focus.background')};\n color: ${dt('panelmenu.item.focus.color')};\n}\n\n.p-panelmenu-item.p-focus > .p-panelmenu-item-content .p-panelmenu-item-icon {\n color: ${dt('panelmenu.item.focus.color')};\n}\n\n.p-panelmenu-item.p-focus > .p-panelmenu-item-content .p-panelmenu-submenu-icon {\n color: ${dt('panelmenu.submenu.icon.focus.color')};\n}\n\n.p-panelmenu-item:not(.p-disabled) > .p-panelmenu-item-content:hover {\n background: ${dt('panelmenu.item.focus.background')};\n color: ${dt('panelmenu.item.focus.color')};\n}\n\n.p-panelmenu-item:not(.p-disabled) > .p-panelmenu-item-content:hover .p-panelmenu-item-icon {\n color: ${dt('panelmenu.item.icon.focus.color')};\n}\n\n.p-panelmenu-item:not(.p-disabled) > .p-panelmenu-item-content:hover .p-panelmenu-submenu-icon {\n color: ${dt('panelmenu.submenu.icon.focus.color')};\n}\n\n\n/*For PrimeNG*/\n\n.p-panelmenu-item:not(.ng-animating) {\n overflow: hidden;\n}\n\n.p-panelmenu-panel {\n overflow: hidden;\n}\n\n`;\n\nconst classes = {\n root: 'p-panelmenu p-component',\n panel: 'p-panelmenu-panel',\n header: ({ instance, item }) => [\n 'p-panelmenu-header',\n {\n 'p-panelmenu-header-active': instance.isItemActive(item) && !!item.items,\n 'p-disabled': instance.isItemDisabled(item)\n }\n ],\n headerContent: 'p-panelmenu-header-content',\n headerLink: 'p-panelmenu-header-link',\n headerIcon: 'p-panelmenu-header-icon',\n headerLabel: 'p-panelmenu-header-label',\n contentContainer: 'p-panelmenu-content-container',\n content: 'p-panelmenu-content',\n rootList: 'p-panelmenu-root-list',\n item: ({ instance, processedItem }) => [\n 'p-panelmenu-item',\n {\n 'p-focus': instance.isItemFocused(processedItem),\n 'p-disabled': instance.isItemDisabled(processedItem)\n }\n ],\n itemContent: 'p-panelmenu-item-content',\n itemLink: 'p-panelmenu-item-link',\n itemIcon: 'p-panelmenu-item-icon',\n itemLabel: 'p-panelmenu-item-label',\n submenuIcon: 'p-panelmenu-submenu-icon',\n submenu: 'p-panelmenu-submenu',\n separator: 'p-menuitem-separator'\n};\n\n@Injectable()\nexport class PanelMenuStyle extends BaseStyle {\n name = 'panelmenu';\n\n theme = theme;\n\n classes = classes;\n}\n\n/**\n *\n * PanelMenu is a hybrid of Accordion and Tree components.\n *\n * [Live Demo](https://www.primeng.org/panelmenu/)\n *\n * @module panelmenustyle\n *\n */\nexport enum PanelMenuClasses {\n /**\n * Class name of the root element\n */\n root = 'p-panelmenu',\n /**\n * Class name of the panel element\n */\n panel = 'p-panelmenu-panel',\n /**\n * Class name of the header element\n */\n header = 'p-panelmenu-header',\n /**\n * Class name of the header content element\n */\n headerContent = 'p-panelmenu-header-content',\n /**\n * Class name of the header link element\n */\n headerLink = 'p-panelmenu-header-link',\n /**\n * Class name of the header icon element\n */\n headerIcon = 'p-panelmenu-header-icon',\n /**\n * Class name of the header label element\n */\n headerLabel = 'p-panelmenu-header-label',\n /**\n * Class name of the content container element\n */\n contentContainer = 'p-panelmenu-content-container',\n /**\n * Class name of the content element\n */\n content = 'p-panelmenu-content',\n /**\n * Class name of the root list element\n */\n rootList = 'p-panelmenu-root-list',\n /**\n * Class name of the item element\n */\n item = 'p-panelmenu-item',\n /**\n * Class name of the item content element\n */\n itemContent = 'p-panelmenu-item-content',\n /**\n * Class name of the item link element\n */\n itemLink = 'p-panelmenu-item-link',\n /**\n * Class name of the item icon element\n */\n itemIcon = 'p-panelmenu-item-icon',\n /**\n * Class name of the item label element\n */\n itemLabel = 'p-panelmenu-item-label',\n /**\n * Class name of the submenu icon element\n */\n submenuIcon = 'p-panelmenu-submenu-icon',\n /**\n * Class name of the submenu element\n */\n submenu = 'p-panelmenu-submenu',\n separator = 'p-menuitem-separator'\n}\n\nexport interface PanelMenuStyle extends BaseStyle {}\n","import { animate, state, style, transition, trigger } from '@angular/animations';\nimport { CommonModule } from '@angular/common';\nimport {\n AfterContentInit,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ContentChild,\n ContentChildren,\n ElementRef,\n EventEmitter,\n forwardRef,\n inject,\n Input,\n NgModule,\n numberAttribute,\n OnChanges,\n Output,\n QueryList,\n signal,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { equals, findLast, findSingle, focus, getAttribute, isEmpty, isNotEmpty, isPrintableCharacter, resolve, uuid } from '@primeuix/utils';\nimport { MenuItem, PrimeTemplate, SharedModule } from 'primeng/api';\nimport { BaseComponent } from 'primeng/basecomponent';\nimport { AngleDownIcon, AngleRightIcon, ChevronDownIcon, ChevronRightIcon } from 'primeng/icons';\nimport { TooltipModule } from 'primeng/tooltip';\nimport { PanelMenuStyle } from './style/panelmenustyle';\n\n@Component({\n selector: 'p-panelMenuSub, p-panelmenu-sub',\n imports: [CommonModule, RouterModule, TooltipModule, AngleDownIcon, AngleRightIcon, SharedModule],\n standalone: true,\n template: `\n <ul\n #list\n [ngClass]=\"{ 'p-panelmenu-submenu': true }\"\n role=\"tree\"\n [tabindex]=\"-1\"\n [attr.aria-activedescendant]=\"focusedItemId\"\n [attr.data-pc-section]=\"'menu'\"\n [attr.aria-hidden]=\"!parentExpanded\"\n (focusin)=\"menuFocus.emit($event)\"\n (focusout)=\"menuBlur.emit($event)\"\n (keydown)=\"menuKeyDown.emit($event)\"\n >\n <ng-template ngFor let-processedItem let-index=\"index\" [ngForOf]=\"items\">\n <li *ngIf=\"processedItem.separator\" class=\"p-menuitem-separator\" role=\"separator\"></li>\n <li\n *ngIf=\"!processedItem.separator && isItemVisible(processedItem)\"\n [ngClass]=\"getItemClass(processedItem)\"\n role=\"treeitem\"\n [attr.id]=\"getItemId(processedItem)\"\n [attr.aria-label]=\"getItemProp(processedItem, 'label')\"\n [attr.aria-expanded]=\"isItemGroup(processedItem) ? isItemActive(processedItem) : undefined\"\n [attr.aria-level]=\"level + 1\"\n [attr.aria-setsize]=\"getAriaSetSize()\"\n [attr.aria-posinset]=\"getAriaPosInset(index)\"\n [class]=\"getItemProp(processedItem, 'styleClass')\"\n [class.p-hidden]=\"processedItem.visible === false\"\n [class.p-focus]=\"isItemFocused(processedItem) && !isItemDisabled(processedItem)\"\n [ngStyle]=\"getItemProp(processedItem, 'style')\"\n [pTooltip]=\"getItemProp(processedItem, 'tooltip')\"\n [attr.data-p-disabled]=\"isItemDisabled(processedItem)\"\n [tooltipOptions]=\"getItemProp(processedItem, 'tooltipOptions')\"\n >\n <div class=\"p-panelmenu-item-content\" (click)=\"onItemClick($event, processedItem)\">\n <ng-container *ngIf=\"!itemTemplate\">\n <a\n *ngIf=\"!getItemProp(processedItem, 'routerLink')\"\n [attr.href]=\"getItemProp(processedItem, 'url')\"\n class=\"p-panelmenu-item-link\"\n [ngClass]=\"{ 'p-disabled': getItemProp(processedItem, 'disabled') }\"\n [target]=\"getItemProp(processedItem, 'target')\"\n [attr.data-pc-section]=\"'action'\"\n [attr.tabindex]=\"!!parentExpanded ? '0' : '-1'\"\n >\n <ng-container *ngIf=\"isItemGroup(processedItem)\">\n <ng-container *ngIf=\"!panelMenu.submenuIconTemplate && !panelMenu._submenuIconTemplate\">\n <AngleDownIcon [styleClass]=\"'p-panelmenu-submenu-icon'\" *ngIf=\"isItemActive(processedItem)\" [ngStyle]=\"getItemProp(processedItem, 'iconStyle')\" />\n <AngleRightIcon [styleClass]=\"'p-panelmenu-submenu-icon'\" *ngIf=\"!isItemActive(processedItem)\" [ngStyle]=\"getItemProp(processedItem, 'iconStyle')\" />\n </ng-container>\n <ng-template *ngTemplateOutlet=\"panelMenu.submenuIconTemplate || panelMenu._submenuIconTemplate\"></ng-template>\n </ng-container>\n <span class=\"p-panelmenu-submenu-icon\" [ngClass]=\"processedItem.icon\" *ngIf=\"processedItem.icon\" [ngStyle]=\"getItemProp(processedItem, 'iconStyle')\"></span>\n <span class=\"p-panelmenu-item-label\" *ngIf=\"processedItem.item?.escape !== false; else htmlLabel\">{{ getItemProp(processedItem, 'label') }}</span>\n <ng-template #htmlLabel><span class=\"p-panelmenu-item-label\" [innerHTML]=\"getItemProp(processedItem, 'label')\"></span></ng-template>\n <span class=\"p-menuitem-badge\" *ngIf=\"processedItem.badge\" [ngClass]=\"processedItem.badgeStyleClass\">{{ processedItem.badge }}</span>\n </a>\n <a\n *ngIf=\"getItemProp(processedItem, 'routerLink')\"\n [routerLink]=\"getItemProp(processedItem, 'routerLink')\"\n [queryParams]=\"getItemProp(processedItem, 'queryParams')\"\n [routerLinkActive]=\"'p-panelmenu-item-link-active'\"\n [routerLinkActiveOptions]=\"getItemProp(processedItem, 'routerLinkActiveOptions') || { exact: false }\"\n class=\"p-panelmenu-item-link\"\n [ngClass]=\"{ 'p-disabled': getItemProp(processedItem, 'disabled') }\"\n [target]=\"getItemProp(processedItem, 'target')\"\n [attr.title]=\"getItemProp(processedItem, 'title')\"\n [fragment]=\"getItemProp(processedItem, 'fragment')\"\n [queryParamsHandling]=\"getItemProp(processedItem, 'queryParamsHandling')\"\n [preserveFragment]=\"getItemProp(processedItem, 'preserveFragment')\"\n [skipLocationChange]=\"getItemProp(processedItem, 'skipLocationChange')\"\n [replaceUrl]=\"getItemProp(processedItem, 'replaceUrl')\"\n [state]=\"getItemProp(processedItem, 'state')\"\n [attr.data-pc-section]=\"'action'\"\n [attr.tabindex]=\"!!parentExpanded ? '0' : '-1'\"\n >\n <ng-container *ngIf=\"isItemGroup(processedItem)\">\n <ng-container *ngIf=\"!panelMenu.submenuIconTemplate && !panelMenu._submenuIconTemplate\">\n <AngleDownIcon *ngIf=\"isItemActive(processedItem)\" [styleClass]=\"'p-panelmenu-submenu-icon'\" [ngStyle]=\"getItemProp(processedItem, 'iconStyle')\" />\n <AngleRightIcon *ngIf=\"!isItemActive(processedItem)\" [styleClass]=\"'p-panelmenu-submenu-icon'\" [ngStyle]=\"getItemProp(processedItem, 'iconStyle')\" />\n </ng-container>\n <ng-template *ngTemplateOutlet=\"panelMenu.submenuIconTemplate && panelMenu._submenuIconTemplate\"></ng-template>\n </ng-container>\n <span class=\"p-panelmenu-submenu-icon\" [ngClass]=\"processedItem.icon\" *ngIf=\"processedItem.icon\" [ngStyle]=\"getItemProp(processedItem, 'iconStyle')\"></span>\n <span class=\"p-panelmenu-item-label\" *ngIf=\"getItemProp(processedItem, 'escape') !== false; else htmlRouteLabel\">{{ getItemProp(processedItem, 'label') }}</span>\n <ng-template #htmlRouteLabel><span class=\"p-panelmenu-item-label\" [innerHTML]=\"getItemProp(processedItem, 'label')\"></span></ng-template>\n <span class=\"p-menuitem-badge\" *ngIf=\"processedItem.badge\" [ngClass]=\"getItemProp(processedItem, 'badgeStyleClass')\">{{ getItemProp(processedItem, 'badge') }}</span>\n </a>\n </ng-container>\n <ng-container *ngIf=\"itemTemplate\">\n <ng-template *ngTemplateOutlet=\"itemTemplate; context: { $implicit: processedItem.item }\"></ng-template>\n </ng-container>\n </div>\n <div class=\"p-toggleable-content\" [@submenu]=\"getAnimation(processedItem)\">\n <p-panelmenu-sub\n *ngIf=\"isItemVisible(processedItem) && isItemGroup(processedItem) && isItemExpanded(processedItem)\"\n [id]=\"getItemId(processedItem) + '_list'\"\n [panelId]=\"panelId\"\n [items]=\"processedItem?.items\"\n [itemTemplate]=\"itemTemplate\"\n [transitionOptions]=\"transitionOptions\"\n [focusedItemId]=\"focusedItemId\"\n [activeItemPath]=\"activeItemPath\"\n [level]=\"level + 1\"\n [parentExpanded]=\"!!parentExpanded && isItemExpanded(processedItem)\"\n (itemToggle)=\"onItemToggle($event)\"\n ></p-panelmenu-sub>\n </div>\n </li>\n </ng-template>\n </ul>\n `,\n animations: [\n trigger('submenu', [\n state(\n 'hidden',\n style({\n height: '0'\n })\n ),\n state(\n 'visible',\n style({\n height: '*'\n })\n ),\n transition('visible <=> hidden', [animate('{{transitionParams}}')]),\n transition('void => *', animate(0))\n ])\n ],\n encapsulation: ViewEncapsulation.None\n})\nexport class PanelMenuSub extends BaseComponent {\n @Input() panelId: string | undefined;\n\n @Input() focusedItemId: string | undefined;\n\n @Input() items: any[];\n\n @Input() itemTemplate: TemplateRef<any> | undefined;\n\n @Input({ transform: numberAttribute }) level: number = 0;\n\n @Input() activeItemPath: any[];\n\n @Input({ transform: booleanAttribute }) root: boolean | undefined;\n\n @Input({ transform: numberAttribute }) tabindex: number | undefined;\n\n @Input() transitionOptions: string | undefined;\n\n @Input({ transform: booleanAttribute }) parentExpanded: boolean | undefined;\n\n @Output() itemToggle: EventEmitter<any> = new EventEmitter<any>();\n\n @Output() menuFocus: EventEmitter<any> = new EventEmitter<any>();\n\n @Output() menuBlur: EventEmitter<any> = new EventEmitter<any>();\n\n @Output() menuKeyDown: EventEmitter<any> = new EventEmitter<any>();\n\n @ViewChild('list') listViewChild: ElementRef;\n\n panelMenu: PanelMenu = inject(forwardRef(() => PanelMenu));\n\n getItemId(processedItem) {\n return processedItem.item?.id ?? `${this.panelId}_${processedItem.key}`;\n }\n\n getItemKey(processedItem) {\n return this.getItemId(processedItem);\n }\n\n getItemClass(processedItem) {\n return {\n 'p-panelmenu-item': true,\n 'p-disabled': this.isItemDisabled(processedItem),\n 'p-focus': this.isItemFocused(processedItem)\n };\n }\n\n getItemProp(processedItem, name?, params?) {\n return processedItem && processedItem.item ? resolve(processedItem.item[name], params) : undefined;\n }\n\n getItemLabel(processedItem) {\n return this.getItemProp(processedItem, 'label');\n }\n\n isItemExpanded(processedItem) {\n return processedItem.expanded;\n }\n\n isItemActive(processedItem) {\n return this.isItemExpanded(processedItem) || this.activeItemPath.some((path) => path && path.key === processedItem.key);\n }\n\n isItemVisible(processedItem) {\n return this.getItemProp(processedItem, 'visible') !== false;\n }\n\n isItemDisabled(processedItem) {\n return this.getItemProp(processedItem, 'disabled');\n }\n\n isItemFocused(processedItem) {\n return this.focusedItemId === this.getItemId(processedItem);\n }\n\n isItemGroup(processedItem) {\n return isNotEmpty(processedItem.items);\n }\n\n getAnimation(processedItem) {\n return this.isItemActive(processedItem) ? { value: 'visible', params: { transitionParams: this.transitionOptions, height: '*' } } : { value: 'hidden', params: { transitionParams: this.transitionOptions, height: '0' } };\n }\n\n getAriaSetSize() {\n return this.items.filter((processedItem) => this.isItemVisible(processedItem) && !this.getItemProp(processedItem, 'separator')).length;\n }\n\n getAriaPosInset(index) {\n return index - this.items.slice(0, index).filter((processedItem) => this.isItemVisible(processedItem) && this.getItemProp(processedItem, 'separator')).length + 1;\n }\n\n onItemClick(event, processedItem) {\n if (!this.isItemDisabled(processedItem)) {\n this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item });\n this.itemToggle.emit({ processedItem, expanded: !this.isItemActive(processedItem) });\n }\n }\n\n onItemToggle(event) {\n this.itemToggle.emit(event);\n }\n}\n\n@Component({\n selector: 'p-panelMenuList, p-panel-menu-list',\n imports: [CommonModule, PanelMenuSub, RouterModule, TooltipModule, SharedModule],\n standalone: true,\n template: `\n <p-panelmenu-sub\n #submenu\n [root]=\"true\"\n [id]=\"panelId + '_list'\"\n [panelId]=\"panelId\"\n [tabindex]=\"tabindex\"\n [itemTemplate]=\"itemTemplate\"\n [focusedItemId]=\"focused ? focusedItemId : undefined\"\n [activeItemPath]=\"activeItemPath()\"\n [transitionOptions]=\"transitionOptions\"\n [items]=\"processedItems()\"\n [parentExpanded]=\"parentExpanded\"\n (itemToggle)=\"onItemToggle($event)\"\n (keydown)=\"onKeyDown($event)\"\n (menuFocus)=\"onFocus($event)\"\n (menuBlur)=\"onBlur($event)\"\n ></p-panelmenu-sub>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class PanelMenuList extends BaseComponent implements OnChanges {\n @Input() panelId: string | undefined;\n\n @Input() id: string | undefined;\n\n @Input() items: any[];\n\n @Input() itemTemplate: TemplateRef<any> | undefined;\n\n @Input({ transform: booleanAttribute }) parentExpanded: boolean | undefined;\n\n @Input({ transform: booleanAttribute }) expanded: boolean | undefined;\n\n @Input() transitionOptions: string | undefined;\n\n @Input({ transform: booleanAttribute }) root: boolean | undefined;\n\n @Input({ transform: numberAttribute }) tabindex: number | undefined;\n\n @Input() activeItem: any;\n\n @Output() itemToggle: EventEmitter<any> = new EventEmitter<any>();\n\n @Output() headerFocus: EventEmitter<any> = new EventEmitter<any>();\n\n @ViewChild('submenu') subMenuViewChild: PanelMenuSub;\n\n searchTimeout: any;\n\n searchValue: any;\n\n focused: boolean | undefined;\n\n focusedItem = signal<any>(null);\n\n activeItemPath = signal<any[]>([]);\n\n processedItems = signal<any[]>([]);\n\n visibleItems = computed(() => {\n const processedItems = this.processedItems();\n return this.flatItems(processedItems);\n });\n\n get focusedItemId() {\n const focusedItem = this.focusedItem();\n return focusedItem && focusedItem.item?.id ? focusedItem.item.id : isNotEmpty(this.focusedItem()) ? `${this.panelId}_${this.focusedItem().key}` : undefined;\n }\n\n ngOnChanges(changes: SimpleChanges) {\n this.processedItems.set(this.createProcessedItems(changes?.items?.currentValue || this.items || []));\n }\n\n getItemProp(processedItem, name) {\n return processedItem && processedItem.item ? resolve(processedItem.item[name]) : undefined;\n }\n\n getItemLabel(processedItem) {\n return this.getItemProp(processedItem, 'label');\n }\n\n isItemVisible(processedItem) {\n return this.getItemProp(processedItem, 'visible') !== false;\n }\n\n isItemDisabled(processedItem) {\n return this.getItemProp(processedItem, 'disabled');\n }\n\n isItemActive(processedItem) {\n return this.activeItemPath().some((path) => path.key === processedItem.parentKey);\n }\n\n isItemGroup(processedItem) {\n return isNotEmpty(processedItem.items);\n }\n\n isElementInPanel(event, element) {\n const panel = event.currentTarget.closest('[data-pc-section=\"panel\"]');\n\n return panel && panel.contains(element);\n }\n\n isItemMatched(processedItem) {\n return this.isValidItem(processedItem) && this.getItemLabel(processedItem).toLocaleLowerCase().startsWith(this.searchValue.toLocaleLowerCase());\n }\n\n isVisibleItem(processedItem) {\n return !!processedItem && (processedItem.level === 0 || this.isItemActive(processedItem)) && this.isItemVisible(processedItem);\n }\n\n isValidItem(processedItem) {\n return !!processedItem && !this.isItemDisabled(processedItem) && !processedItem.separator;\n }\n\n findFirstItem() {\n return this.visibleItems().find((processedItem) => this.isValidItem(processedItem));\n }\n\n findLastItem() {\n return findLast(this.visibleItems(), (processedItem) => this.isValidItem(processedItem));\n }\n\n findItemByEventTarget(target: EventTarget): undefined | any {\n let parentNode = target as ParentNode & Element;\n\n while (parentNode && parentNode.tagName?.toLowerCase() !== 'li') {\n parentNode = parentNode?.parentNode as Element;\n }\n\n return parentNode?.id && this.visibleItems().find((processedItem) => this.isValidItem(processedItem) && `${this.panelId}_${processedItem.key}` === parentNode.id);\n }\n\n createProcessedItems(items, level = 0, parent = {}, parentKey = '') {\n const processedItems = [];\n items &&\n items.forEach((item, index) => {\n const key = (parentKey !== '' ? parentKey + '_' : '') + index;\n const newItem = {\n icon: item.icon,\n expanded: item.expanded,\n separator: item.separator,\n item,\n index,\n level,\n key,\n parent,\n parentKey\n };\n\n newItem['items'] = this.createProcessedItems(item.items, level + 1, newItem, key);\n processedItems.push(newItem);\n });\n return processedItems;\n }\n\n findProcessedItemByItemKey(key, processedItems?, level = 0) {\n processedItems = processedItems || this.processedItems();\n if (processedItems && processedItems.length) {\n for (let i = 0; i < processedItems.length; i++) {\n const processedItem = processedItems[i];\n\n if (this.getItemProp(processedItem, 'key') === key) return processedItem;\n const matchedItem = this.findProcessedItemByItemKey(key, processedItem.items, level + 1);\n if (matchedItem) return matchedItem;\n }\n }\n }\n\n flatItems(processedItems, processedFlattenItems = []) {\n processedItems &&\n processedItems.forEach((processedItem) => {\n if (this.isVisibleItem(processedItem)) {\n processedFlattenItems.push(processedItem);\n this.flatItems(processedItem.items, processedFlattenItems);\n }\n });\n\n return processedFlattenItems;\n }\n\n changeFocusedItem(event) {\n const { originalEvent, processedItem, focusOnNext, selfCheck, allowHeaderFocus = true } = event;\n\n if (isNotEmpty(this.focusedItem()) && this.focusedItem().key !== processedItem.key) {\n this.focusedItem.set(processedItem);\n this.scrollInView();\n } else if (allowHeaderFocus) {\n this.headerFocus.emit({ originalEvent, focusOnNext, selfCheck });\n }\n }\n\n scrollInView() {\n const element = findSingle(this.subMenuViewChild.listViewChild.nativeElement, `li[id=\"${`${this.focusedItemId}`}\"]`);\n\n if (element) {\n element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n }\n\n onFocus(event) {\n if (!this.focused) {\n this.focused = true;\n const focusedItem = this.focusedItem() || (this.isElementInPanel(event, event.relatedTarget) ? this.findItemByEventTarget(event.target) || this.findFirstItem() : this.findLastItem());\n if (event.relatedTarget !== null) this.focusedItem.set(focusedItem);\n }\n }\n\n onBlur(event) {\n const target = event.relatedTarget;\n\n if (this.focused && !this.el.nativeElement.contains(target)) {\n this.focused = false;\n this.focusedItem.set(null);\n this.searchValue = '';\n }\n }\n\n onItemToggle(event) {\n const { processedItem, expanded } = event;\n\n // Update the original item object's 'expanded' property\n if (processedItem.item) {\n processedItem.item.expanded = !processedItem.item.expanded;\n }\n\n // Recreate processedItems with updated 'expanded' states\n this.processedItems.set(this.createProcessedItems(this.items || [], 0, {}, ''));\n\n // Update activeItemPath\n const activeItemPath = this.activeItemPath().filter((p) => p.parentKey !== processedItem.parentKey);\n if (expanded) {\n activeItemPath.push(processedItem);\n }\n this.activeItemPath.set(activeItemPath);\n\n // Update focusedItem\n this.focusedItem.set(processedItem);\n }\n\n onKeyDown(event) {\n const metaKey = event.metaKey || event.ctrlKey;\n\n switch (event.code) {\n case 'ArrowDown':\n this.onArrowDownKey(event);\n break;\n\n case 'ArrowUp':\n this.onArrowUpKey(event);\n break;\n\n case 'ArrowLeft':\n this.onArrowLeftKey(event);\n break;\n\n case 'ArrowRight':\n this.onArrowRightKey(event);\n break;\n\n case 'Home':\n this.onHomeKey(event);\n break;\n\n case 'End':\n this.onEndKey(event);\n break;\n\n case 'Space':\n this.onSpaceKey(event);\n break;\n\n case 'Enter':\n this.onEnterKey(event);\n break;\n\n case 'Escape':\n case 'Tab':\n case 'PageDown':\n case 'PageUp':\n case 'Backspace':\n case 'ShiftLeft':\n case 'ShiftRight':\n //NOOP\n break;\n\n default:\n if (!metaKey && isPrintableCharacter(event.key)) {\n this.searchItems(event, event.key);\n }\n\n break;\n }\n }\n\n onArrowDownKey(event) {\n const processedItem = isNotEmpty(this.focusedItem()) ? this.findNextItem(this.focusedItem()) : this.findFirstItem();\n this.changeFocusedItem({ originalEvent: event, processedItem, focusOnNext: true });\n event.preventDefault();\n }\n onArrowUpKey(event) {\n const processedItem = isNotEmpty(this.focusedItem()) ? this.findPrevItem(this.focusedItem()) : this.findLastItem();\n\n this.changeFocusedItem({ originalEvent: event, processedItem, selfCheck: true });\n event.preventDefault();\n }\n\n onArrowLeftKey(event) {\n if (isNotEmpty(this.focusedItem())) {\n const matched = this.activeItemPath().some((p) => p.key === this.focusedItem().key);\n\n if (matched) {\n const activeItemPath = this.activeItemPath().filter((p) => p.key !== this.focusedItem().key);\n this.activeItemPath.set(activeItemPath);\n } else {\n const focusedItem = isNotEmpty(this.focusedItem().parent) ? this.focusedItem().parent : this.focusedItem();\n this.focusedItem.set(focusedItem);\n }\n\n event.preventDefault();\n }\n }\n\n onArrowRightKey(event) {\n if (isNotEmpty(this.focusedItem())) {\n const grouped = this.isItemGroup(this.focusedItem());\n\n if (grouped) {\n const matched = this.activeItemPath().some((p) => p.key === this.focusedItem().key);\n\n if (matched) {\n this.onArrowDownKey(event);\n } else {\n const activeItemPath = this.activeItemPath().filter((p) => p.parentKey !== this.focusedItem().parentKey);\n activeItemPath.push(this.focusedItem());\n\n this.activeItemPath.set(activeItemPath);\n }\n }\n\n event.preventDefault();\n }\n }\n\n onHomeKey(event) {\n this.changeFocusedItem({ originalEvent: event, processedItem: this.findFirstItem(), allowHeaderFocus: false });\n event.preventDefault();\n }\n\n onEndKey(event) {\n this.changeFocusedItem({ originalEvent: event, processedItem: this.findLastItem(), focusOnNext: true, allowHeaderFocus: false });\n event.preventDefault();\n }\n\n onEnterKey(event) {\n if (isNotEmpty(this.focusedItem())) {\n const element = <any>findSingle(this.subMenuViewChild.listViewChild.nativeElement, `li[id=\"${`${this.focusedItemId}`}\"]`);\n const anchorElement = element && (<any>findSingle(element, '[data-pc-section=\"action\"]') || findSingle(element, 'a,button'));\n\n anchorElement ? anchorElement.click() : element && element.click();\n }\n\n event.preventDefault();\n }\n\n onSpaceKey(event) {\n this.onEnterKey(event);\n }\n\n findNextItem(processedItem) {\n const index = this.visibleItems().findIndex((item) => item.key === processedItem.key);\n\n const matchedItem =\n index < this.visibleItems().length - 1\n ? this.visibleItems()\n .slice(index + 1)\n .find((pItem) => this.isValidItem(pItem))\n : undefined;\n return matchedItem || processedItem;\n }\n\n findPrevItem(processedItem) {\n const index = this.visibleItems().findIndex((item) => item.key === processedItem.key);\n const matchedItem = index > 0 ? findLast(this.visibleItems().slice(0, index), (pItem) => this.isValidItem(pItem)) : undefined;\n\n return matchedItem || processedItem;\n }\n\n searchItems(event, char) {\n this.searchValue = (this.searchValue || '') + char;\n\n let matchedItem = null;\n let matched = false;\n\n if (isNotEmpty(this.focusedItem())) {\n const focusedItemIndex = this.visibleItems().findIndex((processedItem) => processedItem.key === this.focusedItem().key);\n\n matchedItem = this.visibleItems()\n .slice(focusedItemIndex)\n .find((processedItem) => this.isItemMatched(processedItem));\n matchedItem = isEmpty(matchedItem)\n ? this.visibleItems()\n .slice(0, focusedItemIndex)\n .find((processedItem) => this.isItemMatched(processedItem))\n : matchedItem;\n } else {\n matchedItem = this.visibleItems().find((processedItem) => this.isItemMatched(processedItem));\n }\n\n if (isNotEmpty(matchedItem)) {\n matched = true;\n }\n\n if (isEmpty(matchedItem) && isEmpty(this.focusedItem())) {\n matchedItem = this.findFirstItem();\n }\n\n if (isNotEmpty(matchedItem)) {\n this.changeFocusedItem({\n originalEvent: event,\n processedItem: matchedItem,\n allowHeaderFocus: false\n });\n }\n\n if (this.searchTimeout) {\n clearTimeout(this.searchTimeout);\n }\n\n this.searchTimeout = setTimeout(() => {\n this.searchValue = '';\n this.searchTimeout = null;\n }, 500);\n\n return matched;\n }\n}\n\n/**\n * PanelMenu is a hybrid of Accordion and Tree components.\n * @group Components\n */\n@Component({\n selector: 'p-panelMenu, p-panelmenu, p-panel-menu',\n imports: [CommonModule, PanelMenuList, RouterModule, TooltipModule, ChevronDownIcon, ChevronRightIcon, SharedModule],\n standalone: true,\n template: `\n <div [class]=\"styleClass\" [ngStyle]=\"style\" [ngClass]=\"'p-panelmenu p-component'\" #container>\n <ng-container *ngFor=\"let item of model; let f = first; let l = last; let i = index\">\n <div *ngIf=\"isItemVisible(item)\" class=\"p-panelmenu-panel\" [ngClass]=\"getItemProp(item, 'headerClass')\" [ngStyle]=\"getItemProp(item, 'style')\" [attr.data-pc-section]=\"'panel'\">\n <div\n [ngClass]=\"{\n 'p-component p-panelmenu-header': true,\n 'p-panelmenu-header-active': isItemActive(item) && !!item.items,\n 'p-disabled': isItemDisabled(item)\n }\"\n [class]=\"getItemProp(item, 'styleClass')\"\n [ngStyle]=\"getItemProp(item, 'style')\"\n [pTooltip]=\"getItemProp(item, 'tooltip')\"\n [attr.id]=\"getHeaderId(item, i)\"\n [tabindex]=\"0\"\n role=\"button\"\n [tooltipOptions]=\"getItemProp(item, 'tooltipOptions')\"\n [attr.aria-expanded]=\"isItemActive(item)\"\n [attr.aria-label]=\"getItemProp(item, 'label')\"\n [attr.aria-controls]=\"getContentId(item, i)\"\n [attr.aria-disabled]=\"isItemDisabled(item)\"\n [attr.data-p-highlight]=\"isItemActive(item)\"\n [attr.data-p-disabled]=\"isItemDisabled(item)\"\n [attr.data-pc-section]=\"'header'\"\n (click)=\"onHeaderClick($event, item, i)\"\n (keydown)=\"onHeaderKeyDown($event, item, i)\"\n >\n <div class=\"p-panelmenu-header-content\">\n <ng-container *ngIf=\"!itemTemplate && !_itemTemplate\">\n <a\n *ngIf=\"!getItemProp(item, 'routerLink')\"\n [attr.href]=\"getItemProp(item, 'url')\"\n [attr.tabindex]=\"-1\"\n [target]=\"getItemProp(item, 'target')\"\n [attr.title]=\"getItemProp(item, 'title')\"\n class=\"p-panelmenu-header-link\"\n [attr.data-pc-section]=\"'headeraction'\"\n >\n <ng-container *ngIf=\"isItemGroup(item)\">\n <ng-container *ngIf=\"!submenuIconTemplate && !_submenuIconTemplate\">\n <ChevronDownIcon [styleClass]=\"'p-panelmenu-submenu-icon'\" *ngIf=\"isItemActive(item)\" />\n <ChevronRightIcon [styleClass]=\"'p-panelmenu-submenu-icon'\" *ngIf=\"!isItemActive(item)\" />\n </ng-container>\n <ng-template *ngTemplateOutlet=\"submenuIconTemplate || _submenuIconTemplate\"></ng-template>\n </ng-container>\n <span class=\"p-panelmenu-submenu-icon\" [ngClass]=\"item.icon\" *ngIf=\"item.icon\" [ngStyle]=\"getItemProp(item, 'iconStyle')\"></span>\n <span class=\"p-panelmenu-header-label\" *ngIf=\"getItemProp(item, 'escape') !== false; else htmlLabel\">{{ getItemProp(item, 'label') }}</span>\n <ng-template #htmlLabel><span class=\"p-panelmenu-header-label\" [innerHTML]=\"getItemProp(item, 'label')\"></span></ng-template>\n <span class=\"p-menuitem-badge\" *ngIf=\"getItemProp(item, 'badge')\" [ngClass]=\"getItemProp(item, 'badgeStyleClass')\">{{ getItemProp(item, 'badge') }}</span>\n </a>\n </ng-container>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\n <a\n *ngIf=\"getItemProp(item, 'routerLink')\"\n [routerLink]=\"getItemProp(item, 'routerLink')\"\n [queryParams]=\"getItemProp(item, 'queryParams')\"\n [routerLinkActive]=\"'p-panelmenu-item-link-active'\"\n [routerLinkActiveOptions]=\"getItemProp(item, 'routerLinkActiveOptions') || { exact: false }\"\n [target]=\"getItemProp(item, 'target')\"\n class=\"p-panelmenu-header-link\"\n [attr.tabindex]=\"-1\"\n [fragment]=\"getItemProp(item, 'fragment')\"\n [queryParamsHandling]=\"getItemProp(item, 'queryParamsHandling')\"\n [preserveFragment]=\"getItemProp(item, 'preserveFragment')\"\n [skipLocationChange]=\"getItemProp(item, 'skipLocationChange')\"\n [replaceUrl]=\"getItemProp(item, 'replaceUrl')\"\n [state]=\"getItemProp(item, 'state')\"\n [attr.data-pc-section]=\"'headeraction'\"\n >\n <ng-container *ngIf=\"isItemGroup(item)\">\n <ng-container *ngIf=\"!submenuIconTemplate && !_submenuIconTemplate\">\n <ChevronDownIcon [styleClass]=\"'p-panelmenu-submenu-icon'\" *ngIf=\"isItemActive(item)\" />\n <ChevronRightIcon [styleClass]=\"'p-panelmenu-submenu-icon'\" *ngIf=\"!isItemActive(item)\" />\n </ng-container>\n <ng-template *ngTemplateOutlet=\"submenuIconTemplate || _submenuIconTemplate\"></ng-template>\n </ng-container>\n <span class=\"p-panelmenu-submenu-icon\" [ngClass]=\"item.icon\" *ngIf=\"item.icon\" [ngStyle]=\"getItemProp(item, 'iconStyle')\"></span>\n <span class=\"p-panelmenu-header-label\" *ngIf=\"getItemProp(item, 'escape') !== false; else htmlRouteLabel\">{{ getItemProp(item, 'label') }}</span>\n <ng-template #htmlRouteLabel><span class=\"p-panelmenu-header-label\" [innerHTML]=\"getItemProp(item, 'label')\"></span></ng-template>\n <span class=\"p-menuitem-badge\" *ngIf=\"getItemProp(item, 'badge')\" [ngClass]=\"getItemProp(item, 'badgeStyleClass')\">{{ getItemProp(item, 'badge') }}</span>\n </a>\n </div>\n </div>\n <div\n *ngIf=\"isItemGroup(item)\"\n class=\"p-panelmenu-content-container\"\n [@rootItem]=\"getAnimation(item)\"\n (@rootItem.done)=\"onToggleDone()\"\n role=\"region\"\n [attr.id]=\"getContentId(item, i)\"\n [attr.aria-labelledby]=\"getHeaderId(item, i)\"\n [attr.data-pc-section]=\"'toggleablecontent'\"\n [ngClass]=\"{ 'p-panelmenu-expanded': isItemActive(item) }\"\n >\n <div class=\"p-panelmenu-content\" [attr.data-pc-section]=\"'menucontent'\">\n <p-panelMenuList\n [panelId]=\"getPanelId(i, item)\"\n [items]=\"getItemProp(item, 'items')\"\n [itemTemplate]=\"itemTemplate || _itemTemplate\"\n [transitionOptions]=\"transitionOptions\"\n [root]=\"true\"\n [activeItem]=\"activeItem()\"\n [tabindex]=\"tabindex\"\n [parentExpanded]=\"isItemActive(item)\"\n (headerFocus)=\"updateFocusedHeader($event)\"\n ></p-panelMenuList>\n </div>\n </div>\n </div>\n </ng-container>\n </div>\n `,\n animations: [\n trigger('rootItem', [\n state(\n 'hidden',\n style({\n height: '0',\n visibility: 'hidden'\n })\n ),\n state(\n 'visible',\n style({\n height: '*',\n visibility: '*'\n })\n ),\n transition('visible <=> hidden', [animate('{{transitionParams}}')]),\n transition('void => *', animate(0))\n ])\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n providers: [PanelMenuStyle]\n})\nexport class PanelMenu extends BaseComponent implements AfterContentInit {\n /**\n * An array of menuitems.\n * @group Props\n */\n @Input() model: MenuItem[] | undefined;\n /**\n * Inline style of the component.\n * @group Props\n */\n @Input() style: { [klass: string]: any } | null | undefined;\n /**\n * Style class of the component.\n * @group Props\n */\n @Input() styleClass: string | undefined;\n /**\n * Whether multiple tabs can be activated at the same time or not.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) multiple: boolean = false;\n /**\n * Transition options of the animation.\n * @group Props\n */\n @Input() transitionOptions: string = '400ms cubic-bezier(0.86, 0, 0.07, 1)';\n /**\n * Current id state as a string.\n * @group Props\n */\n @Input() id: string | undefined;\n /**\n * Index of the element in tabbing order.\n * @group Props\n */\n @Input({ transform: numberAttribute }) tabindex: number | undefined = 0;\n\n @ViewChild('container') containerViewChild: ElementRef | undefined;\n /**\n * Template option of submenuicon.\n * @group Templates\n */\n @ContentChild('submenuicon', { descendants: false }) submenuIconTemplate: TemplateRef<any> | undefined;\n /**\n * Template option of item.\n * @group Templates\n */\n @ContentChild('item', { descendants: false }) itemTemplate: TemplateRef<any> | undefined;\n\n @ContentChildren(PrimeTemplate) templates: QueryList<PrimeTemplate> | undefined;\n\n _submenuIconTemp