Add appmenu, appmenuitem & appmenuservice
This commit is contained in:
162
src/layout/appmenu.ts
Normal file
162
src/layout/appmenu.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { AppConfigService } from '@/src/service/appconfigservice';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AppMenuItem } from '@/src/layout/appmenuitem';
|
||||
|
||||
@Component({
|
||||
standalone:true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
AppMenuItem
|
||||
],
|
||||
template: `<ul class="layout-menu">
|
||||
<ng-container *ngFor="let item of model; let i = index;">
|
||||
<li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
|
||||
<li *ngIf="item.separator" class="menu-separator"></li>
|
||||
</ng-container>
|
||||
</ul>
|
||||
`,
|
||||
})
|
||||
export class AppMenu {
|
||||
|
||||
model: any[] = [];
|
||||
|
||||
configService = inject(AppConfigService);
|
||||
|
||||
ngOnInit() {
|
||||
this.model = [
|
||||
{
|
||||
label: 'Home',
|
||||
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/' }]
|
||||
},
|
||||
{
|
||||
label: 'UI Components',
|
||||
items: [
|
||||
{ label: 'Form Layout', icon: 'pi pi-fw pi-id-card', to: '/uikit/formlayout' },
|
||||
{ label: 'Input', icon: 'pi pi-fw pi-check-square', to: '/uikit/input' },
|
||||
{ label: 'Button', icon: 'pi pi-fw pi-mobile', to: '/uikit/button', class: 'rotated-icon' },
|
||||
{ label: 'Table', icon: 'pi pi-fw pi-table', to: '/uikit/table' },
|
||||
{ label: 'List', icon: 'pi pi-fw pi-list', to: '/uikit/list' },
|
||||
{ label: 'Tree', icon: 'pi pi-fw pi-share-alt', to: '/uikit/tree' },
|
||||
{ label: 'Panel', icon: 'pi pi-fw pi-tablet', to: '/uikit/panel' },
|
||||
{ label: 'Overlay', icon: 'pi pi-fw pi-clone', to: '/uikit/overlay' },
|
||||
{ label: 'Media', icon: 'pi pi-fw pi-image', to: '/uikit/media' },
|
||||
{ label: 'Menu', icon: 'pi pi-fw pi-bars', to: '/uikit/menu' },
|
||||
{ label: 'Message', icon: 'pi pi-fw pi-comment', to: '/uikit/message' },
|
||||
{ label: 'File', icon: 'pi pi-fw pi-file', to: '/uikit/file' },
|
||||
{ label: 'Chart', icon: 'pi pi-fw pi-chart-bar', to: '/uikit/charts' },
|
||||
{ label: 'Timeline', icon: 'pi pi-fw pi-calendar', to: '/uikit/timeline' },
|
||||
{ label: 'Misc', icon: 'pi pi-fw pi-circle', to: '/uikit/misc' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Pages',
|
||||
icon: 'pi pi-fw pi-briefcase',
|
||||
to: '/pages',
|
||||
items: [
|
||||
{
|
||||
label: 'Landing',
|
||||
icon: 'pi pi-fw pi-globe',
|
||||
to: '/landing'
|
||||
},
|
||||
{
|
||||
label: 'Auth',
|
||||
icon: 'pi pi-fw pi-user',
|
||||
items: [
|
||||
{
|
||||
label: 'Login',
|
||||
icon: 'pi pi-fw pi-sign-in',
|
||||
to: '/auth/login'
|
||||
},
|
||||
{
|
||||
label: 'Error',
|
||||
icon: 'pi pi-fw pi-times-circle',
|
||||
to: '/auth/error'
|
||||
},
|
||||
{
|
||||
label: 'Access Denied',
|
||||
icon: 'pi pi-fw pi-lock',
|
||||
to: '/auth/access'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Crud',
|
||||
icon: 'pi pi-fw pi-pencil',
|
||||
to: '/pages/crud'
|
||||
},
|
||||
{
|
||||
label: 'Not Found',
|
||||
icon: 'pi pi-fw pi-exclamation-circle',
|
||||
to: '/pages/notfound'
|
||||
},
|
||||
{
|
||||
label: 'Empty',
|
||||
icon: 'pi pi-fw pi-circle-off',
|
||||
to: '/pages/empty'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Hierarchy',
|
||||
items: [
|
||||
{
|
||||
label: 'Submenu 1',
|
||||
icon: 'pi pi-fw pi-bookmark',
|
||||
items: [
|
||||
{
|
||||
label: 'Submenu 1.1',
|
||||
icon: 'pi pi-fw pi-bookmark',
|
||||
items: [
|
||||
{ label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' },
|
||||
{ label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' },
|
||||
{ label: 'Submenu 1.1.3', icon: 'pi pi-fw pi-bookmark' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Submenu 1.2',
|
||||
icon: 'pi pi-fw pi-bookmark',
|
||||
items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Submenu 2',
|
||||
icon: 'pi pi-fw pi-bookmark',
|
||||
items: [
|
||||
{
|
||||
label: 'Submenu 2.1',
|
||||
icon: 'pi pi-fw pi-bookmark',
|
||||
items: [
|
||||
{ label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' },
|
||||
{ label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Submenu 2.2',
|
||||
icon: 'pi pi-fw pi-bookmark',
|
||||
items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Get Started',
|
||||
items: [
|
||||
{
|
||||
label: 'Documentation',
|
||||
icon: 'pi pi-fw pi-book',
|
||||
to: '/documentation'
|
||||
},
|
||||
{
|
||||
label: 'View Source',
|
||||
icon: 'pi pi-fw pi-github',
|
||||
url: 'https://github.com/primefaces/sakai-ng',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
152
src/layout/appmenuitem.ts
Normal file
152
src/layout/appmenuitem.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { ChangeDetectorRef, Component, Host, HostBinding, Input } from '@angular/core';
|
||||
import { NavigationEnd, Router, RouterModule } from '@angular/router';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { AppConfigService } from '@/src/service/appconfigservice';
|
||||
import { MenuService } from '@/src/app/layout/app.menu.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RippleModule } from 'primeng/ripple';
|
||||
import { MenuItem} from 'primeng/api';
|
||||
|
||||
@Component({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: '[app-menuitem]',
|
||||
imports: [CommonModule, RouterModule, RippleModule],
|
||||
template: `
|
||||
<ng-container>
|
||||
<div *ngIf="root && item.visible !== false" class="layout-menuitem-root-text">{{item.label}}</div>
|
||||
<a *ngIf="(!item.routerLink || item.items) && item.visible !== false" [attr.href]="item.url" (click)="itemClick($event)"
|
||||
[ngClass]="item.class" [attr.target]="item.target" tabindex="0" pRipple>
|
||||
<i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
|
||||
<span class="layout-menuitem-text">{{item.label}}</span>
|
||||
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
|
||||
</a>
|
||||
<a *ngIf="(item.routerLink && !item.items) && item.visible !== false" (click)="itemClick($event)" [ngClass]="item.class"
|
||||
[routerLink]="item.routerLink" routerLinkActive="active-route" [routerLinkActiveOptions]="item.routerLinkActiveOptions||{ paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }"
|
||||
[fragment]="item.fragment" [queryParamsHandling]="item.queryParamsHandling" [preserveFragment]="item.preserveFragment"
|
||||
[skipLocationChange]="item.skipLocationChange" [replaceUrl]="item.replaceUrl" [state]="item.state" [queryParams]="item.queryParams"
|
||||
[attr.target]="item.target" tabindex="0" pRipple>
|
||||
<i [ngClass]="item.icon" class="layout-menuitem-icon"></i>
|
||||
<span class="layout-menuitem-text">{{item.label}}</span>
|
||||
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" *ngIf="item.items"></i>
|
||||
</a>
|
||||
|
||||
<ul *ngIf="item.items && item.visible !== false" [@children]="submenuAnimation">
|
||||
<ng-template ngFor let-child let-i="index" [ngForOf]="item.items">
|
||||
<li app-menuitem [item]="child" [index]="i" [parentKey]="key" [class]="child.badgeClass"></li>
|
||||
</ng-template>
|
||||
</ul>
|
||||
</ng-container>
|
||||
`,
|
||||
animations: [
|
||||
trigger('children', [
|
||||
state('collapsed', style({
|
||||
height: '0'
|
||||
})),
|
||||
state('expanded', style({
|
||||
height: '*'
|
||||
})),
|
||||
transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
|
||||
])
|
||||
]
|
||||
})
|
||||
export class AppMenuItem {
|
||||
|
||||
@Input() item: MenuItem;
|
||||
|
||||
@Input() index!: number;
|
||||
|
||||
@Input() @HostBinding('class.layout-root-menuitem') root!: boolean;
|
||||
|
||||
@Input() parentKey!: string;
|
||||
|
||||
active = false;
|
||||
|
||||
menuSourceSubscription: Subscription;
|
||||
|
||||
menuResetSubscription: Subscription;
|
||||
|
||||
key: string = "";
|
||||
|
||||
constructor(public configService: AppConfigService , private cd: ChangeDetectorRef, public router: Router, private menuService: MenuService) {
|
||||
this.menuSourceSubscription = this.menuService.menuSource$.subscribe(value => {
|
||||
Promise.resolve(null).then(() => {
|
||||
if (value.routeEvent) {
|
||||
this.active = (value.key === this.key || value.key.startsWith(this.key + '-')) ? true : false;
|
||||
}
|
||||
else {
|
||||
if (value.key !== this.key && !value.key.startsWith(this.key + '-')) {
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.menuResetSubscription = this.menuService.resetSource$.subscribe(() => {
|
||||
this.active = false;
|
||||
});
|
||||
|
||||
this.router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(params => {
|
||||
if (this.item.routerLink) {
|
||||
this.updateActiveStateFromRoute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index);
|
||||
|
||||
if (this.item.routerLink) {
|
||||
this.updateActiveStateFromRoute();
|
||||
}
|
||||
}
|
||||
|
||||
updateActiveStateFromRoute() {
|
||||
let activeRoute = this.router.isActive(this.item.routerLink[0], { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' });
|
||||
|
||||
if (activeRoute) {
|
||||
this.menuService.onMenuStateChange({ key: this.key, routeEvent: true });
|
||||
}
|
||||
}
|
||||
|
||||
itemClick(event: Event) {
|
||||
// avoid processing disabled items
|
||||
if (this.item.disabled) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// execute command
|
||||
if (this.item.command) {
|
||||
this.item.command({ originalEvent: event, item: this.item });
|
||||
}
|
||||
|
||||
// toggle active state
|
||||
if (this.item.items) {
|
||||
this.active = !this.active;
|
||||
}
|
||||
|
||||
this.menuService.onMenuStateChange({ key: this.key });
|
||||
}
|
||||
|
||||
get submenuAnimation() {
|
||||
return this.root ? 'expanded' : (this.active ? 'expanded' : 'collapsed');
|
||||
}
|
||||
|
||||
@HostBinding('class.active-menuitem')
|
||||
get activeClass() {
|
||||
return this.active && !this.root;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.menuSourceSubscription) {
|
||||
this.menuSourceSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.menuResetSubscription) {
|
||||
this.menuResetSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user