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,146 lines (1,126 loc) 51.9 kB
import { trigger, transition, style, animate } from '@angular/animations'; import * as i2 from '@angular/common'; import { isPlatformBrowser, CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { Injectable, PLATFORM_ID, Inject, Pipe, EventEmitter, forwardRef, Output, Input, ViewEncapsulation, Component, computed, signal, inject, numberAttribute, booleanAttribute, ContentChildren, ContentChild, ViewChild, ChangeDetectionStrategy, NgModule } from '@angular/core'; import * as i3 from '@angular/router'; import { RouterModule } from '@angular/router'; import { uuid, focus, relativePosition, absolutePosition, appendChild, isTouchDevice, find, findSingle } from '@primeuix/utils'; import * as i5 from 'primeng/api'; import { SharedModule, PrimeTemplate } from 'primeng/api'; import * as i4 from 'primeng/badge'; import { BadgeModule } from 'primeng/badge'; import { BaseComponent } from 'primeng/basecomponent'; import { ConnectedOverlayScrollHandler } from 'primeng/dom'; import { Ripple } from 'primeng/ripple'; import * as i6 from 'primeng/tooltip'; import { TooltipModule } from 'primeng/tooltip'; import { ZIndexUtils } from 'primeng/utils'; import { BaseStyle } from 'primeng/base'; import * as i1 from '@angular/platform-browser'; const theme = ({ dt }) => ` .p-menu { background: ${dt('menu.background')}; color: ${dt('menu.color')}; border: 1px solid ${dt('menu.border.color')}; border-radius: ${dt('menu.border.radius')}; min-width: 12.5rem; } .p-menu-list { margin: 0; padding: ${dt('menu.list.padding')}; outline: 0 none; list-style: none; display: flex; flex-direction: column; gap: ${dt('menu.list.gap')}; } .p-menu-item-content { transition: background ${dt('menu.transition.duration')}, color ${dt('menu.transition.duration')}; border-radius: ${dt('menu.item.border.radius')}; color: ${dt('menu.item.color')}; } .p-menu-item-link { cursor: pointer; display: flex; align-items: center; text-decoration: none; overflow: hidden; position: relative; color: inherit; padding: ${dt('menu.item.padding')}; gap: ${dt('menu.item.gap')}; user-select: none; outline: 0 none; } .p-menu-item-label { line-height: 1; } .p-menu-item-icon { color: ${dt('menu.item.icon.color')}; } .p-menu-item.p-focus .p-menu-item-content { color: ${dt('menu.item.focus.color')}; background: ${dt('menu.item.focus.background')}; } .p-menu-item.p-focus .p-menu-item-icon { color: ${dt('menu.item.icon.focus.color')}; } .p-menu-item:not(.p-disabled) .p-menu-item-content:hover { color: ${dt('menu.item.focus.color')}; background: ${dt('menu.item.focus.background')}; } .p-menu-item:not(.p-disabled) .p-menu-item-content:hover .p-menu-item-icon { color: ${dt('menu.item.icon.focus.color')}; } .p-menu-overlay { box-shadow: ${dt('menu.shadow')}; } .p-menu-submenu-label { background: ${dt('menu.submenu.label.background')}; padding: ${dt('menu.submenu.label.padding')}; color: ${dt('menu.submenu.label.color')}; font-weight: ${dt('menu.submenu.label.font.weight')}; } .p-menu-separator { border-top: 1px solid ${dt('menu.separator.border.color')}; } /* For PrimeNG */ .p-menu-overlay { position: absolute; } `; const classes = { root: ({ props }) => [ 'p-menu p-component', { 'p-menu-overlay': props.popup } ], start: 'p-menu-start', list: 'p-menu-list', submenuLabel: 'p-menu-submenu-label', separator: 'p-menu-separator', end: 'p-menu-end', item: ({ instance }) => [ 'p-menu-item', { 'p-focus': instance.id === instance.focusedOptionId, 'p-disabled': instance.disabled() } ], itemContent: 'p-menu-item-content', itemLink: 'p-menu-item-link', itemIcon: 'p-menu-item-icon', itemLabel: 'p-menu-item-label' }; class MenuStyle extends BaseStyle { name = 'menu'; theme = theme; classes = classes; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MenuStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MenuStyle }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MenuStyle, decorators: [{ type: Injectable }] }); /** * * Menu is a navigation / command component that supports dynamic and static positioning. * * [Live Demo](https://www.primeng.org/menu/) * * @module menustyle * */ var MenuClasses; (function (MenuClasses) { /** * Class name of the root element */ MenuClasses["root"] = "p-menu"; /** * Class name of the start element */ MenuClasses["start"] = "p-menu-start"; /** * Class name of the list element */ MenuClasses["list"] = "p-menu-list"; /** * Class name of the submenu item element */ MenuClasses["submenuItem"] = "p-menu-submenu-item"; /** * Class name of the separator element */ MenuClasses["separator"] = "p-menu-separator"; /** * Class name of the end element */ MenuClasses["end"] = "p-menu-end"; /** * Class name of the item element */ MenuClasses["item"] = "p-menu-item"; /** * Class name of the item content element */ MenuClasses["itemContent"] = "p-menu-item-content"; /** * Class name of the item link element */ MenuClasses["itemLink"] = "p-menu-item-link"; /** * Class name of the item icon element */ MenuClasses["itemIcon"] = "p-menu-item-icon"; /** * Class name of the item label element */ MenuClasses["itemLabel"] = "p-menu-item-label"; })(MenuClasses || (MenuClasses = {})); class SafeHtmlPipe { platformId; sanitizer; constructor(platformId, sanitizer) { this.platformId = platformId; this.sanitizer = sanitizer; } transform(value) { if (!value || !isPlatformBrowser(this.platformId)) { return value; } return this.sanitizer.bypassSecurityTrustHtml(value); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SafeHtmlPipe, deps: [{ token: PLATFORM_ID }, { token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.5", ngImport: i0, type: SafeHtmlPipe, isStandalone: true, name: "safeHtml" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SafeHtmlPipe, decorators: [{ type: Pipe, args: [{ name: 'safeHtml', standalone: true }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }, { type: i1.DomSanitizer }] }); class MenuItemContent { item; itemTemplate; onMenuItemClick = new EventEmitter(); menu; constructor(menu) { this.menu = menu; } onItemClick(event, item) { this.onMenuItemClick.emit({ originalEvent: event, item }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MenuItemContent, deps: [{ token: forwardRef(() => Menu) }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: MenuItemContent, isStandalone: true, selector: "[pMenuItemContent]", inputs: { item: ["pMenuItemContent", "item"], itemTemplate: "itemTemplate" }, outputs: { onMenuItemClick: "onMenuItemClick" }, ngImport: i0, template: ` <div [attr.data-pc-section]="'content'" class="p-menu-item-content" (click)="onItemClick($event, item)"> <ng-container *ngIf="!itemTemplate"> <a *ngIf="!item?.routerLink" [attr.title]="item.title" [attr.href]="item.url || null" [attr.data-automationid]="item.automationId" [attr.tabindex]="-1" [attr.data-pc-section]="'action'" class="p-menu-item-link" [target]="item.target" [ngClass]="{ 'p-disabled': item.disabled }" pRipple > <ng-container *ngTemplateOutlet="itemContent; context: { $implicit: item }"></ng-container> </a> <a *ngIf="item?.routerLink" [routerLink]="item.routerLink" [attr.data-automationid]="item.automationId" [attr.tabindex]="-1" [attr.data-pc-section]="'action'" [attr.title]="item.title" [queryParams]="item.queryParams" routerLinkActive="p-menu-item-link-active" [routerLinkActiveOptions]="item.routerLinkActiveOptions || { exact: false }" class="p-menu-item-link" [target]="item.target" [ngClass]="{ 'p-disabled': item.disabled }" [fragment]="item.fragment" [queryParamsHandling]="item.queryParamsHandling" [preserveFragment]="item.preserveFragment" [skipLocationChange]="item.skipLocationChange" [replaceUrl]="item.replaceUrl" [state]="item.state" pRipple > <ng-container *ngTemplateOutlet="itemContent; context: { $implicit: item }"></ng-container> </a> </ng-container> <ng-container *ngIf="itemTemplate"> <ng-template *ngTemplateOutlet="itemTemplate; context: { $implicit: item }"></ng-template> </ng-container> <ng-template #itemContent> <span class="p-menu-item-icon" *ngIf="item.icon" [ngClass]="item.icon" [class]="item.iconClass" [ngStyle]="item.iconStyle"></span> <span class="p-menu-item-label" *ngIf="item.escape !== false; else htmlLabel">{{ item.label }}</span> <ng-template #htmlLabel><span class="p-menu-item-label" [innerHTML]="item.label | safeHtml"></span></ng-template> <p-badge *ngIf="item.badge" [styleClass]="item.badgeStyleClass" [value]="item.badge" /> </ng-template> </div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i3.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i4.Badge, selector: "p-badge", inputs: ["styleClass", "style", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "ngmodule", type: SharedModule }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: MenuItemContent, decorators: [{ type: Component, args: [{ selector: '[pMenuItemContent]', standalone: true, imports: [CommonModule, RouterModule, Ripple, TooltipModule, BadgeModule, SharedModule, SafeHtmlPipe], template: ` <div [attr.data-pc-section]="'content'" class="p-menu-item-content" (click)="onItemClick($event, item)"> <ng-container *ngIf="!itemTemplate"> <a *ngIf="!item?.routerLink" [attr.title]="item.title" [attr.href]="item.url || null" [attr.data-automationid]="item.automationId" [attr.tabindex]="-1" [attr.data-pc-section]="'action'" class="p-menu-item-link" [target]="item.target" [ngClass]="{ 'p-disabled': item.disabled }" pRipple > <ng-container *ngTemplateOutlet="itemContent; context: { $implicit: item }"></ng-container> </a> <a *ngIf="item?.routerLink" [routerLink]="item.routerLink" [attr.data-automationid]="item.automationId" [attr.tabindex]="-1" [attr.data-pc-section]="'action'" [attr.title]="item.title" [queryParams]="item.queryParams" routerLinkActive="p-menu-item-link-active" [routerLinkActiveOptions]="item.routerLinkActiveOptions || { exact: false }" class="p-menu-item-link" [target]="item.target" [ngClass]="{ 'p-disabled': item.disabled }" [fragment]="item.fragment" [queryParamsHandling]="item.queryParamsHandling" [preserveFragment]="item.preserveFragment" [skipLocationChange]="item.skipLocationChange" [replaceUrl]="item.replaceUrl" [state]="item.state" pRipple > <ng-container *ngTemplateOutlet="itemContent; context: { $implicit: item }"></ng-container> </a> </ng-container> <ng-container *ngIf="itemTemplate"> <ng-template *ngTemplateOutlet="itemTemplate; context: { $implicit: item }"></ng-template> </ng-container> <ng-template #itemContent> <span class="p-menu-item-icon" *ngIf="item.icon" [ngClass]="item.icon" [class]="item.iconClass" [ngStyle]="item.iconStyle"></span> <span class="p-menu-item-label" *ngIf="item.escape !== false; else htmlLabel">{{ item.label }}</span> <ng-template #htmlLabel><span class="p-menu-item-label" [innerHTML]="item.label | safeHtml"></span></ng-template> <p-badge *ngIf="item.badge" [styleClass]="item.badgeStyleClass" [value]="item.badge" /> </ng-template> </div> `, encapsulation: ViewEncapsulation.None }] }], ctorParameters: () => [{ type: Menu, decorators: [{ type: Inject, args: [forwardRef(() => Menu)] }] }], propDecorators: { item: [{ type: Input, args: ['pMenuItemContent'] }], itemTemplate: [{ type: Input }], onMenuItemClick: [{ type: Output }] } }); /** * Menu is a navigation / command component that supports dynamic and static positioning. * @group Components */ class Menu extends BaseComponent { overlayService; /** * An array of menuitems. * @group Props */ model; /** * Defines if menu would displayed as a popup. * @group Props */ popup; /** * Inline style of the component. * @group Props */ style; /** * Style class of the component. * @group Props */ styleClass; /** * Target element to attach the overlay, valid values are "body" or a local ng-template variable of another element (note: use binding with brackets for template variables, e.g. [appendTo]="mydiv" for a div element having #mydiv as variable name). * @group Props */ appendTo; /** * Whether to automatically manage layering. * @group Props */ autoZIndex = true; /** * Base zIndex value to use in layering. * @group Props */ baseZIndex = 0; /** * Transition options of the show animation. * @group Props */ showTransitionOptions = '.12s cubic-bezier(0, 0, 0.2, 1)'; /** * Transition options of the hide animation. * @group Props */ hideTransitionOptions = '.1s linear'; /** * Defines a string value that labels an interactive element. * @group Props */ ariaLabel; /** * Identifier of the underlying input element. * @group Props */ ariaLabelledBy; /** * Current id state as a string. * @group Props */ id; /** * Index of the element in tabbing order. * @group Props */ tabindex = 0; /** * Callback to invoke when overlay menu is shown. * @group Emits */ onShow = new EventEmitter(); /** * Callback to invoke when overlay menu is hidden. * @group Emits */ onHide = new EventEmitter(); /** * Callback to invoke when the list loses focus. * @param {Event} event - blur event. * @group Emits */ onBlur = new EventEmitter(); /** * Callback to invoke when the list receives focus. * @param {Event} event - focus event. * @group Emits */ onFocus = new EventEmitter(); listViewChild; containerViewChild; container; scrollHandler; documentClickListener; documentResizeListener; preventDocumentDefault; target; visible; focusedOptionId = computed(() => { return this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : null; }); focusedOptionIndex = signal(-1); selectedOptionIndex = signal(-1); focused = false; overlayVisible = false; relativeAlign; _componentStyle = inject(MenuStyle); constructor(overlayService) { super(); this.overlayService = overlayService; this.id = this.id || uuid('pn_id_'); } /** * Toggles the visibility of the popup menu. * @param {Event} event - Browser event. * @group Method */ toggle(event) { if (this.visible) this.hide(); else this.show(event); this.preventDocumentDefault = true; } /** * Displays the popup menu. * @param {Event} event - Browser event. * @group Method */ show(event) { this.target = event.currentTarget; this.relativeAlign = event.relativeAlign; this.visible = true; this.preventDocumentDefault = true; this.overlayVisible = true; this.cd.markForCheck(); } ngOnInit() { super.ngOnInit(); if (!this.popup) { this.bindDocumentClickListener(); } } /** * Defines template option for start. * @group Templates */ startTemplate; _startTemplate; /** * Defines template option for end. * @group Templates */ endTemplate; _endTemplate; /** * Defines template option for header. * @group Templates */ headerTemplate; _headerTemplate; /** * Defines template option for item. * @group Templates */ itemTemplate; _itemTemplate; /** * Defines template option for item. * @group Templates */ submenuHeaderTemplate; _submenuHeaderTemplate; templates; ngAfterContentInit() { this.templates?.forEach((item) => { switch (item.getType()) { case 'start': this._startTemplate = item.template; break; case 'end': this._endTemplate = item.template; break; case 'item': this._itemTemplate = item.template; break; case 'submenuheader': this._submenuHeaderTemplate = item.template; break; default: this._itemTemplate = item.template; break; } }); } getTabIndexValue() { return this.tabindex !== undefined ? this.tabindex.toString() : null; } onOverlayAnimationStart(event) { switch (event.toState) { case 'visible': if (this.popup) { this.container = event.element; this.moveOnTop(); this.onShow.emit({}); this.appendOverlay(); this.alignOverlay(); this.bindDocumentClickListener(); this.bindDocumentResizeListener(); this.bindScrollListener(); focus(this.listViewChild.nativeElement); } break; case 'void': this.onOverlayHide(); this.onHide.emit({}); break; } } onOverlayAnimationEnd(event) { switch (event.toState) { case 'void': if (this.autoZIndex) { ZIndexUtils.clear(event.element); } break; } } alignOverlay() { if (this.relativeAlign) relativePosition(this.container, this.target); else absolutePosition(this.container, this.target); } appendOverlay() { if (this.appendTo) { if (this.appendTo === 'body') this.renderer.appendChild(this.document.body, this.container); else appendChild(this.appendTo, this.container); } } restoreOverlayAppend() { if (this.container && this.appendTo) { this.renderer.appendChild(this.el.nativeElement, this.container); } } moveOnTop() { if (this.autoZIndex) { ZIndexUtils.set('menu', this.container, this.baseZIndex + this.config.zIndex.menu); } } /** * Hides the popup menu. * @group Method */ hide() { this.visible = false; this.relativeAlign = false; this.cd.markForCheck(); } onWindowResize() { if (this.visible && !isTouchDevice()) { this.hide(); } } menuitemId(item, id, index, childIndex) { return item?.id ?? `${id}_${index}${childIndex !== undefined ? '_' + childIndex : ''}`; } isItemFocused(id) { return this.focusedOptionId() === id; } label(label) { return typeof label === 'function' ? label() : label; } disabled(disabled) { return typeof disabled === 'function' ? disabled() : typeof disabled === 'undefined' ? false : disabled; } activedescendant() { return this.focused ? this.focusedOptionId() : undefined; } onListFocus(event) { if (!this.focused) { this.focused = true; this.onFocus.emit(event); } } onListBlur(event) { if (this.focused) { this.focused = false; this.changeFocusedOptionIndex(-1); this.selectedOptionIndex.set(-1); this.focusedOptionIndex.set(-1); this.onBlur.emit(event); } } onListKeyDown(event) { switch (event.code) { case 'ArrowDown': this.onArrowDownKey(event); break; case 'ArrowUp': this.onArrowUpKey(event); break; case 'Home': this.onHomeKey(event); break; case 'End': this.onEndKey(event); break; case 'Enter': this.onEnterKey(event); break; case 'NumpadEnter': this.onEnterKey(event); break; case 'Space': this.onSpaceKey(event); break; case 'Escape': case 'Tab': if (this.popup) { focus(this.target); this.hide(); } this.overlayVisible && this.hide(); break; default: break; } } onArrowDownKey(event) { const optionIndex = this.findNextOptionIndex(this.focusedOptionIndex()); this.changeFocusedOptionIndex(optionIndex); event.preventDefault(); } onArrowUpKey(event) { if (event.altKey && this.popup) { focus(this.target); this.hide(); event.preventDefault(); } else { const optionIndex = this.findPrevOptionIndex(this.focusedOptionIndex()); this.changeFocusedOptionIndex(optionIndex); event.preventDefault(); } } onHomeKey(event) { this.changeFocusedOptionIndex(0); event.preventDefault(); } onEndKey(event) { this.changeFocusedOptionIndex(find(this.containerViewChild.nativeElement, 'li[data-pc-section="menuitem"][data-p-disabled="false"]').length - 1); event.preventDefault(); } onEnterKey(event) { const element = findSingle(this.containerViewChild.nativeElement, `li[id="${`${this.focusedOptionIndex()}`}"]`); const anchorElement = element && findSingle(element, 'a[data-pc-section="action"]'); this.popup && focus(this.target); anchorElement ? anchorElement.click() : element && element.click(); event.preventDefault(); } onSpaceKey(event) { this.onEnterKey(event); } findNextOptionIndex(index) { const links = find(this.containerViewChild.nativeElement, 'li[data-pc-section="menuitem"][data-p-disabled="false"]'); const matchedOptionIndex = [...links].findIndex((link) => link.id === index); return matchedOptionIndex > -1 ? matchedOptionIndex + 1 : 0; } findPrevOptionIndex(index) { const links = find(this.containerViewChild.nativeElement, 'li[data-pc-section="menuitem"][data-p-disabled="false"]'); const matchedOptionIndex = [...links].findIndex((link) => link.id === index); return matchedOptionIndex > -1 ? matchedOptionIndex - 1 : 0; } changeFocusedOptionIndex(index) { const links = find(this.containerViewChild.nativeElement, 'li[data-pc-section="menuitem"][data-p-disabled="false"]'); if (links.length > 0) { let order = index >= links.length ? links.length - 1 : index < 0 ? 0 : index; order > -1 && this.focusedOptionIndex.set(links[order].getAttribute('id')); } } itemClick(event, id) { const { originalEvent, item } = event; if (!this.focused) { this.focused = true; this.onFocus.emit(); } if (item.disabled) { originalEvent.preventDefault(); return; } if (!item.url && !item.routerLink) { originalEvent.preventDefault(); } if (item.command) { item.command({ originalEvent: originalEvent, item: item }); } if (this.popup) { this.hide(); } if (!this.popup && this.focusedOptionIndex() !== id) { this.focusedOptionIndex.set(id); } } onOverlayClick(event) { if (this.popup) { this.overlayService.add({ originalEvent: event, target: this.el.nativeElement }); } this.preventDocumentDefault = true; } bindDocumentClickListener() { if (!this.documentClickListener && isPlatformBrowser(this.platformId)) { const documentTarget = this.el ? this.el.nativeElement.ownerDocument : 'document'; this.documentClickListener = this.renderer.listen(documentTarget, 'click', (event) => { const isOutsideContainer = this.containerViewChild?.nativeElement && !this.containerViewChild?.nativeElement.contains(event.target); const isOutsideTarget = !(this.target && (this.target === event.target || this.target.contains(event.target))); if (!this.popup && isOutsideContainer && isOutsideTarget) { this.onListBlur(event); } if (this.preventDocumentDefault && this.overlayVisible && isOutsideContainer && isOutsideTarget) { this.hide(); this.preventDocumentDefault = false; } }); } } unbindDocumentClickListener() { if (this.documentClickListener) { this.documentClickListener(); this.documentClickListener = null; } } bindDocumentResizeListener() { if (!this.documentResizeListener && isPlatformBrowser(this.platformId)) { const window = this.document.defaultView; this.documentResizeListener = this.renderer.listen(window, 'resize', this.onWindowResize.bind(this)); } } unbindDocumentResizeListener() { if (this.documentResizeListener) { this.documentResizeListener(); this.documentResizeListener = null; } } bindScrollListener() { if (!this.scrollHandler && isPlatformBrowser(this.platformId)) { this.scrollHandler = new ConnectedOverlayScrollHandler(this.target, () => { if (this.visible) { this.hide(); } }); } this.scrollHandler?.bindScrollListener(); } unbindScrollListener() { if (this.scrollHandler) { this.scrollHandler.unbindScrollListener(); this.scrollHandler = null; } } onOverlayHide() { this.unbindDocumentClickListener(); this.unbindDocumentResizeListener(); this.unbindScrollListener(); this.preventDocumentDefault = false; if (!this.cd.destroyed) { this.target = null; } } ngOnDestroy() { if (this.popup) { if (this.scrollHandler) { this.scrollHandler.destroy(); this.scrollHandler = null; } if (this.container && this.autoZIndex) { ZIndexUtils.clear(this.container); } this.restoreOverlayAppend(); this.onOverlayHide(); } if (!this.popup) { this.unbindDocumentClickListener(); } super.ngOnDestroy(); } hasSubMenu() { return this.model?.some((item) => item.items) ?? false; } isItemHidden(item) { if (item.separator) { return item.visible === false || (item.items && item.items.some((subitem) => subitem.visible !== false)); } return item.visible === false; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: Menu, deps: [{ token: i5.OverlayService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "19.2.5", type: Menu, isStandalone: true, selector: "p-menu", inputs: { model: "model", popup: ["popup", "popup", booleanAttribute], style: "style", styleClass: "styleClass", appendTo: "appendTo", autoZIndex: ["autoZIndex", "autoZIndex", booleanAttribute], baseZIndex: ["baseZIndex", "baseZIndex", numberAttribute], showTransitionOptions: "showTransitionOptions", hideTransitionOptions: "hideTransitionOptions", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", id: "id", tabindex: ["tabindex", "tabindex", numberAttribute] }, outputs: { onShow: "onShow", onHide: "onHide", onBlur: "onBlur", onFocus: "onFocus" }, providers: [MenuStyle], queries: [{ propertyName: "startTemplate", first: true, predicate: ["start"] }, { propertyName: "endTemplate", first: true, predicate: ["end"] }, { propertyName: "headerTemplate", first: true, predicate: ["header"] }, { propertyName: "itemTemplate", first: true, predicate: ["item"] }, { propertyName: "submenuHeaderTemplate", first: true, predicate: ["submenuheader"] }, { propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "listViewChild", first: true, predicate: ["list"], descendants: true }, { propertyName: "containerViewChild", first: true, predicate: ["container"], descendants: true }], usesInheritance: true, ngImport: i0, template: ` <div #container [ngClass]="{ 'p-menu p-component': true, 'p-menu-overlay': popup }" [class]="styleClass" [ngStyle]="style" *ngIf="!popup || visible" (click)="onOverlayClick($event)" [@overlayAnimation]="{ value: 'visible', params: { showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions } }" [@.disabled]="popup !== true" (@overlayAnimation.start)="onOverlayAnimationStart($event)" (@overlayAnimation.done)="onOverlayAnimationEnd($event)" [attr.data-pc-name]="'menu'" [attr.id]="id" > <div *ngIf="startTemplate ?? _startTemplate" class="p-menu-start" [attr.data-pc-section]="'start'"> <ng-container *ngTemplateOutlet="startTemplate ?? _startTemplate"></ng-container> </div> <ul #list class="p-menu-list p-reset" role="menu" [attr.id]="id + '_list'" [attr.tabindex]="getTabIndexValue()" [attr.data-pc-section]="'menu'" [attr.aria-activedescendant]="activedescendant()" [attr.aria-label]="ariaLabel" [attr.aria-labelledBy]="ariaLabelledBy" (focus)="onListFocus($event)" (blur)="onListBlur($event)" (keydown)="onListKeyDown($event)" > <ng-template ngFor let-submenu let-i="index" [ngForOf]="model" *ngIf="hasSubMenu()"> <li class="p-menu-separator" *ngIf="submenu.separator && submenu.visible !== false" role="separator"></li> <li class="p-menu-submenu-label" [attr.data-automationid]="submenu.automationId" *ngIf="!submenu.separator" [ngClass]="{ 'p-hidden': submenu.visible === false, flex: submenu.visible }" pTooltip [tooltipOptions]="submenu.tooltipOptions" role="none" [attr.id]="menuitemId(submenu, id, i)" > <ng-container *ngIf="!submenuHeaderTemplate && !_submenuHeaderTemplate"> <span *ngIf="submenu.escape !== false; else htmlSubmenuLabel">{{ submenu.label }}</span> <ng-template #htmlSubmenuLabel><span [innerHTML]="submenu.label | safeHtml"></span></ng-template> </ng-container> <ng-container *ngTemplateOutlet="submenuHeaderTemplate ?? _submenuHeaderTemplate; context: { $implicit: submenu }"></ng-container> </li> <ng-template ngFor let-item let-j="index" [ngForOf]="submenu.items"> <li class="p-menu-separator" *ngIf="item.separator && (item.visible !== false || submenu.visible !== false)" role="separator"></li> <li class="p-menu-item" *ngIf="!item.separator && item.visible !== false && (item.visible !== undefined || submenu.visible !== false)" [pMenuItemContent]="item" [itemTemplate]="itemTemplate ?? _itemTemplate" [ngClass]="{ 'p-focus': focusedOptionId() && menuitemId(item, id, i, j) === focusedOptionId(), 'p-disabled': disabled(item.disabled) }" [ngStyle]="item.style" [class]="item.styleClass" (onMenuItemClick)="itemClick($event, menuitemId(item, id, i, j))" pTooltip [tooltipOptions]="item.tooltipOptions" role="menuitem" [attr.data-pc-section]="'menuitem'" [attr.aria-label]="label(item.label)" [attr.data-p-focused]="isItemFocused(menuitemId(item, id, i, j))" [attr.data-p-disabled]="disabled(item.disabled)" [attr.aria-disabled]="disabled(item.disabled)" [attr.id]="menuitemId(item, id, i, j)" ></li> </ng-template> </ng-template> <ng-template ngFor let-item let-i="index" [ngForOf]="model" *ngIf="!hasSubMenu()"> <li class="p-menu-separator" *ngIf="item.separator && item.visible !== false" role="separator"></li> <li class="p-menu-item" *ngIf="!item.separator && item.visible !== false" [pMenuItemContent]="item" [itemTemplate]="itemTemplate ?? _itemTemplate" [ngClass]="{ 'p-focus': focusedOptionId() && menuitemId(item, id, i) === focusedOptionId(), 'p-disabled': disabled(item.disabled) }" [ngStyle]="item.style" [class]="item.styleClass" (onMenuItemClick)="itemClick($event, menuitemId(item, id, i))" pTooltip [tooltipOptions]="item.tooltipOptions" role="menuitem" [attr.data-pc-section]="'menuitem'" [attr.aria-label]="label(item.label)" [attr.data-p-focused]="isItemFocused(menuitemId(item, id, i))" [attr.data-p-disabled]="disabled(item.disabled)" [attr.aria-disabled]="disabled(item.disabled)" [attr.id]="menuitemId(item, id, i)" ></li> </ng-template> </ul> <div *ngIf="endTemplate ?? _endTemplate" class="p-menu-end" [attr.data-pc-section]="'end'"> <ng-container *ngTemplateOutlet="endTemplate ?? _endTemplate"></ng-container> </div> </div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: MenuItemContent, selector: "[pMenuItemContent]", inputs: ["pMenuItemContent", "itemTemplate"], outputs: ["onMenuItemClick"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SharedModule }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], animations: [trigger('overlayAnimation', [transition(':enter', [style({ opacity: 0, transform: 'scaleY(0.8)' }), animate('{{showTransitionParams}}')]), transition(':leave', [animate('{{hideTransitionParams}}', style({ opacity: 0 }))])])], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: Menu, decorators: [{ type: Component, args: [{ selector: 'p-menu', standalone: true, imports: [CommonModule, RouterModule, MenuItemContent, TooltipModule, BadgeModule, SharedModule, SafeHtmlPipe], template: ` <div #container [ngClass]="{ 'p-menu p-component': true, 'p-menu-overlay': popup }" [class]="styleClass" [ngStyle]="style" *ngIf="!popup || visible" (click)="onOverlayClick($event)" [@overlayAnimation]="{ value: 'visible', params: { showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions } }" [@.disabled]="popup !== true" (@overlayAnimation.start)="onOverlayAnimationStart($event)" (@overlayAnimation.done)="onOverlayAnimationEnd($event)" [attr.data-pc-name]="'menu'" [attr.id]="id" > <div *ngIf="startTemplate ?? _startTemplate" class="p-menu-start" [attr.data-pc-section]="'start'"> <ng-container *ngTemplateOutlet="startTemplate ?? _startTemplate"></ng-container> </div> <ul #list class="p-menu-list p-reset" role="menu" [attr.id]="id + '_list'" [attr.tabindex]="getTabIndexValue()" [attr.data-pc-section]="'menu'" [attr.aria-activedescendant]="activedescendant()" [attr.aria-label]="ariaLabel" [attr.aria-labelledBy]="ariaLabelledBy" (focus)="onListFocus($event)" (blur)="onListBlur($event)" (keydown)="onListKeyDown($event)" > <ng-template ngFor let-submenu let-i="index" [ngForOf]="model" *ngIf="hasSubMenu()"> <li class="p-menu-separator" *ngIf="submenu.separator && submenu.visible !== false" role="separator"></li> <li class="p-menu-submenu-label" [attr.data-automationid]="submenu.automationId" *ngIf="!submenu.separator" [ngClass]="{ 'p-hidden': submenu.visible === false, flex: submenu.visible }" pTooltip [tooltipOptions]="submenu.tooltipOptions" role="none" [attr.id]="menuitemId(submenu, id, i)" > <ng-container *ngIf="!submenuHeaderTemplate && !_submenuHeaderTemplate"> <span *ngIf="submenu.escape !== false; else htmlSubmenuLabel">{{ submenu.label }}</span> <ng-template #htmlSubmenuLabel><span [innerHTML]="submenu.label | safeHtml"></span></ng-template> </ng-container> <ng-container *ngTemplateOutlet="submenuHeaderTemplate ?? _submenuHeaderTemplate; context: { $implicit: submenu }"></ng-container> </li> <ng-template ngFor let-item let-j="index" [ngForOf]="submenu.items"> <li class="p-menu-separator" *ngIf="item.separator && (item.visible !== false || submenu.visible !== false)" role="separator"></li> <li class="p-menu-item" *ngIf="!item.separator && item.visible !== false && (item.visible !== undefined || submenu.visible !== false)" [pMenuItemContent]="item" [itemTemplate]="itemTemplate ?? _itemTemplate" [ngClass]="{ 'p-focus': focusedOptionId() && menuitemId(item, id, i, j) === focusedOptionId(), 'p-disabled': disabled(item.disabled) }" [ngStyle]="item.style" [class]="item.styleClass" (onMenuItemClick)="itemClick($event, menuitemId(item, id, i, j))" pTooltip [tooltipOptions]="item.tooltipOptions" role="menuitem" [attr.data-pc-section]="'menuitem'" [attr.aria-label]="label(item.label)" [attr.data-p-focused]="isItemFocused(menuitemId(item, id, i, j))" [attr.data-p-disabled]="disabled(item.disabled)" [attr.aria-disabled]="disabled(item.disabled)" [attr.id]="menuitemId(item, id, i, j)" ></li> </ng-template> </ng-template> <ng-template ngFor let-item let-i="index" [ngForOf]="model" *ngIf="!hasSubMenu()"> <li class="p-menu-separator" *ngIf="item.separator && item.visible !== false" role="separator"></li> <li class="p-menu-item" *ngIf="!item.separator && item.visible !== false" [pMenuItemContent]="item" [itemTemplate]="itemTemplate ?? _itemTemplate" [ngClass]="{ 'p-focus': focusedOptionId() && menuitemId(item, id, i) === focusedOptionId(), 'p-disabled': disabled(item.disabled) }" [ngStyle]="item.style" [class]="item.styleClass" (onMenuItemClick)="itemClick($event, menuitemId(item, id, i))" pTooltip [tooltipOptions]="item.tooltipOptions" role="menuitem" [attr.data-pc-section]="'menuitem'" [attr.aria-label]="label(item.label)" [attr.data-p-focused]="isItemFocused(menuitemId(item, id, i))" [attr.data-p-disabled]="disabled(item.disabled)" [attr.aria-disabled]="disabled(item.disabled)" [attr.id]="menuitemId(item, id, i)" ></li> </ng-template> </ul> <div *ngIf="endTemplate ?? _endTemplate" class="p-menu-end" [attr.data-pc-section]="'end'"> <ng-container *ngTemplateOutlet="endTemplate ?? _endTemplate"></ng-container> </div> </div> `, animations: [trigger('overlayAnimation', [transition(':enter', [style({ opacity: 0, transform: 'scaleY(0.8)' }), animate('{{showTransitionParams}}')]), transition(':leave', [animate('{{hideTransitionParams}}', style({ opacity: 0 }))])])], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [MenuStyle] }] }], ctorParameters: () => [{ type: i5.OverlayService }], propDecorators: { model: [{ type: Input }], popup: [{ type: Input, args: [{ transform: booleanAttribute }] }], style: [{ type: Input }], styleClass: [{ type: Input }], appendTo: [{ type: Input }], autoZIndex: [{ type: Input, args: [{ transform: booleanAttribute }] }], baseZIndex: [{ type: Input, args: [{ transform: numberAttribute }] }], showTransitionOptions: [{ type: Input }], hideTransitionOptions: [{ type: Input }], ariaLabel: [{ type: Input }], ariaLabelledBy: [{ type: Input }], id: [{ type: Input }], tabindex: [{ type: Input, args: [{ transform: numberAttribute }] }], onShow: [{ type: Output }], onHide: [{ type: Output }], onBlur: [{ type: Output }], onFocus: [{ type: Output }], listViewChild: [{ type: ViewChild, args: ['list'] }], containerViewChild: [{