239 lines
6.2 KiB
TypeScript
Raw Normal View History

import {
Component,
OnInit,
Input,
ChangeDetectorRef,
2019-10-07 10:08:14 +09:00
ViewChild,
Output,
2019-10-31 10:13:30 +09:00
EventEmitter,
2019-11-21 10:29:19 +09:00
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';
2019-11-25 09:34:12 +09:00
import { ucapAnimations } from '@ucap-webmessenger/ui';
import { trigger, transition, style, animate } from '@angular/animations';
2019-11-26 10:43:37 +09:00
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { Subscription } from 'rxjs';
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;
}
2019-10-04 13:45:02 +09:00
@Component({
selector: 'ucap-organization-tree',
templateUrl: './tree.component.html',
2019-11-25 09:34:12 +09:00
styleUrls: ['./tree.component.scss'],
animations: [
ucapAnimations,
trigger('romvoeAdd', [
transition('remove <=> add', [
style({
transform: `rotate(45deg)`,
opacity: 0
}),
animate('.2s 0s ease-out')
])
])
]
2019-10-04 13:45:02 +09:00
})
2019-11-21 10:29:19 +09:00
export class TreeComponent implements OnInit, OnDestroy, AfterViewInit {
2019-10-07 10:08:14 +09:00
@Output()
selected = new EventEmitter<DeptInfo>();
@Input()
loginRes: LoginResponse;
2019-11-21 10:29:19 +09:00
2019-10-04 13:45:02 +09:00
@Input()
set oraganizationList(deptInfoList: DeptInfo[]) {
if (!deptInfoList || 0 === deptInfoList.length) {
return;
}
const nodeMap = new Map<number, OrganizationNode>();
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;
}
2019-10-04 13:45:02 +09:00
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<OrganizationNode>;
2019-11-26 10:43:37 +09:00
@ViewChild(PerfectScrollbarDirective, { static: false })
psDirectiveRef?: PerfectScrollbarDirective;
treeControl: FlatTreeControl<FlatNode>;
treeFlattener: MatTreeFlattener<OrganizationNode, FlatNode>;
dataSource: VirtualScrollTreeFlatDataSource<OrganizationNode, FlatNode>;
2019-11-26 10:43:37 +09:00
treeControlExpansionChangeSubscription: Subscription;
constructor(
private changeDetectorRef: ChangeDetectorRef,
private logger: NGXLogger
) {
this.treeControl = new FlatTreeControl<FlatNode>(
node => node.level,
node => node.expandable
);
this.treeFlattener = new MatTreeFlattener<OrganizationNode, FlatNode>(
(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);
}
2019-10-04 13:45:02 +09:00
2019-11-26 10:43:37 +09:00
ngOnInit() {
this.treeControlExpansionChangeSubscription = this.treeControl.expansionModel.changed.subscribe(
() => {
this.psDirectiveRef.update();
}
);
}
2019-11-21 10:29:19 +09:00
ngOnDestroy(): void {
this.logger.debug('-----------------------TreeComponent ngOnDestroy');
2019-11-26 10:43:37 +09:00
if (!!this.treeControlExpansionChangeSubscription) {
this.treeControlExpansionChangeSubscription.unsubscribe();
}
2019-11-21 10:29:19 +09:00
}
ngAfterViewInit(): void {
this.dataSource.cdkVirtualScrollViewport = this.cvsvOrganization;
}
hasChild = (_: number, node: FlatNode) => node.expandable;
2019-10-07 10:08:14 +09:00
2019-11-26 10:43:37 +09:00
isLastNode(node: FlatNode, depth: number): boolean {
const i = this.findNodeIndex(node, depth);
if (-1 === i) {
return false;
}
2019-11-25 09:34:12 +09:00
if (i === this.dataSource.expandedDataSubject.value.length - 1) {
return true;
}
2019-11-26 10:43:37 +09:00
const n = this.dataSource.expandedDataSubject.value[i];
for (
let idx = i + 1;
idx < this.dataSource.expandedDataSubject.value.length;
idx++
) {
const element = this.dataSource.expandedDataSubject.value[idx];
if (n.level === element.level) {
return false;
}
if (n.level > element.level) {
return true;
}
2019-11-25 09:34:12 +09:00
}
2019-11-26 10:43:37 +09:00
return true;
2019-11-25 09:34:12 +09:00
}
2019-11-26 10:43:37 +09:00
appendDivArray(level: number): number[] {
if (0 === level) {
return [];
2019-11-25 10:28:59 +09:00
}
2019-11-26 10:43:37 +09:00
return Array.from({ length: level }, (_, i: number) => i);
2019-11-25 10:28:59 +09:00
}
onClickNode(node: OrganizationNode) {
2019-10-07 10:08:14 +09:00
this.selected.emit(node.deptInfo);
}
2019-11-25 10:28:59 +09:00
2019-11-26 10:43:37 +09:00
private findNodeIndex(node: FlatNode, depth: number): number {
if (!node) {
return -1;
}
const i = this.dataSource.expandedDataSubject.value.findIndex(n => {
2019-11-25 10:28:59 +09:00
return node.deptInfo.seq === n.deptInfo.seq;
});
2019-11-26 10:43:37 +09:00
if (0 === depth) {
return i;
2019-11-25 10:28:59 +09:00
}
2019-11-26 10:43:37 +09:00
return this.findNodeIndex(this.findParentNode(i), depth - 1);
}
private findParentNode(index: number): FlatNode {
const node = this.dataSource.expandedDataSubject.value[index];
2019-11-25 10:28:59 +09:00
2019-11-26 10:43:37 +09:00
for (let idx = index - 1; idx >= 0; idx--) {
const n = this.dataSource.expandedDataSubject.value[idx];
if (n.level === node.level - 1) {
return n;
2019-11-25 10:28:59 +09:00
}
}
2019-11-26 10:43:37 +09:00
return undefined;
2019-11-25 10:28:59 +09:00
}
2019-10-04 13:45:02 +09:00
}