import { Component, Inject } from '@angular/core'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { SelectionModel } from '@angular/cdk/collections'; import { NestedTreeControl } from '@angular/cdk/tree'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatTreeNestedDataSource } from '@angular/material/tree'; import { HttpService } from '@app/core/services/http.service'; import { I18nService } from '@app/core/services/i18n.service'; import { ToastService } from '@app/core/services/toast.service'; @Component({ selector: 'app-basic-role-menu', templateUrl: './menu.component.html', styleUrls: ['./menu.component.scss'], animations: [ trigger('expand', [ state('collapsed', style({ height: '0' })), state('expanded', style({ height: '*' })), transition('collapsed <=> expanded', animate('200ms cubic-bezier(.4, 0, .2, 1)')), ]), ] }) export class RoleMenuComponent { constructor( @Inject(MAT_DIALOG_DATA) private _id: any, private _httpService: HttpService, private _i18nService: I18nService, private _toastService: ToastService, private _dialogRef: MatDialogRef ) { this._ctrl = new NestedTreeControl(node => node.children); this._tree = new MatTreeNestedDataSource(); this._selection = new SelectionModel(true); this._httpService.get(`roles/${this._id}/menu`).then((res: any) => { if (res.menu.children?.length) { const stack = [...res.menu.children]; while (stack.length) { const item = stack.pop(); if (item.children?.length) { item.children.forEach((child: any) => child.parent = item); stack.push(...item.children); } } } this._selection.select(...res.permission); this._tree.data = res.menu.children; for (let item of this._tree.data) { this._ctrl.expandDescendants(item); } this._loading = false; }); } private _loading: boolean = true; private _ctrl: NestedTreeControl; private _tree: MatTreeNestedDataSource; private _selection: SelectionModel; public get loading() { return this._loading; } public get ctrl() { return this._ctrl; } public get tree() { return this._tree; } public get selection() { return this._selection; } public expandable = (_: number, node: any) => node.children?.length; public selected = (node: any, indeterminate: boolean) => { if (this._tree.data) { const descendants = this._ctrl.getDescendants(node); let result = descendants.some(item => this._selection.isSelected(item.data.id)); if (indeterminate) { result &&= !descendants.every(item => this._selection.isSelected(item.data.id)); } return result; } } public toggle = (node: any) => { this._selection.toggle(node.data.id); const descendants = this._ctrl.getDescendants(node); if (descendants.length) { if (this._selection.isSelected(node.data.id)) { this._selection.select(...descendants.map(item => item.data.id)); } else { this._selection.deselect(...descendants.map(item => item.data.id)); } } let parent = node.parent; while (parent) { if (this._selection.isSelected(parent.data.id)) { if (!this.selected(parent, false)) { this._selection.deselect(parent.data.id); } } else { if (this.selected(parent, false)) { this._selection.select(parent.data.id); } } parent = parent.parent; } } public save = async () => { const res = this._httpService.post(`roles/${this._id}/menu`, this._selection.selected).catch(async err => { switch (err.status) { case 410: this._toastService.show(this._i18nService.translate(`routes.basic.role.error.gone.${err.error?.propertyName?.toLowerCase()}`)); this._dialogRef.close({ success: false }); break; case 422: this._toastService.show(this._i18nService.translate('shared.notification.fail')); this._dialogRef.close({ success: false }); break; default: this._dialogRef.close({ success: false }); break; } }) !== undefined; if (res) { this._toastService.show(this._i18nService.translate('shared.notification.success')); this._dialogRef.close({ success: true }); } } }