1 | import { Component, ViewEncapsulation, Inject, forwardRef, ChangeDetectorRef, Renderer2, Input, ChangeDetectionStrategy, ElementRef, 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 { RippleModule } from 'primeng/ripple';
|
6 | import { RouterModule } from '@angular/router';
|
7 |
|
8 | class TieredMenuSub {
|
9 | constructor(tieredMenu, cf, renderer) {
|
10 | this.cf = cf;
|
11 | this.renderer = renderer;
|
12 | this.autoZIndex = true;
|
13 | this.baseZIndex = 0;
|
14 | this.tieredMenu = tieredMenu;
|
15 | }
|
16 | get parentActive() {
|
17 | return this._parentActive;
|
18 | }
|
19 | set parentActive(value) {
|
20 | this._parentActive = value;
|
21 | if (!value) {
|
22 | this.activeItem = null;
|
23 | }
|
24 | }
|
25 | ngAfterViewInit() {
|
26 | if (this.root && !this.tieredMenu.popup) {
|
27 | this.bindDocumentClickListener();
|
28 | }
|
29 | }
|
30 | onItemMouseEnter(event, item, menuitem) {
|
31 | if (this.tieredMenu.popup || (!this.root || this.activeItem)) {
|
32 | if (menuitem.disabled) {
|
33 | return;
|
34 | }
|
35 | this.activeItem = item;
|
36 | let nextElement = item.children[0].nextElementSibling;
|
37 | if (nextElement) {
|
38 | let sublist = nextElement.children[0];
|
39 | if (this.autoZIndex) {
|
40 | sublist.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
|
41 | }
|
42 | sublist.style.zIndex = String(++DomHandler.zindex);
|
43 | sublist.style.top = '0px';
|
44 | sublist.style.left = DomHandler.getOuterWidth(item.children[0]) + 'px';
|
45 | }
|
46 | }
|
47 | }
|
48 | itemClick(event, item, menuitem) {
|
49 | if (menuitem.disabled) {
|
50 | event.preventDefault();
|
51 | return true;
|
52 | }
|
53 | if (!menuitem.url) {
|
54 | event.preventDefault();
|
55 | }
|
56 | if (menuitem.command) {
|
57 | menuitem.command({
|
58 | originalEvent: event,
|
59 | item: menuitem
|
60 | });
|
61 | }
|
62 | if (this.root && !this.activeItem && !this.tieredMenu.popup) {
|
63 | this.activeItem = item;
|
64 | let nextElement = item.children[0].nextElementSibling;
|
65 | if (nextElement) {
|
66 | let sublist = nextElement.children[0];
|
67 | if (this.autoZIndex) {
|
68 | sublist.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
|
69 | }
|
70 | sublist.style.zIndex = String(++DomHandler.zindex);
|
71 | sublist.style.top = '0px';
|
72 | sublist.style.left = DomHandler.getOuterWidth(item.children[0]) + 'px';
|
73 | this.rootItemClick = true;
|
74 | }
|
75 | }
|
76 | if (!menuitem.items && this.tieredMenu.popup) {
|
77 | this.tieredMenu.hide();
|
78 | }
|
79 | }
|
80 | listClick(event) {
|
81 | if (!this.rootItemClick) {
|
82 | this.activeItem = null;
|
83 | }
|
84 | }
|
85 | bindDocumentClickListener() {
|
86 | if (!this.documentClickListener) {
|
87 | const documentTarget = this.tieredMenu && this.tieredMenu.el ? this.tieredMenu.el.nativeElement.ownerDocument : 'document';
|
88 | this.documentClickListener = this.renderer.listen(documentTarget, 'click', () => {
|
89 | if (!this.rootItemClick) {
|
90 | this.parentActive = false;
|
91 | this.activeItem = null;
|
92 | this.cf.markForCheck();
|
93 | }
|
94 | this.rootItemClick = false;
|
95 | });
|
96 | }
|
97 | }
|
98 | unbindDocumentClickListener() {
|
99 | if (this.documentClickListener) {
|
100 | this.documentClickListener();
|
101 | this.documentClickListener = null;
|
102 | }
|
103 | }
|
104 | ngOnDestroy() {
|
105 | if (this.root && !this.tieredMenu.popup) {
|
106 | this.unbindDocumentClickListener();
|
107 | }
|
108 | }
|
109 | }
|
110 | TieredMenuSub.decorators = [
|
111 | { type: Component, args: [{
|
112 | selector: 'p-tieredMenuSub',
|
113 | template: `
|
114 | <ul [ngClass]="{'p-submenu-list': !root}" (click)="listClick($event)" role="menubar">
|
115 | <ng-template ngFor let-child [ngForOf]="(root ? item : item.items)">
|
116 | <li *ngIf="child.separator" class="p-menu-separator" [ngClass]="{'p-hidden': child.visible === false}" role="separator">
|
117 | <li *ngIf="!child.separator" #listItem [ngClass]="{'p-menuitem':true,'p-menuitem-active':listItem==activeItem,'p-hidden': child.visible === false}"
|
118 | [class]="child.styleClass" [ngStyle]="child.style" role="none"
|
119 | (mouseenter)="onItemMouseEnter($event, listItem, child)">
|
120 | <a *ngIf="!child.routerLink" [attr.href]="child.url" class="p-menuitem-link" [attr.target]="child.target" [attr.tabindex]="child.disabled ? null : '0'" [attr.title]="child.title" [attr.id]="child.id"
|
121 | [ngClass]="{'p-disabled':child.disabled}" (click)="itemClick($event, listItem, child)" role="menuitem" [attr.aria-haspopup]="item.items != null" [attr.aria-expanded]="item === activeItem" pRipple>
|
122 | <span class="p-menuitem-icon" *ngIf="child.icon" [ngClass]="child.icon"></span>
|
123 | <span class="p-menuitem-text" *ngIf="child.escape !== false; else htmlLabel">{{child.label}}</span>
|
124 | <ng-template #htmlLabel><span class="p-menuitem-text" [innerHTML]="child.label"></span></ng-template>
|
125 | <span class="p-submenu-icon pi pi-angle-right" *ngIf="child.items"></span>
|
126 | </a>
|
127 | <a *ngIf="child.routerLink" [routerLink]="child.routerLink" role="menuitem" [queryParams]="child.queryParams" [routerLinkActive]="'p-menuitem-link-active'" role="menuitem" [attr.tabindex]="child.disabled ? null : '0'"
|
128 | [routerLinkActiveOptions]="child.routerLinkActiveOptions||{exact:false}"
|
129 | class="p-menuitem-link" [attr.target]="child.target" [attr.title]="child.title" [attr.id]="child.id"
|
130 | [ngClass]="{'p-disabled':child.disabled}" (click)="itemClick($event, listItem, child)" pRipple
|
131 | [fragment]="child.fragment" [queryParamsHandling]="child.queryParamsHandling" [preserveFragment]="child.preserveFragment" [skipLocationChange]="child.skipLocationChange" [replaceUrl]="child.replaceUrl" [state]="child.state">
|
132 | <span class="p-menuitem-icon" *ngIf="child.icon" [ngClass]="child.icon"></span>
|
133 | <span class="p-menuitem-text" *ngIf="child.escape !== false; else htmlRouteLabel">{{child.label}}</span>
|
134 | <ng-template #htmlRouteLabel><span class="p-menuitem-text" [innerHTML]="child.label"></span></ng-template>
|
135 | <span class="p-submenu-icon pi pi-angle-right" *ngIf="child.items"></span>
|
136 | </a>
|
137 | <p-tieredMenuSub [item]="child" *ngIf="child.items" [baseZIndex]="baseZIndex" [parentActive]="listItem==activeItem" [autoZIndex]="autoZIndex"></p-tieredMenuSub>
|
138 | </li>
|
139 | </ng-template>
|
140 | </ul>
|
141 | `,
|
142 | encapsulation: ViewEncapsulation.None,
|
143 | styles: [".p-tieredmenu-overlay{position:absolute}.p-tieredmenu ul{list-style:none;margin:0;padding:0}.p-tieredmenu .p-submenu-list{display:none;min-width:100%;position:absolute;z-index:1}.p-tieredmenu .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-tieredmenu .p-menuitem-text{line-height:1}.p-tieredmenu .p-menuitem{position:relative}.p-tieredmenu .p-menuitem-link .p-submenu-icon{margin-left:auto}.p-tieredmenu .p-menuitem-active>p-tieredmenusub>.p-submenu-list{display:block;left:100%;top:0}"]
|
144 | },] }
|
145 | ];
|
146 | TieredMenuSub.ctorParameters = () => [
|
147 | { type: undefined, decorators: [{ type: Inject, args: [forwardRef(() => TieredMenu),] }] },
|
148 | { type: ChangeDetectorRef },
|
149 | { type: Renderer2 }
|
150 | ];
|
151 | TieredMenuSub.propDecorators = {
|
152 | item: [{ type: Input }],
|
153 | root: [{ type: Input }],
|
154 | autoZIndex: [{ type: Input }],
|
155 | baseZIndex: [{ type: Input }],
|
156 | parentActive: [{ type: Input }]
|
157 | };
|
158 | class TieredMenu {
|
159 | constructor(el, renderer, cd) {
|
160 | this.el = el;
|
161 | this.renderer = renderer;
|
162 | this.cd = cd;
|
163 | this.autoZIndex = true;
|
164 | this.baseZIndex = 0;
|
165 | this.showTransitionOptions = '.12s cubic-bezier(0, 0, 0.2, 1)';
|
166 | this.hideTransitionOptions = '.1s linear';
|
167 | }
|
168 | toggle(event) {
|
169 | if (this.visible)
|
170 | this.hide();
|
171 | else
|
172 | this.show(event);
|
173 | this.preventDocumentDefault = true;
|
174 | }
|
175 | show(event) {
|
176 | this.target = event.currentTarget;
|
177 | this.visible = true;
|
178 | this.parentActive = true;
|
179 | this.preventDocumentDefault = true;
|
180 | this.cd.markForCheck();
|
181 | }
|
182 | onOverlayAnimationStart(event) {
|
183 | switch (event.toState) {
|
184 | case 'visible':
|
185 | if (this.popup) {
|
186 | this.container = event.element;
|
187 | this.moveOnTop();
|
188 | this.appendOverlay();
|
189 | DomHandler.absolutePosition(this.container, this.target);
|
190 | this.bindDocumentClickListener();
|
191 | this.bindDocumentResizeListener();
|
192 | this.bindScrollListener();
|
193 | }
|
194 | break;
|
195 | case 'void':
|
196 | this.onOverlayHide();
|
197 | break;
|
198 | }
|
199 | }
|
200 | appendOverlay() {
|
201 | if (this.appendTo) {
|
202 | if (this.appendTo === 'body')
|
203 | document.body.appendChild(this.container);
|
204 | else
|
205 | DomHandler.appendChild(this.container, this.appendTo);
|
206 | }
|
207 | }
|
208 | restoreOverlayAppend() {
|
209 | if (this.container && this.appendTo) {
|
210 | this.el.nativeElement.appendChild(this.container);
|
211 | }
|
212 | }
|
213 | moveOnTop() {
|
214 | if (this.autoZIndex) {
|
215 | this.container.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
|
216 | }
|
217 | }
|
218 | hide() {
|
219 | this.visible = false;
|
220 | this.parentActive = false;
|
221 | this.cd.markForCheck();
|
222 | }
|
223 | onWindowResize() {
|
224 | this.hide();
|
225 | }
|
226 | bindDocumentClickListener() {
|
227 | if (!this.documentClickListener) {
|
228 | const documentTarget = this.el ? this.el.nativeElement.ownerDocument : 'document';
|
229 | this.documentClickListener = this.renderer.listen(documentTarget, 'click', () => {
|
230 | if (!this.preventDocumentDefault && this.popup) {
|
231 | this.hide();
|
232 | }
|
233 | this.preventDocumentDefault = false;
|
234 | });
|
235 | }
|
236 | }
|
237 | unbindDocumentClickListener() {
|
238 | if (this.documentClickListener) {
|
239 | this.documentClickListener();
|
240 | this.documentClickListener = null;
|
241 | }
|
242 | }
|
243 | bindDocumentResizeListener() {
|
244 | this.documentResizeListener = this.onWindowResize.bind(this);
|
245 | window.addEventListener('resize', this.documentResizeListener);
|
246 | }
|
247 | unbindDocumentResizeListener() {
|
248 | if (this.documentResizeListener) {
|
249 | window.removeEventListener('resize', this.documentResizeListener);
|
250 | this.documentResizeListener = null;
|
251 | }
|
252 | }
|
253 | bindScrollListener() {
|
254 | if (!this.scrollHandler) {
|
255 | this.scrollHandler = new ConnectedOverlayScrollHandler(this.target, () => {
|
256 | if (this.visible) {
|
257 | this.hide();
|
258 | }
|
259 | });
|
260 | }
|
261 | this.scrollHandler.bindScrollListener();
|
262 | }
|
263 | unbindScrollListener() {
|
264 | if (this.scrollHandler) {
|
265 | this.scrollHandler.unbindScrollListener();
|
266 | }
|
267 | }
|
268 | onOverlayHide() {
|
269 | this.unbindDocumentClickListener();
|
270 | this.unbindDocumentResizeListener();
|
271 | this.unbindScrollListener();
|
272 | this.preventDocumentDefault = false;
|
273 | this.target = null;
|
274 | }
|
275 | ngOnDestroy() {
|
276 | if (this.popup) {
|
277 | if (this.scrollHandler) {
|
278 | this.scrollHandler.destroy();
|
279 | this.scrollHandler = null;
|
280 | }
|
281 | this.restoreOverlayAppend();
|
282 | this.onOverlayHide();
|
283 | }
|
284 | }
|
285 | }
|
286 | TieredMenu.decorators = [
|
287 | { type: Component, args: [{
|
288 | selector: 'p-tieredMenu',
|
289 | template: `
|
290 | <div [ngClass]="{'p-tieredmenu p-component':true, 'p-tieredmenu-overlay':popup}" [class]="styleClass" [ngStyle]="style"
|
291 | [@overlayAnimation]="{value: 'visible', params: {showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions}}" [@.disabled]="popup !== true"
|
292 | (@overlayAnimation.start)="onOverlayAnimationStart($event)" (click)="preventDocumentDefault=true" *ngIf="!popup || visible">
|
293 | <p-tieredMenuSub [item]="model" root="root" [parentActive]="parentActive" [baseZIndex]="baseZIndex" [autoZIndex]="autoZIndex"></p-tieredMenuSub>
|
294 | </div>
|
295 | `,
|
296 | animations: [
|
297 | trigger('overlayAnimation', [
|
298 | transition(':enter', [
|
299 | style({ opacity: 0, transform: 'scaleY(0.8)' }),
|
300 | animate('{{showTransitionParams}}')
|
301 | ]),
|
302 | transition(':leave', [
|
303 | animate('{{hideTransitionParams}}', style({ opacity: 0 }))
|
304 | ])
|
305 | ])
|
306 | ],
|
307 | changeDetection: ChangeDetectionStrategy.OnPush,
|
308 | encapsulation: ViewEncapsulation.None
|
309 | },] }
|
310 | ];
|
311 | TieredMenu.ctorParameters = () => [
|
312 | { type: ElementRef },
|
313 | { type: Renderer2 },
|
314 | { type: ChangeDetectorRef }
|
315 | ];
|
316 | TieredMenu.propDecorators = {
|
317 | model: [{ type: Input }],
|
318 | popup: [{ type: Input }],
|
319 | style: [{ type: Input }],
|
320 | styleClass: [{ type: Input }],
|
321 | appendTo: [{ type: Input }],
|
322 | autoZIndex: [{ type: Input }],
|
323 | baseZIndex: [{ type: Input }],
|
324 | showTransitionOptions: [{ type: Input }],
|
325 | hideTransitionOptions: [{ type: Input }]
|
326 | };
|
327 | class TieredMenuModule {
|
328 | }
|
329 | TieredMenuModule.decorators = [
|
330 | { type: NgModule, args: [{
|
331 | imports: [CommonModule, RouterModule, RippleModule],
|
332 | exports: [TieredMenu, RouterModule],
|
333 | declarations: [TieredMenu, TieredMenuSub]
|
334 | },] }
|
335 | ];
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 | export { TieredMenu, TieredMenuModule, TieredMenuSub };
|
342 |
|