UNPKG

6.4 kBJavaScriptView Raw
1import { toNodeId } from '../common';
2import { TreeIdentity as Identity } from '../TreeIdentity';
3export class TreeQuery {
4 constructor(args) {
5 this.walkDown = (visit) => {
6 let stopped = false;
7 const walk = (args) => {
8 if (!args.node || stopped) {
9 return;
10 }
11 const { id, key, namespace } = Identity.parse(args.node.id);
12 if (this.namespace && namespace !== this.namespace) {
13 return;
14 }
15 let skipChildren = false;
16 visit({
17 id,
18 key,
19 namespace,
20 node: args.node,
21 parent: args.parent,
22 index: args.index,
23 level: args.depth,
24 stop: () => (stopped = true),
25 skip: () => (skipChildren = true),
26 });
27 if (stopped) {
28 return;
29 }
30 let index = -1;
31 if (!skipChildren) {
32 for (const child of TreeQuery.children(args.node, undefined, { assign: false })) {
33 index++;
34 walk({
35 node: child,
36 parent: args.node,
37 index,
38 depth: args.depth + 1,
39 });
40 }
41 }
42 };
43 return walk({ node: this.root, depth: 0, index: -1 });
44 };
45 this.walkUp = (startAt, visit) => {
46 let level = -1;
47 const inner = (startAt, visit) => {
48 const current = this.findById(toNodeId(startAt));
49 level++;
50 if (current) {
51 let stop = false;
52 const parentNode = this.parent(current);
53 const { id, key, namespace } = Identity.parse(current.id);
54 const args = {
55 id,
56 key,
57 namespace,
58 node: current,
59 parent: parentNode,
60 get index() {
61 const id = current ? current.id : '';
62 return !parentNode
63 ? -1
64 : (parentNode.children || []).findIndex((node) => node.id === id);
65 },
66 level,
67 stop: () => (stop = true),
68 };
69 visit(args);
70 if (!stop && parentNode) {
71 inner(args.parent, visit);
72 }
73 }
74 };
75 return inner(startAt, visit);
76 };
77 this.find = (match) => {
78 let result;
79 this.walkDown((e) => {
80 if (match(e) === true) {
81 result = e.node;
82 e.stop();
83 }
84 });
85 return result ? result : undefined;
86 };
87 this.findById = (id) => {
88 if (!id) {
89 return undefined;
90 }
91 else {
92 const target = Identity.parse(typeof id === 'string' ? id : id.id);
93 return this.find((e) => {
94 if (!target.namespace && e.key === target.key) {
95 return true;
96 }
97 else {
98 return e.key === target.key && e.namespace === target.namespace;
99 }
100 });
101 }
102 };
103 this.parent = (node) => {
104 if (!node) {
105 return undefined;
106 }
107 if (typeof node !== 'object') {
108 const id = node;
109 node = this.findById(id);
110 if (!node) {
111 throw new Error(`Cannot find parent of '${id}' because that child node was not found.`);
112 }
113 }
114 let result;
115 const target = node;
116 this.walkDown((e) => {
117 if (TreeQuery.hasChild(e.node, target)) {
118 result = e.node;
119 e.stop();
120 }
121 });
122 return result;
123 };
124 this.ancestor = (node, match) => {
125 let result;
126 this.walkUp(node, (e) => {
127 if (match(e)) {
128 result = e.node;
129 e.stop();
130 }
131 });
132 return result;
133 };
134 this.depth = (node) => {
135 let depth = -1;
136 if (!node || !this.root) {
137 return depth;
138 }
139 else {
140 const id = toNodeId(node);
141 this.walkDown((e) => {
142 if (e.node.id === id) {
143 depth = e.level;
144 e.stop();
145 }
146 });
147 return depth;
148 }
149 };
150 this.exists = (input) => {
151 const node = typeof input === 'function' ? this.find(input) : this.findById(input);
152 return Boolean(node);
153 };
154 this.root = args.root;
155 this.namespace = (args.namespace || '').trim();
156 }
157 static create(args) {
158 const input = args;
159 const isQueryArgs = typeof input.root === 'object';
160 const root = (isQueryArgs ? input.root : args);
161 const namespace = isQueryArgs ? input.namespace || '' : '';
162 return new TreeQuery({ root, namespace });
163 }
164 static children(of, fn, options = {}) {
165 options = (typeof fn === 'object' ? fn : options) || {};
166 const children = (!of ? [] : of.children || []);
167 if (options.assign !== false && of && !of.children) {
168 of.children = children;
169 }
170 if (typeof fn === 'function') {
171 fn(children);
172 }
173 return children;
174 }
175 static hasChild(parent, child) {
176 const nodes = TreeQuery.children(parent);
177 const id = toNodeId(child);
178 return nodes.some((node) => node.id === id);
179 }
180 static childAt(index, parent) {
181 return TreeQuery.children(parent)[index];
182 }
183}