import { Component, OnInit, Input, ChangeDetectorRef, ViewChild, Output, EventEmitter, AfterViewInit, OnDestroy } from '@angular/core'; import { MatTreeFlattener, MatTree } from '@angular/material'; import { NGXLogger } from 'ngx-logger'; import { DeptInfo } from '@ucap-webmessenger/protocol-query'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { FlatTreeControl } from '@angular/cdk/tree'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { VirtualScrollTreeFlatDataSource } from '@ucap-webmessenger/ui'; import { ucapAnimations } from '@ucap-webmessenger/ui'; import { trigger, transition, style, animate } from '@angular/animations'; interface OrganizationNode { deptInfo: DeptInfo; name: string; children?: OrganizationNode[]; } /** Flat node with expandable and level information */ interface FlatNode { expandable: boolean; name: string; level: number; deptInfo: DeptInfo; } @Component({ selector: 'ucap-organization-tree', templateUrl: './tree.component.html', styleUrls: ['./tree.component.scss'], animations: [ ucapAnimations, trigger('romvoeAdd', [ transition('remove <=> add', [ style({ transform: `rotate(45deg)`, opacity: 0 }), animate('.2s 0s ease-out') ]) ]) ] }) export class TreeComponent implements OnInit, OnDestroy, AfterViewInit { @Output() selected = new EventEmitter(); @Input() loginRes: LoginResponse; @Input() set oraganizationList(deptInfoList: DeptInfo[]) { if (!deptInfoList || 0 === deptInfoList.length) { return; } const nodeMap = new Map(); const rootNodeList: OrganizationNode[] = []; const remainChildNodeList: OrganizationNode[] = []; let myNode: OrganizationNode; deptInfoList.forEach(deptInfo => { const node: OrganizationNode = { deptInfo, name: deptInfo.name, children: [] }; if (nodeMap.has(deptInfo.seq)) { this.logger.warn('duplicate seq', deptInfo.seq); return; } nodeMap.set(deptInfo.seq, node); if (deptInfo.seq === this.loginRes.departmentCode) { myNode = node; } if (0 === deptInfo.parentSeq) { rootNodeList.push(node); return; } if (nodeMap.has(deptInfo.parentSeq)) { nodeMap.get(deptInfo.parentSeq).children.push(node); } else { remainChildNodeList.push(node); } }); remainChildNodeList.forEach(node => { if (nodeMap.has(node.deptInfo.parentSeq)) { nodeMap.get(node.deptInfo.parentSeq).children.push(node); } }); this.dataSource.data = rootNodeList; } @ViewChild('cvsvOrganization', { static: false }) cvsvOrganization: CdkVirtualScrollViewport; @ViewChild('orgranizationTree', { static: false }) orgranizationTree: MatTree; treeControl: FlatTreeControl; treeFlattener: MatTreeFlattener; dataSource: VirtualScrollTreeFlatDataSource; constructor( private changeDetectorRef: ChangeDetectorRef, private logger: NGXLogger ) { this.treeControl = new FlatTreeControl( node => node.level, node => node.expandable ); this.treeFlattener = new MatTreeFlattener( (node: OrganizationNode, level: number) => { return { expandable: !!node.children && node.children.length > 0, name: node.name, level, deptInfo: node.deptInfo }; }, node => node.level, node => node.expandable, node => node.children ); this.dataSource = new VirtualScrollTreeFlatDataSource< OrganizationNode, FlatNode >(this.treeControl, this.treeFlattener); } ngOnInit() {} ngOnDestroy(): void { this.logger.debug('-----------------------TreeComponent ngOnDestroy'); } ngAfterViewInit(): void { this.dataSource.cdkVirtualScrollViewport = this.cvsvOrganization; } hasChild = (_: number, node: FlatNode) => node.expandable; isLastNode(node: FlatNode): boolean { const i = this.findNodeIndex(node); if (i === this.dataSource.expandedDataSubject.value.length - 1) { return true; } if (node.level !== this.dataSource.expandedDataSubject.value[i + 1].level) { return true; } return false; } isParentLastNode(node: FlatNode): boolean { const pi = this.findParentNodeIndex(node); if (-1 === pi) { return false; } return this.isLastNode(this.dataSource.expandedDataSubject.value[pi]); } onClickNode(node: OrganizationNode) { this.selected.emit(node.deptInfo); } private findNodeIndex(node: FlatNode): number { return this.dataSource.expandedDataSubject.value.findIndex(n => { return node.deptInfo.seq === n.deptInfo.seq; }); } private findParentNodeIndex(node: FlatNode): number { const i = this.findNodeIndex(node); if (-1 === i) { return -1; } for (let idx = i - 1; idx >= 0; idx--) { if ( this.dataSource.expandedDataSubject.value[idx].level === node.level - 1 ) { return idx; } } return -1; } }