primeng
Version:
PrimeNG is a premium UI library for Angular featuring a rich set of 90+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeBlock,
1 lines • 104 kB
Source Map (JSON)
{"version":3,"file":"primeng-panelmenu.mjs","sources":["../../src/panelmenu/style/panelmenustyle.ts","../../src/panelmenu/panelmenu-token.ts","../../src/panelmenu/panelmenu-sub.ts","../../src/panelmenu/panelmenu-list.ts","../../src/panelmenu/panelmenu.ts","../../src/panelmenu/primeng-panelmenu.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { style as panelmenu_style } from '@primeuix/styles/panelmenu';\nimport { BaseStyle } from 'primeng/base';\n\nconst style = /*css*/ `\n ${panelmenu_style}\n /*For PrimeNG*/\n\n\n .p-panelmenu-root-list,\n .p-panelmenu-submenu,\n .p-panelmenu-item-link {\n outline: 0 none;\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: ({ instance, processedItem }) => ['p-panelmenu-content-container', { 'p-panelmenu-expanded': instance.isItemActive(processedItem) }],\n contentWrapper: 'p-panelmenu-content-wrapper',\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) && !instance.isItemDisabled(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 badge: 'p-menuitem-badge'\n};\n\n@Injectable()\nexport class PanelMenuStyle extends BaseStyle {\n name = 'panelmenu';\n\n style = style;\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 { InjectionToken } from '@angular/core';\n\nexport const PANELMENU_INSTANCE = new InjectionToken<any>('PANELMENU_INSTANCE');\nexport const PANELMENUSUB_INSTANCE = new InjectionToken<any>('PANELMENUSUB_INSTANCE');\n","import { NgTemplateOutlet } from '@angular/common';\nimport { booleanAttribute, Component, computed, ElementRef, inject, input, numberAttribute, output, TemplateRef, ViewEncapsulation } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { MotionOptions } from '@primeuix/motion';\nimport { isNotEmpty, resolve } from '@primeuix/utils';\nimport { MenuItem, SharedModule } from 'primeng/api';\nimport { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { Bind, BindModule } from 'primeng/bind';\nimport { ChevronDown as ChevronDownIcon } from '@primeicons/angular/chevron-down';\nimport { ChevronRight as ChevronRightIcon } from '@primeicons/angular/chevron-right';\nimport { MotionModule } from 'primeng/motion';\nimport { TooltipModule } from 'primeng/tooltip';\nimport { PanelMenuItemTemplateContext, ProcessedMenuItem } from 'primeng/types/panelmenu';\nimport { PanelMenuStyle } from './style/panelmenustyle';\nimport type { PanelMenu } from './panelmenu';\nimport { PANELMENU_INSTANCE, PANELMENUSUB_INSTANCE } from './panelmenu-token';\n\n@Component({\n selector: 'ul[pPanelMenuSub]',\n imports: [NgTemplateOutlet, RouterModule, TooltipModule, ChevronDownIcon, ChevronRightIcon, SharedModule, BindModule, MotionModule],\n standalone: true,\n template: `\n @for (processedItem of items(); track processedItem.key; let index = $index) {\n @if (isSeparator(processedItem)) {\n <li [class]=\"cn(cx('separator'), getItemProp(processedItem, 'styleClass'))\" role=\"separator\" [pBind]=\"ptm('separator')\"></li>\n } @else if (isItemVisible(processedItem)) {\n <li\n role=\"treeitem\"\n [attr.id]=\"getItemId(processedItem)\"\n [attr.aria-label]=\"getItemProp(processedItem, 'label')\"\n [attr.aria-expanded]=\"getAriaExpanded(processedItem)\"\n [attr.aria-level]=\"level() + 1\"\n [attr.aria-setsize]=\"getAriaSetSize()\"\n [attr.aria-posinset]=\"getAriaPosInset(index)\"\n [class]=\"cn(cx('item', { processedItem }), getItemProp(processedItem, 'styleClass'))\"\n [style]=\"getItemProp(processedItem, 'style')\"\n [pTooltip]=\"getItemProp(processedItem, 'tooltip')\"\n [pTooltipUnstyled]=\"unstyled()\"\n [pBind]=\"getPTOptions(processedItem, index, 'item')\"\n [attr.data-p-disabled]=\"isItemDisabled(processedItem)\"\n [attr.data-p-focused]=\"isItemFocused(processedItem)\"\n [tooltipOptions]=\"getItemProp(processedItem, 'tooltipOptions')\"\n >\n <div [class]=\"cx('itemContent')\" [pBind]=\"getPTOptions(processedItem, index, 'itemContent')\" (click)=\"onItemClick($event, processedItem)\">\n @if (!itemTemplate()) {\n @if (!getItemProp(processedItem, 'routerLink')) {\n <a\n [attr.href]=\"getItemProp(processedItem, 'url')\"\n [attr.title]=\"getItemProp(processedItem, 'title')\"\n [attr.data-automationid]=\"getItemProp(processedItem, 'automationId')\"\n [class]=\"cn(cx('itemLink'), getItemProp(processedItem, 'linkClass'))\"\n [style]=\"getItemProp(processedItem, 'linkStyle')\"\n [target]=\"getItemProp(processedItem, 'target')\"\n [attr.tabindex]=\"getItemTabindex()\"\n [pBind]=\"getPTOptions(processedItem, index, 'itemLink')\"\n >\n @if (isItemGroup(processedItem)) {\n @if (!panelMenu.submenuIconTemplate()) {\n @if (isItemActive(processedItem)) {\n <svg data-p-icon=\"chevron-down\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions(processedItem, index, 'submenuIcon')\" />\n } @else {\n <svg data-p-icon=\"chevron-right\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions(processedItem, index, 'submenuIcon')\" />\n }\n }\n <ng-container *ngTemplateOutlet=\"panelMenu.submenuIconTemplate()\"></ng-container>\n }\n @if (hasIcon(processedItem)) {\n <span\n [class]=\"cn(cx('itemIcon'), getItemProp(processedItem, 'icon'), getItemProp(processedItem, 'iconClass'))\"\n [style]=\"getItemProp(processedItem, 'iconStyle')\"\n [pBind]=\"getPTOptions(processedItem, index, 'itemIcon')\"\n ></span>\n }\n @if (shouldEscapeLabel(processedItem)) {\n <span [class]=\"cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))\" [style]=\"getItemProp(processedItem, 'labelStyle')\" [pBind]=\"getPTOptions(processedItem, index, 'itemLabel')\">{{\n getItemProp(processedItem, 'label')\n }}</span>\n } @else {\n <span\n [class]=\"cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))\"\n [style]=\"getItemProp(processedItem, 'labelStyle')\"\n [innerHTML]=\"getItemProp(processedItem, 'label')\"\n [pBind]=\"getPTOptions(processedItem, index, 'itemLabel')\"\n ></span>\n }\n </a>\n } @else {\n <a\n [routerLink]=\"getItemProp(processedItem, 'routerLink')\"\n [queryParams]=\"getItemProp(processedItem, 'queryParams')\"\n [routerLinkActive]=\"'p-panelmenu-item-link-active'\"\n [routerLinkActiveOptions]=\"getRouterLinkActiveOptions(processedItem)\"\n [class]=\"cn(cx('itemLink'), getItemProp(processedItem, 'linkClass'))\"\n [style]=\"getItemProp(processedItem, 'linkStyle')\"\n [target]=\"getItemProp(processedItem, 'target')\"\n [attr.title]=\"getItemProp(processedItem, 'title')\"\n [attr.data-automationid]=\"getItemProp(processedItem, 'automationId')\"\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.tabindex]=\"getItemTabindex()\"\n [pBind]=\"getPTOptions(processedItem, index, 'itemLink')\"\n >\n @if (isItemGroup(processedItem)) {\n @if (!panelMenu.submenuIconTemplate()) {\n @if (isItemActive(processedItem)) {\n <svg data-p-icon=\"chevron-down\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions(processedItem, index, 'submenuIcon')\" />\n } @else {\n <svg data-p-icon=\"chevron-right\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions(processedItem, index, 'submenuIcon')\" />\n }\n }\n <ng-container *ngTemplateOutlet=\"panelMenu.submenuIconTemplate()\"></ng-container>\n }\n @if (hasIcon(processedItem)) {\n <span\n [class]=\"cn(cx('itemIcon'), getItemProp(processedItem, 'icon'), getItemProp(processedItem, 'iconClass'))\"\n [style]=\"getItemProp(processedItem, 'iconStyle')\"\n [pBind]=\"getPTOptions(processedItem, index, 'itemIcon')\"\n ></span>\n }\n @if (getItemProp(processedItem, 'label')) {\n <span\n [class]=\"cn(cx('itemLabel'), getItemProp(processedItem, 'labelClass'))\"\n [style]=\"getItemProp(processedItem, 'labelStyle')\"\n [innerHTML]=\"getItemProp(processedItem, 'label')\"\n [pBind]=\"getPTOptions(processedItem, index, 'itemLabel')\"\n ></span>\n }\n @if (hasBadge(processedItem)) {\n <span [class]=\"cn(cx('badge'), getItemProp(processedItem, 'badgeStyleClass'))\">{{ processedItem.badge }}</span>\n }\n </a>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"itemTemplate(); context: getItemTemplateContext(processedItem)\"></ng-container>\n }\n </div>\n\n <div\n [class]=\"cx('contentContainer', { processedItem: processedItem })\"\n pMotionName=\"p-collapsible\"\n [pMotion]=\"isItemVisible(processedItem) && isItemGroup(processedItem) && isItemExpanded(processedItem)\"\n [pMotionOptions]=\"motionOptions()\"\n >\n <div [class]=\"cx('contentWrapper')\" [pBind]=\"ptm('contentWrapper')\">\n <ul\n pPanelMenuSub\n [id]=\"getItemId(processedItem) + '_list'\"\n [panelId]=\"panelId()\"\n [items]=\"processedItem?.items\"\n [itemTemplate]=\"itemTemplate()\"\n [focusedItemId]=\"focusedItemId()\"\n [activeItemPath]=\"activeItemPath()\"\n [level]=\"level() + 1\"\n [pt]=\"pt()\"\n [unstyled]=\"unstyled()\"\n [parentExpanded]=\"parentExpanded() && isItemExpanded(processedItem)\"\n (itemToggle)=\"onItemToggle($event)\"\n [motionOptions]=\"motionOptions()\"\n ></ul>\n </div>\n </div>\n </li>\n }\n }\n `,\n encapsulation: ViewEncapsulation.None,\n providers: [PanelMenuStyle, { provide: PANELMENUSUB_INSTANCE, useExisting: PanelMenuSub }, { provide: PARENT_INSTANCE, useExisting: PanelMenuSub }],\n host: {\n '[class]': 'hostClass()',\n role: 'tree',\n '[tabindex]': '-1',\n '[attr.aria-activedescendant]': 'focusedItemId',\n '[attr.aria-hidden]': '!parentExpanded',\n '(focusin)': 'menuFocus.emit($event)',\n '(focusout)': 'menuBlur.emit($event)',\n '(keydown)': 'menuKeyDown.emit($event)'\n },\n hostDirectives: [Bind]\n})\nexport class PanelMenuSub extends BaseComponent {\n panelId = input<string>();\n\n focusedItemId = input<string>();\n\n items = input<ProcessedMenuItem[]>([]);\n\n itemTemplate = input<TemplateRef<PanelMenuItemTemplateContext>>();\n\n level = input(0, { transform: numberAttribute });\n\n activeItemPath = input<ProcessedMenuItem[]>([]);\n\n root = input(false, { transform: booleanAttribute });\n\n tabindex = input<number | undefined>();\n\n parentExpanded = input(false, { transform: booleanAttribute });\n\n motionOptions = input<MotionOptions>();\n\n itemToggle = output<{ processedItem: ProcessedMenuItem; expanded: boolean }>();\n\n menuFocus = output<FocusEvent>();\n\n menuBlur = output<FocusEvent>();\n\n menuKeyDown = output<KeyboardEvent>();\n\n hostClass = computed(() => (this.root() ? this.cn(this.cx('rootList'), this.cx('submenu')) : this.cx('submenu')));\n\n listViewChild: ElementRef = inject(ElementRef);\n\n panelMenu = inject<PanelMenu>(PANELMENU_INSTANCE);\n\n _componentStyle = inject(PanelMenuStyle);\n\n bindDirectiveInstance = inject(Bind, { self: true });\n\n $pcPanelMenu: PanelMenu | undefined = inject(PANELMENU_INSTANCE, { optional: true, skipSelf: true }) ?? undefined;\n\n onAfterViewChecked() {\n this.bindDirectiveInstance.setAttrs(this.ptm(this.root() ? 'rootList' : 'submenu'));\n }\n\n getPTOptions(processedItem: ProcessedMenuItem, index: number, key: string) {\n return this.ptm(key, {\n context: {\n item: processedItem.item,\n index,\n active: this.isItemActive(processedItem),\n focused: this.isItemFocused(processedItem),\n disabled: this.isItemDisabled(processedItem)\n }\n });\n }\n\n getItemId(processedItem: ProcessedMenuItem) {\n return processedItem.item?.id ?? `${this.panelId()}_${processedItem.key}`;\n }\n\n getItemKey(processedItem: ProcessedMenuItem) {\n return this.getItemId(processedItem);\n }\n\n getItemClass(processedItem: ProcessedMenuItem) {\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: ProcessedMenuItem, name: string, params?: any): any {\n return processedItem && processedItem.item ? resolve((processedItem.item as any)[name], params) : undefined;\n }\n\n getItemLabel(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'label');\n }\n\n isItemExpanded(processedItem: ProcessedMenuItem) {\n return processedItem.expanded;\n }\n\n isItemActive(processedItem: ProcessedMenuItem) {\n return this.isItemExpanded(processedItem) || this.activeItemPath().some((path) => path && path.key === processedItem.key);\n }\n\n isItemVisible(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'visible') !== false;\n }\n\n isItemDisabled(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'disabled');\n }\n\n isItemFocused(processedItem: ProcessedMenuItem) {\n return this.focusedItemId() === this.getItemId(processedItem);\n }\n\n isItemGroup(processedItem: ProcessedMenuItem) {\n return isNotEmpty(processedItem.items);\n }\n\n getAriaExpanded(processedItem: ProcessedMenuItem) {\n return this.isItemGroup(processedItem) ? this.isItemActive(processedItem) : undefined;\n }\n\n getItemTabindex() {\n return this.parentExpanded() ? '0' : '-1';\n }\n\n getRouterLinkActiveOptions(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'routerLinkActiveOptions') || { exact: false };\n }\n\n isSeparator(processedItem: ProcessedMenuItem) {\n return processedItem.separator;\n }\n\n hasIcon(processedItem: ProcessedMenuItem) {\n return !!processedItem.icon;\n }\n\n hasBadge(processedItem: ProcessedMenuItem) {\n return !!processedItem.badge;\n }\n\n shouldEscapeLabel(processedItem: ProcessedMenuItem) {\n return processedItem.item?.escape !== false;\n }\n\n getItemTemplateContext(processedItem: ProcessedMenuItem) {\n return { $implicit: processedItem.item };\n }\n\n getAriaSetSize() {\n return this.items().filter((processedItem) => this.isItemVisible(processedItem) && !this.getItemProp(processedItem, 'separator')).length;\n }\n\n getAriaPosInset(index: number) {\n return (\n index -\n this.items()\n .slice(0, index)\n .filter((processedItem) => this.isItemVisible(processedItem) && this.getItemProp(processedItem, 'separator')).length +\n 1\n );\n }\n\n onItemClick(event: MouseEvent, processedItem: ProcessedMenuItem) {\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: { processedItem: ProcessedMenuItem; expanded: boolean }) {\n this.itemToggle.emit(event);\n }\n}\n","import { booleanAttribute, ChangeDetectionStrategy, Component, computed, effect, input, numberAttribute, output, signal, TemplateRef, viewChild, ViewEncapsulation } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { MotionOptions } from '@primeuix/motion';\nimport { findLast, findSingle, isEmpty, isNotEmpty, isPrintableCharacter, resolve } from '@primeuix/utils';\nimport { MenuItem, SharedModule } from 'primeng/api';\nimport { BaseComponent } from 'primeng/basecomponent';\nimport { TooltipModule } from 'primeng/tooltip';\nimport { PanelMenuItemTemplateContext, ProcessedMenuItem } from 'primeng/types/panelmenu';\nimport { PanelMenuSub } from './panelmenu-sub';\n\n@Component({\n selector: 'ul[pPanelMenuList]',\n imports: [PanelMenuSub, RouterModule, TooltipModule, SharedModule],\n standalone: true,\n template: `\n <ul\n pPanelMenuSub\n #submenu\n [root]=\"root()\"\n [id]=\"panelId() + '_list'\"\n [panelId]=\"panelId()\"\n [tabindex]=\"tabindex()\"\n [itemTemplate]=\"itemTemplate()\"\n [focusedItemId]=\"activeFocusedItemId()\"\n [activeItemPath]=\"activeItemPath()\"\n [items]=\"processedItems()\"\n [parentExpanded]=\"parentExpanded()\"\n (itemToggle)=\"onItemToggle($event)\"\n (keydown)=\"onKeyDown($event)\"\n (menuFocus)=\"onFocus($event)\"\n (menuBlur)=\"onBlur($event)\"\n [pt]=\"pt()\"\n [unstyled]=\"unstyled()\"\n [motionOptions]=\"motionOptions()\"\n ></ul>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None\n})\nexport class PanelMenuList extends BaseComponent {\n panelId = input<string>();\n\n id = input<string>();\n\n items = input<MenuItem[]>([]);\n\n itemTemplate = input<TemplateRef<PanelMenuItemTemplateContext>>();\n\n parentExpanded = input(false, { transform: booleanAttribute });\n\n expanded = input(false, { transform: booleanAttribute });\n\n root = input(false, { transform: booleanAttribute });\n\n tabindex = input<number | undefined>();\n\n activeItem = input<MenuItem>();\n\n motionOptions = input<MotionOptions>();\n\n itemToggle = output<{ processedItem: ProcessedMenuItem; expanded: boolean }>();\n\n headerFocus = output<{ originalEvent: Event; focusOnNext?: boolean; selfCheck?: boolean }>();\n\n subMenuViewChild = viewChild<PanelMenuSub>('submenu');\n\n searchTimeout: ReturnType<typeof setTimeout> | null = null;\n\n searchValue = '';\n\n focused = signal(false);\n\n focusedItem = signal<ProcessedMenuItem | null>(null);\n\n activeItemPath = signal<ProcessedMenuItem[]>([]);\n\n processedItems = signal<ProcessedMenuItem[]>([]);\n\n visibleItems = computed(() => {\n const processedItems = this.processedItems();\n return this.flatItems(processedItems);\n });\n\n focusedItemId = computed(() => {\n const focusedItem = this.focusedItem();\n return focusedItem?.item?.id ? focusedItem.item.id : isNotEmpty(focusedItem) ? `${this.panelId()}_${focusedItem!.key}` : undefined;\n });\n\n activeFocusedItemId = computed(() => (this.focused() ? this.focusedItemId() : undefined));\n\n constructor() {\n super();\n effect(() => {\n const items = this.items();\n if (items) {\n this.processedItems.set(this.createProcessedItems(items));\n }\n });\n }\n\n getItemProp(processedItem: ProcessedMenuItem, name: string) {\n return processedItem && processedItem.item ? resolve((processedItem.item as any)[name]) : undefined;\n }\n\n getItemLabel(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'label');\n }\n\n isItemVisible(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'visible') !== false;\n }\n\n isItemDisabled(processedItem: ProcessedMenuItem) {\n return this.getItemProp(processedItem, 'disabled');\n }\n\n isItemActive(processedItem: ProcessedMenuItem) {\n return this.activeItemPath().some((path) => path.key === processedItem.parentKey);\n }\n\n isItemGroup(processedItem: ProcessedMenuItem) {\n return isNotEmpty(processedItem.items);\n }\n\n isElementInPanel(event: FocusEvent, element: EventTarget | null) {\n const panel = (event.currentTarget as HTMLElement)?.closest('[data-pc-name=\"panelmenu\"]');\n\n return panel && element && panel.contains(element as Node);\n }\n\n isItemMatched(processedItem: ProcessedMenuItem) {\n return this.isValidItem(processedItem) && this.getItemLabel(processedItem)?.toLocaleLowerCase().startsWith(this.searchValue.toLocaleLowerCase());\n }\n\n isVisibleItem(processedItem: ProcessedMenuItem) {\n return !!processedItem && (processedItem.level === 0 || this.isItemActive(processedItem)) && this.isItemVisible(processedItem);\n }\n\n isValidItem(processedItem: ProcessedMenuItem) {\n return !!processedItem && !this.isItemDisabled(processedItem) && !processedItem.separator;\n }\n\n findFirstItem(): ProcessedMenuItem | undefined {\n return this.visibleItems().find((processedItem) => this.isValidItem(processedItem));\n }\n\n findLastItem(): ProcessedMenuItem | undefined {\n return findLast(this.visibleItems(), (processedItem) => this.isValidItem(processedItem));\n }\n\n findItemByEventTarget(target: EventTarget): ProcessedMenuItem | undefined {\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) : undefined;\n }\n\n createProcessedItems(items: MenuItem[], level = 0, parent: ProcessedMenuItem | {} = {}, parentKey = ''): ProcessedMenuItem[] {\n const processedItems: ProcessedMenuItem[] = [];\n items &&\n items.forEach((item, index) => {\n const key = (parentKey !== '' ? parentKey + '_' : '') + index;\n const newItem: ProcessedMenuItem = {\n icon: item.icon,\n expanded: item.expanded,\n separator: item.separator,\n item,\n index,\n level,\n key,\n parent: parent as ProcessedMenuItem,\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: string, processedItems?: ProcessedMenuItem[], level = 0): ProcessedMenuItem | undefined {\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 return undefined;\n }\n\n flatItems(processedItems: ProcessedMenuItem[], processedFlattenItems: ProcessedMenuItem[] = []): ProcessedMenuItem[] {\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: { originalEvent: Event; processedItem: ProcessedMenuItem; focusOnNext?: boolean; selfCheck?: boolean; allowHeaderFocus?: boolean }) {\n const { originalEvent, processedItem, focusOnNext, selfCheck, allowHeaderFocus = true } = event;\n const currentFocusedItem = this.focusedItem();\n\n if (isNotEmpty(currentFocusedItem) && currentFocusedItem!.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?.({ block: 'nearest', inline: 'nearest' });\n }\n }\n\n onFocus(event: FocusEvent) {\n if (!this.focused()) {\n this.focused.set(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 ?? null);\n }\n }\n\n onBlur(event: FocusEvent) {\n const target = event.relatedTarget;\n\n if (this.focused() && !this.el.nativeElement.contains(target)) {\n this.focused.set(false);\n this.focusedItem.set(null);\n this.searchValue = '';\n }\n }\n\n onItemToggle(event: { processedItem: ProcessedMenuItem; expanded: boolean }) {\n const { processedItem, expanded } = event;\n\n // Update the original item object's 'expanded' property\n if (processedItem.item) {\n processedItem.item.expanded = expanded;\n }\n\n // Update the expanded property in the existing processedItem\n processedItem.expanded = expanded;\n\n // Trigger signal update without recreating the entire tree\n this.processedItems.update((items) => [...items]);\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: KeyboardEvent) {\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: KeyboardEvent) {\n const currentFocusedItem = this.focusedItem();\n const processedItem = isNotEmpty(currentFocusedItem) ? this.findNextItem(currentFocusedItem!) : this.findFirstItem();\n if (processedItem) {\n this.changeFocusedItem({ originalEvent: event, processedItem, focusOnNext: true });\n }\n event.preventDefault();\n }\n\n onArrowUpKey(event: KeyboardEvent) {\n const currentFocusedItem = this.focusedItem();\n const processedItem = isNotEmpty(currentFocusedItem) ? this.findPrevItem(currentFocusedItem!) : this.findLastItem();\n\n if (processedItem) {\n this.changeFocusedItem({ originalEvent: event, processedItem, selfCheck: true });\n }\n event.preventDefault();\n }\n\n onArrowLeftKey(event: KeyboardEvent) {\n const currentFocusedItem = this.focusedItem();\n if (isNotEmpty(currentFocusedItem)) {\n const matched = this.activeItemPath().some((p) => p.key === currentFocusedItem!.key);\n\n if (matched) {\n const activeItemPath = this.activeItemPath().filter((p) => p.key !== currentFocusedItem!.key);\n this.activeItemPath.set(activeItemPath);\n } else {\n const focusedItem = isNotEmpty(currentFocusedItem!.parent) ? currentFocusedItem!.parent : currentFocusedItem;\n this.focusedItem.set(focusedItem ?? null);\n }\n\n event.preventDefault();\n }\n }\n\n onArrowRightKey(event: KeyboardEvent) {\n const currentFocusedItem = this.focusedItem();\n if (isNotEmpty(currentFocusedItem)) {\n const grouped = this.isItemGroup(currentFocusedItem!);\n\n if (grouped) {\n const matched = this.activeItemPath().some((p) => p.key === currentFocusedItem!.key);\n\n if (matched) {\n this.onArrowDownKey(event);\n } else {\n const activeItemPath = this.activeItemPath().filter((p) => p.parentKey !== currentFocusedItem!.parentKey);\n activeItemPath.push(currentFocusedItem!);\n\n this.activeItemPath.set(activeItemPath);\n }\n }\n\n event.preventDefault();\n }\n }\n\n onHomeKey(event: KeyboardEvent) {\n const processedItem = this.findFirstItem();\n if (processedItem) {\n this.changeFocusedItem({ originalEvent: event, processedItem, allowHeaderFocus: false });\n }\n event.preventDefault();\n }\n\n onEndKey(event: KeyboardEvent) {\n const processedItem = this.findLastItem();\n if (processedItem) {\n this.changeFocusedItem({ originalEvent: event, processedItem, focusOnNext: true, allowHeaderFocus: false });\n }\n event.preventDefault();\n }\n\n onEnterKey(event: KeyboardEvent) {\n if (isNotEmpty(this.focusedItem())) {\n const element = findSingle(this.subMenuViewChild()?.listViewChild.nativeElement, `li[id=\"${this.focusedItemId()}\"]`) as HTMLElement | null;\n const anchorElement = element && ((findSingle(element, 'a') as HTMLElement) || (findSingle(element, 'button') as HTMLElement));\n\n anchorElement ? anchorElement.click() : element?.click();\n }\n\n event.preventDefault();\n }\n\n onSpaceKey(event: KeyboardEvent) {\n this.onEnterKey(event);\n }\n\n findNextItem(processedItem: ProcessedMenuItem): ProcessedMenuItem {\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: ProcessedMenuItem): ProcessedMenuItem {\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: KeyboardEvent, char: string) {\n this.searchValue = (this.searchValue || '') + char;\n\n let matchedItem: ProcessedMenuItem | undefined = undefined;\n let matched = false;\n\n const currentFocusedItem = this.focusedItem();\n if (isNotEmpty(currentFocusedItem)) {\n const focusedItemIndex = this.visibleItems().findIndex((processedItem) => processedItem.key === currentFocusedItem!.key);\n\n matchedItem =\n this.visibleItems()\n .slice(focusedItemIndex)\n .find((processedItem) => this.isItemMatched(processedItem)) || undefined;\n matchedItem = isEmpty(matchedItem)\n ? this.visibleItems()\n .slice(0, focusedItemIndex)\n .find((processedItem) => this.isItemMatched(processedItem)) || undefined\n : matchedItem;\n } else {\n matchedItem = this.visibleItems().find((processedItem) => this.isItemMatched(processedItem)) || undefined;\n }\n\n if (isNotEmpty(matchedItem)) {\n matched = true;\n }\n\n if (isEmpty(matchedItem) && isEmpty(this.focusedItem())) {\n matchedItem = this.findFirstItem() || undefined;\n }\n\n if (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","import { NgTemplateOutlet } from '@angular/common';\nimport { booleanAttribute, ChangeDetectionStrategy, Component, computed, contentChild, inject, input, NgModule, numberAttribute, signal, TemplateRef, ViewEncapsulation } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { MotionOptions } from '@primeuix/motion';\nimport { equals, findSingle, focus, getAttribute, isNotEmpty, resolve, uuid } from '@primeuix/utils';\nimport { MenuItem, SharedModule } from 'primeng/api';\nimport { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { Bind, BindModule } from 'primeng/bind';\nimport { ChevronDown as ChevronDownIcon } from '@primeicons/angular/chevron-down';\nimport { ChevronRight as ChevronRightIcon } from '@primeicons/angular/chevron-right';\nimport { MotionModule } from 'primeng/motion';\nimport { TooltipModule } from 'primeng/tooltip';\nimport { PanelMenuItemTemplateContext, PanelMenuPassThrough } from 'primeng/types/panelmenu';\nimport { PanelMenuList } from './panelmenu-list';\nimport { PANELMENU_INSTANCE } from './panelmenu-token';\nimport { PanelMenuStyle } from './style/panelmenustyle';\n\n/**\n * PanelMenu is a hybrid of Accordion and Tree components.\n * @group Components\n * @deprecated Use Menu component with `toggleable` option instead.\n */\n@Component({\n selector: 'p-panelmenu, p-panel-menu',\n imports: [NgTemplateOutlet, PanelMenuList, RouterModule, TooltipModule, ChevronDownIcon, ChevronRightIcon, SharedModule, BindModule, MotionModule],\n standalone: true,\n template: `\n @for (item of model(); track item.id ?? $index; let i = $index) {\n @if (isItemVisible(item)) {\n <div [class]=\"cn(cx('panel'), getItemProp(item, 'headerClass'))\" [style]=\"getItemProp(item, 'style')\" [pBind]=\"ptm('panel')\">\n <div\n [class]=\"cn(cx('header', { item }), getItemProp(item, 'styleClass'))\"\n [style]=\"getItemProp(item, 'style')\"\n [pTooltip]=\"getItemProp(item, 'tooltip')\"\n [pTooltipUnstyled]=\"unstyled()\"\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 [pBind]=\"getPTOptions('header', item, i)\"\n (click)=\"onHeaderClick($event, item, i)\"\n (keydown)=\"onHeaderKeyDown($event, item, i)\"\n >\n <div [class]=\"cx('headerContent')\" [pBind]=\"getPTOptions('headerContent', item, i)\">\n @if (!itemTemplate()) {\n @if (!getItemProp(item, 'routerLink')) {\n <a\n [attr.href]=\"getItemProp(item, 'url')\"\n [attr.tabindex]=\"-1\"\n [target]=\"getItemProp(item, 'target')\"\n [attr.title]=\"getItemProp(item, 'title')\"\n [attr.data-automationid]=\"getItemProp(item, 'automationId')\"\n [class]=\"cn(cx('headerLink'), getItemProp(item, 'linkClass'))\"\n [style]=\"getItemProp(item, 'linkStyle')\"\n [pBind]=\"getPTOptions('headerLink', item, i)\"\n >\n @if (isItemGroup(item)) {\n @if (!headerIconTemplate()) {\n @if (isItemActive(item)) {\n <svg data-p-icon=\"chevron-down\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions('submenuIcon', item, i)\" />\n } @else {\n <svg data-p-icon=\"chevron-right\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions('submenuIcon', item, i)\" />\n }\n }\n <ng-container *ngTemplateOutlet=\"headerIconTemplate()\"></ng-container>\n }\n @if (hasIcon(item)) {\n <span [class]=\"cn(cx('headerIcon'), item.icon, getItemProp(item, 'iconClass'))\" [style]=\"getItemProp(item, 'iconStyle')\" [pBind]=\"getPTOptions('headerIcon', item, i)\"></span>\n }\n @if (shouldEscapeLabel(item)) {\n <span [class]=\"cn(cx('headerLabel'), getItemProp(item, 'labelClass'))\" [style]=\"getItemProp(item, 'labelStyle')\" [pBind]=\"getPTOptions('headerLabel', item, i)\">{{ getItemProp(item, 'label') }}</span>\n } @else {\n <span\n [class]=\"cn(cx('headerLabel'), getItemProp(item, 'labelClass'))\"\n [style]=\"getItemProp(item, 'labelStyle')\"\n [innerHTML]=\"getItemProp(item, 'label')\"\n [pBind]=\"getPTOptions('headerLabel', item, i)\"\n ></span>\n }\n @if (hasBadge(item)) {\n <span [class]=\"cn(cx('badge'), getItemProp(item, 'badgeStyleClass'))\">{{ getItemProp(item, 'badge') }}</span>\n }\n </a>\n }\n }\n <ng-container *ngTemplateOutlet=\"itemTemplate(); context: getItemTemplateContext(item)\"></ng-container>\n @if (getItemProp(item, 'routerLink')) {\n <a\n [routerLink]=\"getItemProp(item, 'routerLink')\"\n [queryParams]=\"getItemProp(item, 'queryParams')\"\n [routerLinkActive]=\"'p-panelmenu-item-link-active'\"\n [routerLinkActiveOptions]=\"getRouterLinkActiveOptions(item)\"\n [target]=\"getItemProp(item, 'target')\"\n [attr.title]=\"getItemProp(item, 'title')\"\n [attr.data-automationid]=\"getItemProp(item, 'automationId')\"\n [class]=\"cn(cx('headerLink'), getItemProp(item, 'linkClass'))\"\n [style]=\"getItemProp(item, 'linkStyle')\"\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 [pBind]=\"getPTOptions('headerLink', item, i)\"\n >\n @if (isItemGroup(item)) {\n @if (!headerIconTemplate()) {\n @if (isItemActive(item)) {\n <svg data-p-icon=\"chevron-down\" [class]=\"cx('submenuIcon')\" [pBind]=\"getPTOptions('submenuIcon', item, i)\" />\n } @else {\n <svg data-p-icon=\"c