jqtree 1.8.5 → 1.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tool-versions +2 -0
- package/README.md +1 -1
- package/bower.json +1 -1
- package/eslint.config.mjs +69 -0
- package/package.json +27 -28
- package/src/dataLoader.ts +62 -62
- package/src/dragAndDropHandler/dragElement.ts +10 -10
- package/src/dragAndDropHandler/generateHitAreas.ts +5 -5
- package/src/dragAndDropHandler/index.ts +250 -250
- package/src/dragAndDropHandler/types.ts +1 -1
- package/src/elementsRenderer.ts +124 -125
- package/src/jqtreeMethodTypes.ts +1 -1
- package/src/jqtreeOptions.ts +5 -5
- package/src/keyHandler.ts +66 -58
- package/src/mouseHandler.ts +211 -215
- package/src/node.ts +390 -392
- package/src/nodeElement/folderElement.ts +48 -48
- package/src/nodeElement/ghostDropHint.ts +4 -4
- package/src/nodeElement/index.ts +39 -39
- package/src/nodeUtils.ts +1 -1
- package/src/position.ts +1 -1
- package/src/saveStateHandler.ts +135 -137
- package/src/scrollHandler/containerScrollParent.ts +70 -69
- package/src/scrollHandler/createScrollParent.ts +1 -0
- package/src/scrollHandler/documentScrollParent.ts +88 -87
- package/src/scrollHandler.ts +20 -20
- package/src/selectNodeHandler.ts +16 -16
- package/src/simple.widget.ts +16 -15
- package/src/tree.jquery.d.ts +25 -27
- package/src/tree.jquery.ts +849 -843
- package/src/version.ts +1 -1
- package/tree.jquery.debug.js +2533 -2523
- package/tree.jquery.debug.js.map +1 -1
- package/tree.jquery.js +2 -2
- package/tree.jquery.js.map +1 -1
package/src/node.ts
CHANGED
|
@@ -4,20 +4,20 @@ import { Position } from "./position";
|
|
|
4
4
|
type IterateCallback = (node: Node, level: number) => boolean;
|
|
5
5
|
|
|
6
6
|
export class Node implements INode {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
|
|
9
9
|
public children: Node[];
|
|
10
|
-
public
|
|
10
|
+
public element?: HTMLElement;
|
|
11
|
+
public id?: NodeId;
|
|
11
12
|
public idMapping: Map<NodeId, Node>;
|
|
12
|
-
public tree?: Node;
|
|
13
|
-
public nodeClass?: typeof Node;
|
|
14
|
-
public load_on_demand: boolean;
|
|
15
|
-
public is_open: boolean;
|
|
16
|
-
public element: HTMLElement;
|
|
17
13
|
public is_loading: boolean;
|
|
14
|
+
public is_open: boolean;
|
|
18
15
|
public isEmptyFolder: boolean;
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
public load_on_demand: boolean;
|
|
17
|
+
public name: string;
|
|
18
|
+
public nodeClass?: typeof Node;
|
|
19
|
+
public parent: Node | null;
|
|
20
|
+
public tree?: Node;
|
|
21
21
|
|
|
22
22
|
constructor(
|
|
23
23
|
nodeData: NodeData | null = null,
|
|
@@ -44,76 +44,59 @@ export class Node implements INode {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
private createNode(nodeData?: NodeData): Node {
|
|
48
|
+
const nodeClass = this.getNodeClass();
|
|
49
|
+
return new nodeClass(nodeData);
|
|
50
|
+
}
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
private doRemoveChild(node: Node): void {
|
|
53
|
+
this.children.splice(this.getChildIndex(node), 1);
|
|
54
|
+
this.tree?.removeNodeFromIndex(node);
|
|
55
|
+
}
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
private getNodeClass(): typeof Node {
|
|
58
|
+
return this.nodeClass ?? this.tree?.nodeClass ?? Node;
|
|
59
|
+
}
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
// Load children data from nodeInfo if it has children
|
|
62
|
+
private loadChildrenFromData(nodeInfo: NodeData) {
|
|
63
|
+
if (isNodeRecordWithChildren(nodeInfo) && nodeInfo.children.length) {
|
|
64
|
+
this.loadFromData(nodeInfo.children);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
private setParent(parent: Node): void {
|
|
69
|
+
this.parent = parent;
|
|
70
|
+
this.tree = parent.tree;
|
|
71
|
+
this.tree?.addNodeToIndex(this);
|
|
72
|
+
}
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return;
|
|
66
|
-
} else if (typeof o === "string") {
|
|
67
|
-
this.name = o;
|
|
68
|
-
} else if (typeof o === "object") {
|
|
69
|
-
for (const key in o) {
|
|
70
|
-
if (Object.prototype.hasOwnProperty.call(o, key)) {
|
|
71
|
-
const value = o[key];
|
|
74
|
+
public addAfter(nodeInfo: NodeData): Node | null {
|
|
75
|
+
if (!this.parent) {
|
|
76
|
+
return null;
|
|
77
|
+
} else {
|
|
78
|
+
const node = this.createNode(nodeInfo);
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} else if (key !== "children" && key !== "parent") {
|
|
79
|
-
// You can't update the children or the parent using this function
|
|
80
|
-
this[key] = value;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
80
|
+
const childIndex = this.parent.getChildIndex(this);
|
|
81
|
+
this.parent.addChildAtPosition(node, childIndex + 1);
|
|
82
|
+
|
|
83
|
+
node.loadChildrenFromData(nodeInfo);
|
|
84
|
+
return node;
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
{
|
|
93
|
-
name: 'node1',
|
|
94
|
-
children: [
|
|
95
|
-
{ name: 'child1' },
|
|
96
|
-
{ name: 'child2' }
|
|
97
|
-
]
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: 'node2'
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
*/
|
|
104
|
-
public loadFromData(data: NodeData[]): Node {
|
|
105
|
-
this.removeChildren();
|
|
88
|
+
public addBefore(nodeInfo: NodeData): Node | null {
|
|
89
|
+
if (!this.parent) {
|
|
90
|
+
return null;
|
|
91
|
+
} else {
|
|
92
|
+
const node = this.createNode(nodeInfo);
|
|
106
93
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.addChild(node);
|
|
94
|
+
const childIndex = this.parent.getChildIndex(this);
|
|
95
|
+
this.parent.addChildAtPosition(node, childIndex);
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
97
|
+
node.loadChildrenFromData(nodeInfo);
|
|
98
|
+
return node;
|
|
114
99
|
}
|
|
115
|
-
|
|
116
|
-
return this;
|
|
117
100
|
}
|
|
118
101
|
|
|
119
102
|
/*
|
|
@@ -141,128 +124,62 @@ export class Node implements INode {
|
|
|
141
124
|
node.setParent(this);
|
|
142
125
|
}
|
|
143
126
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
*/
|
|
149
|
-
public removeChild(node: Node): void {
|
|
150
|
-
// remove children from the index
|
|
151
|
-
node.removeChildren();
|
|
152
|
-
|
|
153
|
-
this.doRemoveChild(node);
|
|
127
|
+
public addNodeToIndex(node: Node): void {
|
|
128
|
+
if (node.id != null) {
|
|
129
|
+
this.idMapping.set(node.id, node);
|
|
130
|
+
}
|
|
154
131
|
}
|
|
155
132
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
public getChildIndex(node: Node): number {
|
|
162
|
-
return this.children.indexOf(node);
|
|
163
|
-
}
|
|
133
|
+
public addParent(nodeInfo: NodeData): Node | null {
|
|
134
|
+
if (!this.parent) {
|
|
135
|
+
return null;
|
|
136
|
+
} else {
|
|
137
|
+
const newParent = this.createNode(nodeInfo);
|
|
164
138
|
|
|
165
|
-
|
|
166
|
-
|
|
139
|
+
if (this.tree) {
|
|
140
|
+
newParent.setParent(this.tree);
|
|
141
|
+
}
|
|
142
|
+
const originalParent = this.parent;
|
|
167
143
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
*/
|
|
172
|
-
public hasChildren(): boolean {
|
|
173
|
-
return this.children.length !== 0;
|
|
174
|
-
}
|
|
144
|
+
for (const child of originalParent.children) {
|
|
145
|
+
newParent.addChild(child);
|
|
146
|
+
}
|
|
175
147
|
|
|
176
|
-
|
|
177
|
-
|
|
148
|
+
originalParent.children = [];
|
|
149
|
+
originalParent.addChild(newParent);
|
|
150
|
+
return newParent;
|
|
151
|
+
}
|
|
178
152
|
}
|
|
179
153
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
Calls callback with (node, level).
|
|
184
|
-
|
|
185
|
-
The callback must return true to continue the iteration on current node.
|
|
186
|
-
|
|
187
|
-
tree.iterate(
|
|
188
|
-
function(node, level) {
|
|
189
|
-
console.log(node.name);
|
|
154
|
+
public append(nodeInfo: NodeData): Node {
|
|
155
|
+
const node = this.createNode(nodeInfo);
|
|
156
|
+
this.addChild(node);
|
|
190
157
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
);
|
|
158
|
+
node.loadChildrenFromData(nodeInfo);
|
|
159
|
+
return node;
|
|
160
|
+
}
|
|
195
161
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const _iterate = (node: Node, level: number): void => {
|
|
199
|
-
if (node.children) {
|
|
200
|
-
for (const child of node.children) {
|
|
201
|
-
const result = callback(child, level);
|
|
162
|
+
public filter(f: (node: Node) => boolean): Node[] {
|
|
163
|
+
const result: Node[] = [];
|
|
202
164
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
165
|
+
this.iterate((node: Node) => {
|
|
166
|
+
if (f(node)) {
|
|
167
|
+
result.push(node);
|
|
207
168
|
}
|
|
208
|
-
};
|
|
209
169
|
|
|
210
|
-
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return result;
|
|
211
174
|
}
|
|
212
175
|
|
|
213
176
|
/*
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
Argument position: Position.BEFORE, Position.AFTER or Position.Inside
|
|
177
|
+
Get child index.
|
|
217
178
|
|
|
218
|
-
|
|
219
|
-
tree.moveNode(node1, node2, Position.AFTER);
|
|
179
|
+
var index = getChildIndex(node);
|
|
220
180
|
*/
|
|
221
|
-
public
|
|
222
|
-
|
|
223
|
-
targetNode: Node,
|
|
224
|
-
position: Position,
|
|
225
|
-
): boolean {
|
|
226
|
-
if (!movedNode.parent || movedNode.isParentOf(targetNode)) {
|
|
227
|
-
// - Node is parent of target node
|
|
228
|
-
// - Or, parent is empty
|
|
229
|
-
return false;
|
|
230
|
-
} else {
|
|
231
|
-
movedNode.parent.doRemoveChild(movedNode);
|
|
232
|
-
|
|
233
|
-
switch (position) {
|
|
234
|
-
case Position.After: {
|
|
235
|
-
if (targetNode.parent) {
|
|
236
|
-
targetNode.parent.addChildAtPosition(
|
|
237
|
-
movedNode,
|
|
238
|
-
targetNode.parent.getChildIndex(targetNode) + 1,
|
|
239
|
-
);
|
|
240
|
-
return true;
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
case Position.Before: {
|
|
246
|
-
if (targetNode.parent) {
|
|
247
|
-
targetNode.parent.addChildAtPosition(
|
|
248
|
-
movedNode,
|
|
249
|
-
targetNode.parent.getChildIndex(targetNode),
|
|
250
|
-
);
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
case Position.Inside: {
|
|
257
|
-
// move inside as first child
|
|
258
|
-
targetNode.addChildAtPosition(movedNode, 0);
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
default:
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
181
|
+
public getChildIndex(node: Node): number {
|
|
182
|
+
return this.children.indexOf(node);
|
|
266
183
|
}
|
|
267
184
|
|
|
268
185
|
/*
|
|
@@ -293,7 +210,7 @@ export class Node implements INode {
|
|
|
293
210
|
}
|
|
294
211
|
|
|
295
212
|
if (node.hasChildren()) {
|
|
296
|
-
tmpNode
|
|
213
|
+
tmpNode.children = getDataFromNodes(node.children);
|
|
297
214
|
}
|
|
298
215
|
|
|
299
216
|
return tmpNode;
|
|
@@ -307,121 +224,22 @@ export class Node implements INode {
|
|
|
307
224
|
}
|
|
308
225
|
}
|
|
309
226
|
|
|
310
|
-
public
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
public getNodeByNameMustExist(name: string): Node {
|
|
315
|
-
const node = this.getNodeByCallback((n: Node) => n.name === name);
|
|
316
|
-
|
|
317
|
-
if (!node) {
|
|
318
|
-
throw `Node with name ${name} not found`;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return node;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
public getNodeByCallback(callback: (node: Node) => boolean): Node | null {
|
|
325
|
-
let result: Node | null = null;
|
|
326
|
-
|
|
327
|
-
this.iterate((node: Node) => {
|
|
328
|
-
if (result) {
|
|
329
|
-
return false;
|
|
330
|
-
} else if (callback(node)) {
|
|
331
|
-
result = node;
|
|
332
|
-
return false;
|
|
333
|
-
} else {
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
return result;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
public addAfter(nodeInfo: NodeData): Node | null {
|
|
342
|
-
if (!this.parent) {
|
|
343
|
-
return null;
|
|
344
|
-
} else {
|
|
345
|
-
const node = this.createNode(nodeInfo);
|
|
346
|
-
|
|
347
|
-
const childIndex = this.parent.getChildIndex(this);
|
|
348
|
-
this.parent.addChildAtPosition(node, childIndex + 1);
|
|
349
|
-
|
|
350
|
-
node.loadChildrenFromData(nodeInfo);
|
|
351
|
-
return node;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
public addBefore(nodeInfo: NodeData): Node | null {
|
|
356
|
-
if (!this.parent) {
|
|
357
|
-
return null;
|
|
358
|
-
} else {
|
|
359
|
-
const node = this.createNode(nodeInfo);
|
|
360
|
-
|
|
361
|
-
const childIndex = this.parent.getChildIndex(this);
|
|
362
|
-
this.parent.addChildAtPosition(node, childIndex);
|
|
363
|
-
|
|
364
|
-
node.loadChildrenFromData(nodeInfo);
|
|
365
|
-
return node;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
public addParent(nodeInfo: NodeData): Node | null {
|
|
370
|
-
if (!this.parent) {
|
|
227
|
+
public getLastChild(): Node | null {
|
|
228
|
+
if (!this.hasChildren()) {
|
|
371
229
|
return null;
|
|
372
230
|
} else {
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
if (this.tree) {
|
|
376
|
-
newParent.setParent(this.tree);
|
|
377
|
-
}
|
|
378
|
-
const originalParent = this.parent;
|
|
231
|
+
const lastChild = this.children[this.children.length - 1];
|
|
379
232
|
|
|
380
|
-
|
|
381
|
-
|
|
233
|
+
if (!lastChild) {
|
|
234
|
+
return null;
|
|
382
235
|
}
|
|
383
236
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
public remove(): void {
|
|
391
|
-
if (this.parent) {
|
|
392
|
-
this.parent.removeChild(this);
|
|
393
|
-
this.parent = null;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
public append(nodeInfo: NodeData): Node {
|
|
398
|
-
const node = this.createNode(nodeInfo);
|
|
399
|
-
this.addChild(node);
|
|
400
|
-
|
|
401
|
-
node.loadChildrenFromData(nodeInfo);
|
|
402
|
-
return node;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
public prepend(nodeInfo: NodeData): Node {
|
|
406
|
-
const node = this.createNode(nodeInfo);
|
|
407
|
-
this.addChildAtPosition(node, 0);
|
|
408
|
-
|
|
409
|
-
node.loadChildrenFromData(nodeInfo);
|
|
410
|
-
return node;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
public isParentOf(node: Node): boolean {
|
|
414
|
-
let parent = node.parent;
|
|
415
|
-
|
|
416
|
-
while (parent) {
|
|
417
|
-
if (parent === this) {
|
|
418
|
-
return true;
|
|
237
|
+
if (!(lastChild.hasChildren() && lastChild.is_open)) {
|
|
238
|
+
return lastChild;
|
|
239
|
+
} else {
|
|
240
|
+
return lastChild.getLastChild();
|
|
419
241
|
}
|
|
420
|
-
|
|
421
|
-
parent = parent.parent;
|
|
422
242
|
}
|
|
423
|
-
|
|
424
|
-
return false;
|
|
425
243
|
}
|
|
426
244
|
|
|
427
245
|
public getLevel(): number {
|
|
@@ -436,40 +254,18 @@ export class Node implements INode {
|
|
|
436
254
|
return level;
|
|
437
255
|
}
|
|
438
256
|
|
|
439
|
-
public
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
public addNodeToIndex(node: Node): void {
|
|
444
|
-
if (node.id != null) {
|
|
445
|
-
this.idMapping.set(node.id, node);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
public removeNodeFromIndex(node: Node): void {
|
|
450
|
-
if (node.id != null) {
|
|
451
|
-
this.idMapping.delete(node.id);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
public removeChildren(): void {
|
|
456
|
-
this.iterate((child: Node) => {
|
|
457
|
-
this.tree?.removeNodeFromIndex(child);
|
|
458
|
-
return true;
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
this.children = [];
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
public getPreviousSibling(): Node | null {
|
|
465
|
-
if (!this.parent) {
|
|
257
|
+
public getNextNode(includeChildren = true): Node | null {
|
|
258
|
+
if (includeChildren && this.hasChildren()) {
|
|
259
|
+
return this.children[0] ?? null;
|
|
260
|
+
} else if (!this.parent) {
|
|
466
261
|
return null;
|
|
467
262
|
} else {
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
263
|
+
const nextSibling = this.getNextSibling();
|
|
264
|
+
|
|
265
|
+
if (nextSibling) {
|
|
266
|
+
return nextSibling;
|
|
471
267
|
} else {
|
|
472
|
-
return
|
|
268
|
+
return this.parent.getNextNode(false);
|
|
473
269
|
}
|
|
474
270
|
}
|
|
475
271
|
}
|
|
@@ -480,51 +276,17 @@ export class Node implements INode {
|
|
|
480
276
|
} else {
|
|
481
277
|
const nextIndex = this.parent.getChildIndex(this) + 1;
|
|
482
278
|
if (nextIndex < this.parent.children.length) {
|
|
483
|
-
return this.parent.children[nextIndex]
|
|
279
|
+
return this.parent.children[nextIndex] ?? null;
|
|
484
280
|
} else {
|
|
485
281
|
return null;
|
|
486
282
|
}
|
|
487
283
|
}
|
|
488
284
|
}
|
|
489
285
|
|
|
490
|
-
public getNodesByProperty(key: string, value: unknown): Node[] {
|
|
491
|
-
return this.filter((node: Node) => node[key] === value);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
public filter(f: (node: Node) => boolean): Node[] {
|
|
495
|
-
const result: Node[] = [];
|
|
496
|
-
|
|
497
|
-
this.iterate((node: Node) => {
|
|
498
|
-
if (f(node)) {
|
|
499
|
-
result.push(node);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return true;
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
return result;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
public getNextNode(includeChildren = true): Node | null {
|
|
509
|
-
if (includeChildren && this.hasChildren()) {
|
|
510
|
-
return this.children[0] || null;
|
|
511
|
-
} else if (!this.parent) {
|
|
512
|
-
return null;
|
|
513
|
-
} else {
|
|
514
|
-
const nextSibling = this.getNextSibling();
|
|
515
|
-
|
|
516
|
-
if (nextSibling) {
|
|
517
|
-
return nextSibling;
|
|
518
|
-
} else {
|
|
519
|
-
return this.parent.getNextNode(false);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
286
|
public getNextVisibleNode(): Node | null {
|
|
525
287
|
if (this.hasChildren() && this.is_open) {
|
|
526
288
|
// First child
|
|
527
|
-
return this.children[0]
|
|
289
|
+
return this.children[0] ?? null;
|
|
528
290
|
} else {
|
|
529
291
|
if (!this.parent) {
|
|
530
292
|
return null;
|
|
@@ -541,6 +303,57 @@ export class Node implements INode {
|
|
|
541
303
|
}
|
|
542
304
|
}
|
|
543
305
|
|
|
306
|
+
public getNodeByCallback(callback: (node: Node) => boolean): Node | null {
|
|
307
|
+
let result: Node | null = null;
|
|
308
|
+
|
|
309
|
+
this.iterate((node: Node) => {
|
|
310
|
+
if (result) {
|
|
311
|
+
return false;
|
|
312
|
+
} else if (callback(node)) {
|
|
313
|
+
result = node;
|
|
314
|
+
return false;
|
|
315
|
+
} else {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
public getNodeById(nodeId: NodeId): Node | null {
|
|
324
|
+
return this.idMapping.get(nodeId) ?? null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public getNodeByName(name: string): Node | null {
|
|
328
|
+
return this.getNodeByCallback((node: Node) => node.name === name);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
public getNodeByNameMustExist(name: string): Node {
|
|
332
|
+
const node = this.getNodeByCallback((n: Node) => n.name === name);
|
|
333
|
+
|
|
334
|
+
if (!node) {
|
|
335
|
+
throw new Error(`Node with name ${name} not found`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return node;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
public getNodesByProperty(key: string, value: unknown): Node[] {
|
|
342
|
+
return this.filter((node: Node) => node[key] === value);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
public getParent(): Node | null {
|
|
346
|
+
// Return parent except if it is the root node
|
|
347
|
+
if (!this.parent) {
|
|
348
|
+
return null;
|
|
349
|
+
} else if (!this.parent.parent) {
|
|
350
|
+
// Root node -> null
|
|
351
|
+
return null;
|
|
352
|
+
} else {
|
|
353
|
+
return this.parent;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
544
357
|
public getPreviousNode(): Node | null {
|
|
545
358
|
if (!this.parent) {
|
|
546
359
|
return null;
|
|
@@ -557,6 +370,19 @@ export class Node implements INode {
|
|
|
557
370
|
}
|
|
558
371
|
}
|
|
559
372
|
|
|
373
|
+
public getPreviousSibling(): Node | null {
|
|
374
|
+
if (!this.parent) {
|
|
375
|
+
return null;
|
|
376
|
+
} else {
|
|
377
|
+
const previousIndex = this.parent.getChildIndex(this) - 1;
|
|
378
|
+
if (previousIndex >= 0) {
|
|
379
|
+
return this.parent.children[previousIndex] ?? null;
|
|
380
|
+
} else {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
560
386
|
public getPreviousVisibleNode(): Node | null {
|
|
561
387
|
if (!this.parent) {
|
|
562
388
|
return null;
|
|
@@ -578,34 +404,15 @@ export class Node implements INode {
|
|
|
578
404
|
}
|
|
579
405
|
}
|
|
580
406
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (!this.parent) {
|
|
584
|
-
return null;
|
|
585
|
-
} else if (!this.parent.parent) {
|
|
586
|
-
// Root node -> null
|
|
587
|
-
return null;
|
|
588
|
-
} else {
|
|
589
|
-
return this.parent;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
public getLastChild(): Node | null {
|
|
594
|
-
if (!this.hasChildren()) {
|
|
595
|
-
return null;
|
|
596
|
-
} else {
|
|
597
|
-
const lastChild = this.children[this.children.length - 1];
|
|
598
|
-
|
|
599
|
-
if (!lastChild) {
|
|
600
|
-
return null;
|
|
601
|
-
}
|
|
407
|
+
/*
|
|
408
|
+
Does the tree have children?
|
|
602
409
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
410
|
+
if (tree.hasChildren()) {
|
|
411
|
+
//
|
|
412
|
+
}
|
|
413
|
+
*/
|
|
414
|
+
public hasChildren(): boolean {
|
|
415
|
+
return this.children.length !== 0;
|
|
609
416
|
}
|
|
610
417
|
|
|
611
418
|
// Init Node from data without making it the root of the tree
|
|
@@ -632,30 +439,221 @@ export class Node implements INode {
|
|
|
632
439
|
addNode(data);
|
|
633
440
|
}
|
|
634
441
|
|
|
635
|
-
|
|
636
|
-
this.
|
|
637
|
-
this.tree = parent.tree;
|
|
638
|
-
this.tree?.addNodeToIndex(this);
|
|
442
|
+
public isFolder(): boolean {
|
|
443
|
+
return this.hasChildren() || this.load_on_demand;
|
|
639
444
|
}
|
|
640
445
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
446
|
+
public isParentOf(node: Node): boolean {
|
|
447
|
+
let parent = node.parent;
|
|
448
|
+
|
|
449
|
+
while (parent) {
|
|
450
|
+
if (parent === this) {
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
parent = parent.parent;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return false;
|
|
644
458
|
}
|
|
645
459
|
|
|
646
|
-
|
|
647
|
-
|
|
460
|
+
/*
|
|
461
|
+
Iterate over all the nodes in the tree.
|
|
462
|
+
|
|
463
|
+
Calls callback with (node, level).
|
|
464
|
+
|
|
465
|
+
The callback must return true to continue the iteration on current node.
|
|
466
|
+
|
|
467
|
+
tree.iterate(
|
|
468
|
+
function(node, level) {
|
|
469
|
+
console.log(node.name);
|
|
470
|
+
|
|
471
|
+
// stop iteration after level 2
|
|
472
|
+
return (level <= 2);
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
*/
|
|
477
|
+
public iterate(callback: IterateCallback): void {
|
|
478
|
+
const _iterate = (node: Node, level: number): void => {
|
|
479
|
+
for (const child of node.children) {
|
|
480
|
+
const result = callback(child, level);
|
|
481
|
+
|
|
482
|
+
if (result && child.hasChildren()) {
|
|
483
|
+
_iterate(child, level + 1);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
_iterate(this, 0);
|
|
648
489
|
}
|
|
649
490
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
491
|
+
/*
|
|
492
|
+
Create tree from data.
|
|
493
|
+
|
|
494
|
+
Structure of data is:
|
|
495
|
+
[
|
|
496
|
+
{
|
|
497
|
+
name: 'node1',
|
|
498
|
+
children: [
|
|
499
|
+
{ name: 'child1' },
|
|
500
|
+
{ name: 'child2' }
|
|
501
|
+
]
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: 'node2'
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
*/
|
|
508
|
+
public loadFromData(data: NodeData[]): this {
|
|
509
|
+
this.removeChildren();
|
|
510
|
+
|
|
511
|
+
for (const childData of data) {
|
|
512
|
+
const node = this.createNode(childData);
|
|
513
|
+
this.addChild(node);
|
|
514
|
+
|
|
515
|
+
if (isNodeRecordWithChildren(childData)) {
|
|
516
|
+
node.loadFromData(childData.children);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return this;
|
|
653
521
|
}
|
|
654
522
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
523
|
+
/*
|
|
524
|
+
Move node relative to another node.
|
|
525
|
+
|
|
526
|
+
Argument position: Position.BEFORE, Position.AFTER or Position.Inside
|
|
527
|
+
|
|
528
|
+
// move node1 after node2
|
|
529
|
+
tree.moveNode(node1, node2, Position.AFTER);
|
|
530
|
+
*/
|
|
531
|
+
public moveNode(
|
|
532
|
+
movedNode: Node,
|
|
533
|
+
targetNode: Node,
|
|
534
|
+
position: Position,
|
|
535
|
+
): boolean {
|
|
536
|
+
if (!movedNode.parent || movedNode.isParentOf(targetNode)) {
|
|
537
|
+
// - Node is parent of target node
|
|
538
|
+
// - Or, parent is empty
|
|
539
|
+
return false;
|
|
540
|
+
} else {
|
|
541
|
+
movedNode.parent.doRemoveChild(movedNode);
|
|
542
|
+
|
|
543
|
+
switch (position) {
|
|
544
|
+
case Position.After: {
|
|
545
|
+
if (targetNode.parent) {
|
|
546
|
+
targetNode.parent.addChildAtPosition(
|
|
547
|
+
movedNode,
|
|
548
|
+
targetNode.parent.getChildIndex(targetNode) + 1,
|
|
549
|
+
);
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
case Position.Before: {
|
|
556
|
+
if (targetNode.parent) {
|
|
557
|
+
targetNode.parent.addChildAtPosition(
|
|
558
|
+
movedNode,
|
|
559
|
+
targetNode.parent.getChildIndex(targetNode),
|
|
560
|
+
);
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
case Position.Inside: {
|
|
567
|
+
// move inside as first child
|
|
568
|
+
targetNode.addChildAtPosition(movedNode, 0);
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
default:
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
public prepend(nodeInfo: NodeData): Node {
|
|
579
|
+
const node = this.createNode(nodeInfo);
|
|
580
|
+
this.addChildAtPosition(node, 0);
|
|
581
|
+
|
|
582
|
+
node.loadChildrenFromData(nodeInfo);
|
|
583
|
+
return node;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
public remove(): void {
|
|
587
|
+
if (this.parent) {
|
|
588
|
+
this.parent.removeChild(this);
|
|
589
|
+
this.parent = null;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/*
|
|
594
|
+
Remove child. This also removes the children of the node.
|
|
595
|
+
|
|
596
|
+
tree.removeChild(tree.children[0]);
|
|
597
|
+
*/
|
|
598
|
+
public removeChild(node: Node): void {
|
|
599
|
+
// remove children from the index
|
|
600
|
+
node.removeChildren();
|
|
601
|
+
|
|
602
|
+
this.doRemoveChild(node);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
public removeChildren(): void {
|
|
606
|
+
this.iterate((child: Node) => {
|
|
607
|
+
this.tree?.removeNodeFromIndex(child);
|
|
608
|
+
return true;
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
this.children = [];
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
public removeNodeFromIndex(node: Node): void {
|
|
615
|
+
if (node.id != null) {
|
|
616
|
+
this.idMapping.delete(node.id);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/*
|
|
621
|
+
Set the data of this node.
|
|
622
|
+
|
|
623
|
+
setData(string): set the name of the node
|
|
624
|
+
setData(object): set attributes of the node
|
|
625
|
+
|
|
626
|
+
Examples:
|
|
627
|
+
setData('node1')
|
|
628
|
+
|
|
629
|
+
setData({ name: 'node1', id: 1});
|
|
630
|
+
|
|
631
|
+
setData({ name: 'node2', id: 2, color: 'green'});
|
|
632
|
+
|
|
633
|
+
* This is an internal function; it is not in the docs
|
|
634
|
+
* Does not remove existing node values
|
|
635
|
+
*/
|
|
636
|
+
public setData(o: NodeData | null): void {
|
|
637
|
+
if (!o) {
|
|
638
|
+
return;
|
|
639
|
+
} else if (typeof o === "string") {
|
|
640
|
+
this.name = o;
|
|
641
|
+
} else if (typeof o === "object") {
|
|
642
|
+
for (const key in o) {
|
|
643
|
+
if (Object.prototype.hasOwnProperty.call(o, key)) {
|
|
644
|
+
const value = o[key];
|
|
645
|
+
|
|
646
|
+
if (key === "label" || key === "name") {
|
|
647
|
+
// You can use the 'label' key instead of 'name'; this is a legacy feature
|
|
648
|
+
if (typeof value === "string") {
|
|
649
|
+
this.name = value;
|
|
650
|
+
}
|
|
651
|
+
} else if (key !== "children" && key !== "parent") {
|
|
652
|
+
// You can't update the children or the parent using this function
|
|
653
|
+
this[key] = value;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
659
657
|
}
|
|
660
658
|
}
|
|
661
659
|
}
|