]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/tree/tree.ts
Docs: merge 3.2 release notes
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / share / tree / tree.ts
1
2 export class TreeNode {
3     // Unique identifier
4     id: any;
5
6     // Display label
7     label: string;
8
9     // True if child nodes should be visible
10     expanded: boolean;
11
12     children: TreeNode[];
13
14     // Set by the tree.
15     depth: number;
16
17     // Set by the tree.
18     selected: boolean;
19
20     // Optional link to user-provided stuff.
21     // This field is ignored by the tree.
22     callerData: any;
23
24     constructor(values: {[key: string]: any}) {
25         this.children = [];
26         this.expanded = true;
27         this.depth = 0;
28         this.selected = false;
29
30         if (!values) { return; }
31
32         if ('id' in values) { this.id = values.id; }
33         if ('label' in values) { this.label = values.label; }
34         if ('children' in values) { this.children = values.children; }
35         if ('expanded' in values) { this.expanded = values.expanded; }
36         if ('callerData' in values) { this.callerData = values.callerData; }
37     }
38
39     toggleExpand() {
40         this.expanded = !this.expanded;
41     }
42 }
43
44 export class Tree {
45
46     rootNode: TreeNode;
47     idMap: {[id: string]: TreeNode};
48
49     constructor(rootNode?: TreeNode) {
50         this.rootNode = rootNode;
51         this.idMap = {};
52     }
53
54     // Returns a depth-first list of tree nodes
55     // Tweaks node attributes along the way to match the shape of the tree.
56     nodeList(filterHidden?: boolean): TreeNode[] {
57
58         const nodes = [];
59
60         const recurseTree =
61             (node: TreeNode, depth: number, hidden: boolean) => {
62             if (!node) { return; }
63
64             node.depth = depth++;
65             this.idMap[node.id + ''] = node;
66
67             if (hidden) {
68                 // it could be confusing for a hidden node to be selected.
69                 node.selected = false;
70             }
71
72             if (hidden && filterHidden) {
73                 // Avoid adding hidden child nodes to the list.
74             } else {
75                 nodes.push(node);
76             }
77
78             node.children.forEach(n => recurseTree(n, depth, !node.expanded));
79         };
80
81         recurseTree(this.rootNode, 0, false);
82         return nodes;
83     }
84
85     findNode(id: any): TreeNode {
86         if (this.idMap[id + '']) {
87             return this.idMap[id + ''];
88         } else {
89             // nodeList re-indexes all the nodes.
90             this.nodeList();
91             return this.idMap[id + ''];
92         }
93     }
94
95     findParentNode(node: TreeNode) {
96         const list = this.nodeList();
97         for (let idx = 0; idx < list.length; idx++) {
98             const pnode = list[idx];
99             if (pnode.children.filter(c => c.id === node.id).length) {
100                 return pnode;
101             }
102         }
103         return null;
104     }
105
106     removeNode(node: TreeNode) {
107         if (!node) { return; }
108         const pnode = this.findParentNode(node);
109         if (pnode) {
110             pnode.children = pnode.children.filter(n => n.id !== node.id);
111         } else {
112             this.rootNode = null;
113         }
114     }
115
116     expandAll() {
117         this.nodeList().forEach(node => node.expanded = true);
118     }
119
120     collapseAll() {
121         this.nodeList().forEach(node => node.expanded = false);
122     }
123
124     selectedNode(): TreeNode {
125         return this.nodeList().filter(node => node.selected)[0];
126     }
127
128     selectNode(node: TreeNode) {
129         this.nodeList().forEach(n => n.selected = false);
130         node.selected = true;
131     }
132 }
133