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
JavaScript
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