1 | import { Component, ViewEncapsulation, Inject, forwardRef, Input, EventEmitter, ChangeDetectionStrategy, ElementRef, Renderer2, ChangeDetectorRef, ViewChild, Output, NgModule } from '@angular/core';
|
2 | import { trigger, transition, style, animate } from '@angular/animations';
|
3 | import { CommonModule } from '@angular/common';
|
4 | import { DomHandler, ConnectedOverlayScrollHandler } from 'primeng/dom';
|
5 | import { RouterModule } from '@angular/router';
|
6 | import { RippleModule } from 'primeng/ripple';
|
7 |
|
8 | class MenuItemContent {
|
9 | constructor(menu) {
|
10 | this.menu = menu;
|
11 | }
|
12 | }
|
13 | MenuItemContent.decorators = [
|
14 | { type: Component, args: [{
|
15 | selector: '[pMenuItemContent]',
|
16 | template: `
|
17 | <a *ngIf="!item.routerLink" [attr.href]="item.url||null" class="p-menuitem-link" [attr.tabindex]="item.disabled ? null : '0'" [attr.data-automationid]="item.automationId" [attr.target]="item.target" [attr.title]="item.title" [attr.id]="item.id"
|
18 | [ngClass]="{'p-disabled':item.disabled}" (click)="menu.itemClick($event, item)" role="menuitem">
|
19 | <span class="p-menuitem-icon" *ngIf="item.icon" [ngClass]="item.icon"></span>
|
20 | <span class="p-menuitem-text" *ngIf="item.escape !== false; else htmlLabel">{{item.label}}</span>
|
21 | <ng-template #htmlLabel><span class="p-menuitem-text" [innerHTML]="item.label"></span></ng-template>
|
22 | </a>
|
23 | <a *ngIf="item.routerLink" [routerLink]="item.routerLink" [attr.data-automationid]="item.automationId" [queryParams]="item.queryParams" [routerLinkActive]="'p-menuitem-link-active'"
|
24 | [routerLinkActiveOptions]="item.routerLinkActiveOptions||{exact:false}" class="p-menuitem-link" [attr.target]="item.target" [attr.id]="item.id" [attr.tabindex]="item.disabled ? null : '0'"
|
25 | [attr.title]="item.title" [ngClass]="{'p-disabled':item.disabled}" (click)="menu.itemClick($event, item)" role="menuitem" pRipple
|
26 | [fragment]="item.fragment" [queryParamsHandling]="item.queryParamsHandling" [preserveFragment]="item.preserveFragment" [skipLocationChange]="item.skipLocationChange" [replaceUrl]="item.replaceUrl" [state]="item.state">
|
27 | <span class="p-menuitem-icon" *ngIf="item.icon" [ngClass]="item.icon"></span>
|
28 | <span class="p-menuitem-text" *ngIf="item.escape !== false; else htmlRouteLabel">{{item.label}}</span>
|
29 | <ng-template #htmlRouteLabel><span class="p-menuitem-text" [innerHTML]="item.label"></span></ng-template>
|
30 | </a>
|
31 | `,
|
32 | encapsulation: ViewEncapsulation.None
|
33 | },] }
|
34 | ];
|
35 | MenuItemContent.ctorParameters = () => [
|
36 | { type: undefined, decorators: [{ type: Inject, args: [forwardRef(() => Menu),] }] }
|
37 | ];
|
38 | MenuItemContent.propDecorators = {
|
39 | item: [{ type: Input, args: ["pMenuItemContent",] }]
|
40 | };
|
41 | class Menu {
|
42 | constructor(el, renderer, cd) {
|
43 | this.el = el;
|
44 | this.renderer = renderer;
|
45 | this.cd = cd;
|
46 | this.autoZIndex = true;
|
47 | this.baseZIndex = 0;
|
48 | this.showTransitionOptions = '.12s cubic-bezier(0, 0, 0.2, 1)';
|
49 | this.hideTransitionOptions = '.1s linear';
|
50 | this.onShow = new EventEmitter();
|
51 | this.onHide = new EventEmitter();
|
52 | }
|
53 | toggle(event) {
|
54 | if (this.visible)
|
55 | this.hide();
|
56 | else
|
57 | this.show(event);
|
58 | this.preventDocumentDefault = true;
|
59 | }
|
60 | show(event) {
|
61 | this.target = event.currentTarget;
|
62 | this.relativeAlign = event.relativeAlign;
|
63 | this.visible = true;
|
64 | this.preventDocumentDefault = true;
|
65 | this.cd.markForCheck();
|
66 | }
|
67 | onOverlayAnimationStart(event) {
|
68 | switch (event.toState) {
|
69 | case 'visible':
|
70 | if (this.popup) {
|
71 | this.container = event.element;
|
72 | this.moveOnTop();
|
73 | this.onShow.emit({});
|
74 | this.appendOverlay();
|
75 | this.alignOverlay();
|
76 | this.bindDocumentClickListener();
|
77 | this.bindDocumentResizeListener();
|
78 | this.bindScrollListener();
|
79 | }
|
80 | break;
|
81 | case 'void':
|
82 | this.onOverlayHide();
|
83 | this.onHide.emit({});
|
84 | break;
|
85 | }
|
86 | }
|
87 | alignOverlay() {
|
88 | if (this.relativeAlign)
|
89 | DomHandler.relativePosition(this.container, this.target);
|
90 | else
|
91 | DomHandler.absolutePosition(this.container, this.target);
|
92 | }
|
93 | appendOverlay() {
|
94 | if (this.appendTo) {
|
95 | if (this.appendTo === 'body')
|
96 | document.body.appendChild(this.container);
|
97 | else
|
98 | DomHandler.appendChild(this.container, this.appendTo);
|
99 | }
|
100 | }
|
101 | restoreOverlayAppend() {
|
102 | if (this.container && this.appendTo) {
|
103 | this.el.nativeElement.appendChild(this.container);
|
104 | }
|
105 | }
|
106 | moveOnTop() {
|
107 | if (this.autoZIndex) {
|
108 | this.container.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
|
109 | }
|
110 | }
|
111 | hide() {
|
112 | this.visible = false;
|
113 | this.relativeAlign = false;
|
114 | this.cd.markForCheck();
|
115 | }
|
116 | onWindowResize() {
|
117 | this.hide();
|
118 | }
|
119 | itemClick(event, item) {
|
120 | if (item.disabled) {
|
121 | event.preventDefault();
|
122 | return;
|
123 | }
|
124 | if (!item.url) {
|
125 | event.preventDefault();
|
126 | }
|
127 | if (item.command) {
|
128 | item.command({
|
129 | originalEvent: event,
|
130 | item: item
|
131 | });
|
132 | }
|
133 | if (this.popup) {
|
134 | this.hide();
|
135 | }
|
136 | }
|
137 | bindDocumentClickListener() {
|
138 | if (!this.documentClickListener) {
|
139 | const documentTarget = this.el ? this.el.nativeElement.ownerDocument : 'document';
|
140 | this.documentClickListener = this.renderer.listen(documentTarget, 'click', () => {
|
141 | if (!this.preventDocumentDefault) {
|
142 | this.hide();
|
143 | }
|
144 | this.preventDocumentDefault = false;
|
145 | });
|
146 | }
|
147 | }
|
148 | unbindDocumentClickListener() {
|
149 | if (this.documentClickListener) {
|
150 | this.documentClickListener();
|
151 | this.documentClickListener = null;
|
152 | }
|
153 | }
|
154 | bindDocumentResizeListener() {
|
155 | this.documentResizeListener = this.onWindowResize.bind(this);
|
156 | window.addEventListener('resize', this.documentResizeListener);
|
157 | }
|
158 | unbindDocumentResizeListener() {
|
159 | if (this.documentResizeListener) {
|
160 | window.removeEventListener('resize', this.documentResizeListener);
|
161 | this.documentResizeListener = null;
|
162 | }
|
163 | }
|
164 | bindScrollListener() {
|
165 | if (!this.scrollHandler) {
|
166 | this.scrollHandler = new ConnectedOverlayScrollHandler(this.target, () => {
|
167 | if (this.visible) {
|
168 | this.hide();
|
169 | }
|
170 | });
|
171 | }
|
172 | this.scrollHandler.bindScrollListener();
|
173 | }
|
174 | unbindScrollListener() {
|
175 | if (this.scrollHandler) {
|
176 | this.scrollHandler.unbindScrollListener();
|
177 | }
|
178 | }
|
179 | onOverlayHide() {
|
180 | this.unbindDocumentClickListener();
|
181 | this.unbindDocumentResizeListener();
|
182 | this.unbindScrollListener();
|
183 | this.preventDocumentDefault = false;
|
184 | this.target = null;
|
185 | }
|
186 | ngOnDestroy() {
|
187 | if (this.popup) {
|
188 | if (this.scrollHandler) {
|
189 | this.scrollHandler.destroy();
|
190 | this.scrollHandler = null;
|
191 | }
|
192 | this.restoreOverlayAppend();
|
193 | this.onOverlayHide();
|
194 | }
|
195 | }
|
196 | hasSubMenu() {
|
197 | if (this.model) {
|
198 | for (var item of this.model) {
|
199 | if (item.items) {
|
200 | return true;
|
201 | }
|
202 | }
|
203 | }
|
204 | return false;
|
205 | }
|
206 | }
|
207 | Menu.decorators = [
|
208 | { type: Component, args: [{
|
209 | selector: 'p-menu',
|
210 | template: `
|
211 | <div #container [ngClass]="{'p-menu p-component': true, 'p-menu-overlay': popup}"
|
212 | [class]="styleClass" [ngStyle]="style" (click)="preventDocumentDefault=true" *ngIf="!popup || visible"
|
213 | [@overlayAnimation]="{value: 'visible', params: {showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions}}" [@.disabled]="popup !== true" (@overlayAnimation.start)="onOverlayAnimationStart($event)">
|
214 | <ul class="p-menu-list p-reset">
|
215 | <ng-template ngFor let-submenu [ngForOf]="model" *ngIf="hasSubMenu()">
|
216 | <li class="p-menu-separator" *ngIf="submenu.separator" [ngClass]="{'p-hidden': submenu.visible === false}"></li>
|
217 | <li class="p-submenu-header" [attr.data-automationid]="submenu.automationId" *ngIf="!submenu.separator" [ngClass]="{'p-hidden': submenu.visible === false}">
|
218 | <span *ngIf="submenu.escape !== false; else htmlSubmenuLabel">{{submenu.label}}</span>
|
219 | <ng-template #htmlSubmenuLabel><span [innerHTML]="submenu.label"></span></ng-template>
|
220 | </li>
|
221 | <ng-template ngFor let-item [ngForOf]="submenu.items">
|
222 | <li class="p-menu-separator" *ngIf="item.separator" [ngClass]="{'p-hidden': (item.visible === false || submenu.visible === false)}"></li>
|
223 | <li class="p-menuitem" *ngIf="!item.separator" [pMenuItemContent]="item" [ngClass]="{'p-hidden': (item.visible === false || submenu.visible === false)}" [ngStyle]="item.style" [class]="item.styleClass"></li>
|
224 | </ng-template>
|
225 | </ng-template>
|
226 | <ng-template ngFor let-item [ngForOf]="model" *ngIf="!hasSubMenu()">
|
227 | <li class="p-menu-separator" *ngIf="item.separator" [ngClass]="{'p-hidden': item.visible === false}"></li>
|
228 | <li class="p-menuitem" *ngIf="!item.separator" [pMenuItemContent]="item" [ngClass]="{'p-hidden': item.visible === false}" [ngStyle]="item.style" [class]="item.styleClass"></li>
|
229 | </ng-template>
|
230 | </ul>
|
231 | </div>
|
232 | `,
|
233 | animations: [
|
234 | trigger('overlayAnimation', [
|
235 | transition(':enter', [
|
236 | style({ opacity: 0, transform: 'scaleY(0.8)' }),
|
237 | animate('{{showTransitionParams}}')
|
238 | ]),
|
239 | transition(':leave', [
|
240 | animate('{{hideTransitionParams}}', style({ opacity: 0 }))
|
241 | ])
|
242 | ])
|
243 | ],
|
244 | changeDetection: ChangeDetectionStrategy.OnPush,
|
245 | encapsulation: ViewEncapsulation.None,
|
246 | styles: [".p-menu-overlay{position:absolute}.p-menu ul{list-style:none;margin:0;padding:0}.p-menu .p-menuitem-link{-ms-flex-align:center;align-items:center;cursor:pointer;display:-ms-flexbox;display:flex;overflow:hidden;position:relative;text-decoration:none}.p-menu .p-menuitem-text{line-height:1}"]
|
247 | },] }
|
248 | ];
|
249 | Menu.ctorParameters = () => [
|
250 | { type: ElementRef },
|
251 | { type: Renderer2 },
|
252 | { type: ChangeDetectorRef }
|
253 | ];
|
254 | Menu.propDecorators = {
|
255 | model: [{ type: Input }],
|
256 | popup: [{ type: Input }],
|
257 | style: [{ type: Input }],
|
258 | styleClass: [{ type: Input }],
|
259 | appendTo: [{ type: Input }],
|
260 | autoZIndex: [{ type: Input }],
|
261 | baseZIndex: [{ type: Input }],
|
262 | showTransitionOptions: [{ type: Input }],
|
263 | hideTransitionOptions: [{ type: Input }],
|
264 | containerViewChild: [{ type: ViewChild, args: ['container',] }],
|
265 | onShow: [{ type: Output }],
|
266 | onHide: [{ type: Output }]
|
267 | };
|
268 | class MenuModule {
|
269 | }
|
270 | MenuModule.decorators = [
|
271 | { type: NgModule, args: [{
|
272 | imports: [CommonModule, RouterModule, RippleModule],
|
273 | exports: [Menu, RouterModule],
|
274 | declarations: [Menu, MenuItemContent]
|
275 | },] }
|
276 | ];
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 | export { Menu, MenuItemContent, MenuModule };
|
283 |
|