|
| 1 | +/** |
| 2 | + * @license |
| 3 | + * Copyright Google LLC All Rights Reserved. |
| 4 | + * |
| 5 | + * Use of this source code is governed by an MIT-style license that can be |
| 6 | + * found in the LICENSE file at https://angular.io/license |
| 7 | + */ |
| 8 | + |
| 9 | +import {CollectionViewer, DataSource} from '@angular/cdk/collections'; |
| 10 | +import {FlatTreeControl, TreeControl} from '@angular/cdk/tree'; |
| 11 | +import {Observable} from 'rxjs/Observable'; |
| 12 | +import {merge} from 'rxjs/observable/merge'; |
| 13 | +import {map} from 'rxjs/operators/map'; |
| 14 | +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; |
| 15 | +import {JsonNode, JsonDatabase} from './json-database'; |
| 16 | + |
| 17 | +/** Flat node with expandable and level information */ |
| 18 | +export class JsonFlatNode { |
| 19 | + key: string; |
| 20 | + value: any; |
| 21 | + level: number; |
| 22 | + expandable: boolean; |
| 23 | +} |
| 24 | + |
| 25 | +function _flattenNode(node: JsonNode, level: number, |
| 26 | + resultNodes: JsonFlatNode[], parentMap: boolean[]) { |
| 27 | + let flatNode: JsonFlatNode = new JsonFlatNode(); |
| 28 | + flatNode.key = node.key; |
| 29 | + flatNode.value = node.value; |
| 30 | + flatNode.level = level; |
| 31 | + flatNode.expandable = !!node.children; |
| 32 | + resultNodes.push(flatNode); |
| 33 | + |
| 34 | + if (flatNode.expandable) { |
| 35 | + node.children.forEach((child, index) => { |
| 36 | + let childParentMap: boolean[] = parentMap.slice(); |
| 37 | + childParentMap.push(index != node.children.length - 1); |
| 38 | + _flattenNode(child, level + 1, resultNodes, childParentMap); |
| 39 | + }); |
| 40 | + } |
| 41 | + return resultNodes; |
| 42 | +} |
| 43 | + |
| 44 | +/** Tree flattener to transfrom JsonNode to JsonFlatNode */ |
| 45 | +export function flattenNodes(structuredData: JsonNode[]): JsonFlatNode[] { |
| 46 | + let resultNodes: JsonFlatNode[] = []; |
| 47 | + structuredData.forEach(node => _flattenNode(node, 0, resultNodes, [])); |
| 48 | + return resultNodes; |
| 49 | +} |
| 50 | + |
| 51 | +export function expandFlattenedNodes(nodes: JsonFlatNode[], |
| 52 | + treeControl: TreeControl<JsonFlatNode>): JsonFlatNode[] { |
| 53 | + let results: JsonFlatNode[] = []; |
| 54 | + let currentExpand: boolean[] = []; |
| 55 | + currentExpand[0] = true; |
| 56 | + |
| 57 | + nodes.forEach((node) => { |
| 58 | + let expand = true; |
| 59 | + for (let i = 0; i <= node.level; i++) { |
| 60 | + expand = expand && currentExpand[i]; |
| 61 | + } |
| 62 | + if (expand) { |
| 63 | + results.push(node); |
| 64 | + } |
| 65 | + if (node.expandable) { |
| 66 | + currentExpand[node.level + 1] = treeControl.isExpanded(node); |
| 67 | + } |
| 68 | + }); |
| 69 | + return results; |
| 70 | +} |
| 71 | + |
| 72 | +/** Flat data source */ |
| 73 | +export class FlatDataSource implements DataSource<any> { |
| 74 | + _flattenedData = new BehaviorSubject<any>([]); |
| 75 | + get flattenedData() { return this._flattenedData.value; } |
| 76 | + |
| 77 | + _expandedData = new BehaviorSubject<any>([]); |
| 78 | + get expandedData() { return this._expandedData.value; } |
| 79 | + |
| 80 | + constructor(database: JsonDatabase, private treeControl: FlatTreeControl<JsonFlatNode>) { |
| 81 | + database.dataChange.subscribe((tree) => { |
| 82 | + this._flattenedData.next(flattenNodes(tree)); |
| 83 | + this.treeControl.dataNodes = this.flattenedData; |
| 84 | + }); |
| 85 | + } |
| 86 | + |
| 87 | + connect(collectionViewer: CollectionViewer): Observable<JsonFlatNode[]> { |
| 88 | + return merge([ |
| 89 | + collectionViewer.viewChange, |
| 90 | + this.treeControl.expansionModel.onChange, |
| 91 | + this._flattenedData]) |
| 92 | + .pipe(map(() => { |
| 93 | + this._expandedData.next( |
| 94 | + expandFlattenedNodes(this.flattenedData, this.treeControl)); |
| 95 | + return this.expandedData; |
| 96 | + })); |
| 97 | + } |
| 98 | + |
| 99 | + disconnect() { |
| 100 | + } |
| 101 | +} |
| 102 | + |
0 commit comments