tree-element 0.1.1 → 0.3.0
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/README.md +43 -31
- package/lib/animation.d.ts +2 -2
- package/lib/animation.d.ts.map +1 -1
- package/lib/animation.js +16 -12
- package/lib/animation.js.map +1 -1
- package/lib/classNames.d.ts.map +1 -1
- package/lib/classNames.js +2 -2
- package/lib/classNames.js.map +1 -1
- package/lib/dataLoader.d.ts +1 -0
- package/lib/dataLoader.d.ts.map +1 -1
- package/lib/dataLoader.js +22 -20
- package/lib/dataLoader.js.map +1 -1
- package/lib/dragAndDropHandler/binarySearch.js +3 -3
- package/lib/dragAndDropHandler/binarySearch.js.map +1 -1
- package/lib/dragAndDropHandler/dragElement.d.ts.map +1 -1
- package/lib/dragAndDropHandler/dragElement.js +11 -13
- package/lib/dragAndDropHandler/dragElement.js.map +1 -1
- package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -1
- package/lib/dragAndDropHandler/generateHitAreas.js +25 -25
- package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -1
- package/lib/dragAndDropHandler/index.d.ts.map +1 -1
- package/lib/dragAndDropHandler/index.js +125 -158
- package/lib/dragAndDropHandler/index.js.map +1 -1
- package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts.map +1 -1
- package/lib/dragAndDropHandler/iterateVisibleNodes.js +23 -22
- package/lib/dragAndDropHandler/iterateVisibleNodes.js.map +1 -1
- package/lib/dragAndDropHandler/types.d.ts.map +1 -1
- package/lib/elementsRenderer.d.ts +20 -7
- package/lib/elementsRenderer.d.ts.map +1 -1
- package/lib/elementsRenderer.js +195 -136
- package/lib/elementsRenderer.js.map +1 -1
- package/lib/events.d.ts +44 -11
- package/lib/events.d.ts.map +1 -1
- package/lib/index.d.ts +36 -13
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +386 -349
- package/lib/index.js.map +1 -1
- package/lib/keyHandler.d.ts +13 -8
- package/lib/keyHandler.d.ts.map +1 -1
- package/lib/keyHandler.js +106 -51
- package/lib/keyHandler.js.map +1 -1
- package/lib/methodTypes.d.ts +3 -3
- package/lib/methodTypes.d.ts.map +1 -1
- package/lib/mouseHandler.d.ts +1 -1
- package/lib/mouseHandler.d.ts.map +1 -1
- package/lib/mouseHandler.js +36 -49
- package/lib/mouseHandler.js.map +1 -1
- package/lib/mouseUtils.d.ts.map +1 -1
- package/lib/mouseUtils.js +2 -2
- package/lib/mouseUtils.js.map +1 -1
- package/lib/node.d.ts +47 -16
- package/lib/node.d.ts.map +1 -1
- package/lib/node.js +169 -171
- package/lib/node.js.map +1 -1
- package/lib/nodeElement/borderDropHint.d.ts.map +1 -1
- package/lib/nodeElement/borderDropHint.js +4 -5
- package/lib/nodeElement/borderDropHint.js.map +1 -1
- package/lib/nodeElement/folderElement.d.ts +8 -5
- package/lib/nodeElement/folderElement.d.ts.map +1 -1
- package/lib/nodeElement/folderElement.js +75 -67
- package/lib/nodeElement/folderElement.js.map +1 -1
- package/lib/nodeElement/ghostDropHint.d.ts.map +1 -1
- package/lib/nodeElement/ghostDropHint.js +13 -13
- package/lib/nodeElement/ghostDropHint.js.map +1 -1
- package/lib/nodeElement/index.d.ts +2 -1
- package/lib/nodeElement/index.d.ts.map +1 -1
- package/lib/nodeElement/index.js +38 -30
- package/lib/nodeElement/index.js.map +1 -1
- package/lib/nodeUtils.d.ts.map +1 -1
- package/lib/nodeUtils.js +1 -1
- package/lib/nodeUtils.js.map +1 -1
- package/lib/options.d.ts +19 -3
- package/lib/options.d.ts.map +1 -1
- package/lib/positionUtils.js +3 -3
- package/lib/positionUtils.js.map +1 -1
- package/lib/requestUrl.d.ts +1 -1
- package/lib/requestUrl.d.ts.map +1 -1
- package/lib/requestUrl.js +6 -17
- package/lib/requestUrl.js.map +1 -1
- package/lib/saveStateHandler.d.ts.map +1 -1
- package/lib/saveStateHandler.js +59 -89
- package/lib/saveStateHandler.js.map +1 -1
- package/lib/scrollHandler/containerScrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/containerScrollParent.js +11 -18
- package/lib/scrollHandler/containerScrollParent.js.map +1 -1
- package/lib/scrollHandler/createScrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/createScrollParent.js +12 -13
- package/lib/scrollHandler/createScrollParent.js.map +1 -1
- package/lib/scrollHandler/documentScrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/documentScrollParent.js +18 -27
- package/lib/scrollHandler/documentScrollParent.js.map +1 -1
- package/lib/scrollHandler/scrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/scrollParent.js +30 -34
- package/lib/scrollHandler/scrollParent.js.map +1 -1
- package/lib/scrollHandler.d.ts.map +1 -1
- package/lib/scrollHandler.js +11 -14
- package/lib/scrollHandler.js.map +1 -1
- package/lib/selectNodeHandler.d.ts +4 -2
- package/lib/selectNodeHandler.d.ts.map +1 -1
- package/lib/selectNodeHandler.js +64 -83
- package/lib/selectNodeHandler.js.map +1 -1
- package/lib/setDefaultOptions.d.ts.map +1 -1
- package/lib/setDefaultOptions.js +34 -38
- package/lib/setDefaultOptions.js.map +1 -1
- package/lib/triggerCustomEvent.d.ts.map +1 -1
- package/lib/triggerCustomEvent.js +2 -2
- package/lib/triggerCustomEvent.js.map +1 -1
- package/lib/util.d.ts.map +1 -1
- package/lib/util.js +2 -2
- package/lib/util.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +43 -32
- package/src/animation.ts +14 -12
- package/src/classNames.ts +52 -52
- package/src/dataLoader.ts +79 -71
- package/src/dragAndDropHandler/binarySearch.ts +17 -17
- package/src/dragAndDropHandler/dragElement.ts +51 -50
- package/src/dragAndDropHandler/generateHitAreas.ts +156 -161
- package/src/dragAndDropHandler/index.ts +334 -348
- package/src/dragAndDropHandler/iterateVisibleNodes.ts +61 -63
- package/src/dragAndDropHandler/types.ts +5 -5
- package/src/elementsRenderer.ts +377 -294
- package/src/events.ts +241 -208
- package/src/header.txt +1 -1
- package/src/index.ts +259 -229
- package/src/keyHandler.ts +179 -115
- package/src/methodTypes.ts +7 -10
- package/src/mouseHandler.ts +316 -316
- package/src/mouseUtils.ts +14 -14
- package/src/node.ts +773 -771
- package/src/nodeElement/borderDropHint.ts +25 -25
- package/src/nodeElement/folderElement.ts +116 -102
- package/src/nodeElement/ghostDropHint.ts +64 -62
- package/src/nodeElement/index.ts +81 -73
- package/src/nodeUtils.ts +5 -5
- package/src/options.ts +25 -5
- package/src/requestUrl.ts +7 -14
- package/src/saveStateHandler.ts +191 -207
- package/src/scrollHandler/containerScrollParent.ts +40 -49
- package/src/scrollHandler/createScrollParent.ts +26 -27
- package/src/scrollHandler/documentScrollParent.ts +66 -72
- package/src/scrollHandler/scrollParent.ts +90 -86
- package/src/scrollHandler.ts +45 -45
- package/src/selectNodeHandler.ts +171 -165
- package/src/setDefaultOptions.ts +44 -32
- package/src/triggerCustomEvent.ts +7 -3
- package/src/util.ts +2 -3
- package/src/version.ts +1 -1
- package/tree_element.css +87 -69
- package/tree_element.debug.js +3866 -3831
- package/tree_element.debug.js.map +1 -1
- package/tree_element.js +2 -2
- package/tree_element.js.map +1 -1
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ClassNames } from "./classNames";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TreeEventName, TreeEvents } from "./events";
|
|
3
3
|
import type { PositionInfo } from "./mouseUtils";
|
|
4
4
|
import type { NodeData, NodeId, Position } from "./node";
|
|
5
5
|
import type { TreeElementOptions } from "./options";
|
|
@@ -28,19 +28,6 @@ import __version__ from "./version";
|
|
|
28
28
|
// the submodules directly, because those are not exposed in package.json.
|
|
29
29
|
// Type only, so that the iife build keeps exposing the TreeElement class itself
|
|
30
30
|
// as its global, instead of an object of named exports.
|
|
31
|
-
export type {
|
|
32
|
-
MoveInfo,
|
|
33
|
-
Node,
|
|
34
|
-
NodeData,
|
|
35
|
-
NodeId,
|
|
36
|
-
Position,
|
|
37
|
-
SavedState,
|
|
38
|
-
SelectNodeOptions,
|
|
39
|
-
TreeElementOptions,
|
|
40
|
-
TreeEvent,
|
|
41
|
-
TreeEventName,
|
|
42
|
-
TreeEvents,
|
|
43
|
-
};
|
|
44
31
|
|
|
45
32
|
export type TriggerEventProvider = (
|
|
46
33
|
element: HTMLElement,
|
|
@@ -49,21 +36,21 @@ export type TriggerEventProvider = (
|
|
|
49
36
|
) => boolean;
|
|
50
37
|
|
|
51
38
|
interface TreeElementParams extends Partial<TreeElementOptions> {
|
|
52
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
The element the tree is rendered into. Its content is replaced.
|
|
41
|
+
*/
|
|
53
42
|
htmlElement: HTMLElement;
|
|
54
43
|
/** Replaces how events are dispatched. It must return whether the event
|
|
55
44
|
* was not cancelled. This exists for tests and for integrating with
|
|
56
45
|
* another event system. */
|
|
57
|
-
overrideTriggerEventProvider?: TriggerEventProvider
|
|
46
|
+
overrideTriggerEventProvider?: TriggerEventProvider;
|
|
58
47
|
}
|
|
59
48
|
|
|
60
49
|
export default class TreeElement {
|
|
61
|
-
/** @hidden */
|
|
62
|
-
public tree: Node;
|
|
63
|
-
|
|
64
50
|
private classNames: ClassNames;
|
|
65
51
|
private dataLoader: DataLoader;
|
|
66
52
|
private dndHandler: DragAndDropHandler;
|
|
53
|
+
private fallbackTabStop: HTMLElement | null;
|
|
67
54
|
private htmlElement: HTMLElement;
|
|
68
55
|
private isInitialized: boolean;
|
|
69
56
|
private keyHandler: KeyHandler;
|
|
@@ -74,20 +61,31 @@ export default class TreeElement {
|
|
|
74
61
|
private saveStateHandler: SaveStateHandler;
|
|
75
62
|
private scrollHandler: ScrollHandler;
|
|
76
63
|
private selectNodeHandler: SelectNodeHandler;
|
|
64
|
+
private tree: Node;
|
|
77
65
|
private triggerEventProvider: TriggerEventProvider;
|
|
78
66
|
|
|
79
|
-
/**
|
|
80
|
-
|
|
67
|
+
/**
|
|
68
|
+
@hidden
|
|
69
|
+
*/
|
|
70
|
+
constructor({
|
|
71
|
+
htmlElement,
|
|
72
|
+
overrideTriggerEventProvider,
|
|
73
|
+
...options
|
|
74
|
+
}: TreeElementParams) {
|
|
81
75
|
this.htmlElement = htmlElement;
|
|
82
76
|
this.options = setDefaultOptions(htmlElement, options);
|
|
83
77
|
this.classNames = createClassNames(this.options);
|
|
84
|
-
this.triggerEventProvider =
|
|
78
|
+
this.triggerEventProvider =
|
|
79
|
+
overrideTriggerEventProvider ?? triggerCustomEvent;
|
|
85
80
|
|
|
86
81
|
this.isInitialized = false;
|
|
87
82
|
this.tree = new Node({}, true);
|
|
88
83
|
this.nodeMap = new WeakMap();
|
|
84
|
+
this.fallbackTabStop = null;
|
|
89
85
|
|
|
90
86
|
const {
|
|
87
|
+
ariaLabel,
|
|
88
|
+
ariaLabelledBy,
|
|
91
89
|
autoEscape,
|
|
92
90
|
buttonLeft,
|
|
93
91
|
closedIcon,
|
|
@@ -115,10 +113,10 @@ export default class TreeElement {
|
|
|
115
113
|
const closeNode = this.closeNode.bind(this);
|
|
116
114
|
const getNodeElement = this.getNodeElement.bind(this);
|
|
117
115
|
const getNodeElementForNode = this.getNodeElementForNode.bind(this);
|
|
116
|
+
const getFocusedNode = this.getFocusedNode.bind(this);
|
|
118
117
|
const getNodeById = this.getNodeById.bind(this);
|
|
119
118
|
const getSelectedNode = this.getSelectedNode.bind(this);
|
|
120
119
|
const getTree = this.getTree.bind(this);
|
|
121
|
-
const isFocusOnTree = this.isFocusOnTree.bind(this);
|
|
122
120
|
const loadData = this.loadData.bind(this);
|
|
123
121
|
const openNode = this.openNode.bind(this);
|
|
124
122
|
const openParents = this.openParents.bind(this);
|
|
@@ -127,10 +125,11 @@ export default class TreeElement {
|
|
|
127
125
|
const setNodeElement = this.setNodeElement.bind(this);
|
|
128
126
|
const treeElement = this.htmlElement;
|
|
129
127
|
const triggerEvent = this.triggerEvent.bind(this);
|
|
128
|
+
const updateTabStop = this.updateTabStop.bind(this);
|
|
130
129
|
|
|
131
130
|
const saveState = () => {
|
|
132
131
|
saveStateHandler.saveState();
|
|
133
|
-
}
|
|
132
|
+
};
|
|
134
133
|
|
|
135
134
|
const selectNodeHandler = new SelectNodeHandler({
|
|
136
135
|
getNodeById,
|
|
@@ -139,7 +138,8 @@ export default class TreeElement {
|
|
|
139
138
|
getSelectable: () => this.options.selectable,
|
|
140
139
|
openParents,
|
|
141
140
|
saveState,
|
|
142
|
-
triggerEvent
|
|
141
|
+
triggerEvent,
|
|
142
|
+
updateTabStop,
|
|
143
143
|
});
|
|
144
144
|
|
|
145
145
|
const addToSelection =
|
|
@@ -150,7 +150,8 @@ export default class TreeElement {
|
|
|
150
150
|
selectNodeHandler.isNodeSelected.bind(selectNodeHandler);
|
|
151
151
|
const removeFromSelection =
|
|
152
152
|
selectNodeHandler.removeFromSelection.bind(selectNodeHandler);
|
|
153
|
-
const selectNode =
|
|
153
|
+
const selectNode =
|
|
154
|
+
selectNodeHandler.selectSingleNode.bind(selectNodeHandler);
|
|
154
155
|
|
|
155
156
|
const getMouseDelay = () => this.options.startDndDelay ?? 0;
|
|
156
157
|
|
|
@@ -204,14 +205,18 @@ export default class TreeElement {
|
|
|
204
205
|
|
|
205
206
|
const keyHandler = new KeyHandler({
|
|
206
207
|
closeNode,
|
|
208
|
+
getFocusedNode,
|
|
207
209
|
getSelectedNode,
|
|
208
|
-
|
|
210
|
+
getTree,
|
|
209
211
|
keyboardSupport,
|
|
210
212
|
openNode,
|
|
211
213
|
selectNode,
|
|
214
|
+
triggerEvent,
|
|
212
215
|
});
|
|
213
216
|
|
|
214
217
|
const renderer = new ElementsRenderer({
|
|
218
|
+
ariaLabel,
|
|
219
|
+
ariaLabelledBy,
|
|
215
220
|
autoEscape,
|
|
216
221
|
buttonLeft,
|
|
217
222
|
classNames,
|
|
@@ -267,10 +272,7 @@ export default class TreeElement {
|
|
|
267
272
|
* @returns The new node, or `null` when the node has no parent.
|
|
268
273
|
* @group Changing the tree
|
|
269
274
|
*/
|
|
270
|
-
public addNodeAfter(
|
|
271
|
-
nodeData: NodeData,
|
|
272
|
-
existingNode: Node,
|
|
273
|
-
): Node | null {
|
|
275
|
+
public addNodeAfter(nodeData: NodeData, existingNode: Node): Node | null {
|
|
274
276
|
const newNode = existingNode.addAfter(nodeData);
|
|
275
277
|
|
|
276
278
|
if (newNode) {
|
|
@@ -286,10 +288,7 @@ export default class TreeElement {
|
|
|
286
288
|
* @returns The new node, or `null` when the node has no parent.
|
|
287
289
|
* @group Changing the tree
|
|
288
290
|
*/
|
|
289
|
-
public addNodeBefore(
|
|
290
|
-
nodeData: NodeData,
|
|
291
|
-
existingNode: Node,
|
|
292
|
-
): Node | null {
|
|
291
|
+
public addNodeBefore(nodeData: NodeData, existingNode: Node): Node | null {
|
|
293
292
|
const newNode = existingNode.addBefore(nodeData);
|
|
294
293
|
|
|
295
294
|
if (newNode) {
|
|
@@ -306,10 +305,7 @@ export default class TreeElement {
|
|
|
306
305
|
* @returns The new node, or `null` when the node has no parent.
|
|
307
306
|
* @group Changing the tree
|
|
308
307
|
*/
|
|
309
|
-
public addParentNode(
|
|
310
|
-
nodeData: NodeData,
|
|
311
|
-
existingNode: Node,
|
|
312
|
-
): Node | null {
|
|
308
|
+
public addParentNode(nodeData: NodeData, existingNode: Node): Node | null {
|
|
313
309
|
const newNode = existingNode.addParent(nodeData);
|
|
314
310
|
|
|
315
311
|
if (newNode) {
|
|
@@ -325,12 +321,13 @@ export default class TreeElement {
|
|
|
325
321
|
* @param mustSetFocus - Move the focus to the node. Default `true`.
|
|
326
322
|
* @group Selection
|
|
327
323
|
*/
|
|
328
|
-
public addToSelection(node: Node, mustSetFocus
|
|
324
|
+
public addToSelection(node: Node, mustSetFocus = true) {
|
|
329
325
|
this.selectNodeHandler.addToSelection(node);
|
|
330
326
|
this.openParents(node);
|
|
331
327
|
|
|
332
|
-
this.getNodeElementForNode(node).select(mustSetFocus
|
|
328
|
+
this.getNodeElementForNode(node).select(mustSetFocus);
|
|
333
329
|
|
|
330
|
+
this.updateTabStop();
|
|
334
331
|
this.saveState();
|
|
335
332
|
}
|
|
336
333
|
|
|
@@ -354,20 +351,29 @@ export default class TreeElement {
|
|
|
354
351
|
}
|
|
355
352
|
|
|
356
353
|
/**
|
|
357
|
-
* Closes a folder.
|
|
354
|
+
* Closes a folder. Await the promise when you need to know the closing
|
|
355
|
+
* animation has finished.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```js
|
|
359
|
+
* await tree.closeNode(node);
|
|
360
|
+
* console.log("closed");
|
|
361
|
+
* ```
|
|
358
362
|
*
|
|
359
363
|
* @param slide - Override the `slide` option for this call.
|
|
360
364
|
* @group Opening and closing
|
|
361
365
|
*/
|
|
362
|
-
public closeNode(node: Node, slide?: boolean): void {
|
|
363
|
-
if (node.isFolder() || node.isEmptyFolder) {
|
|
364
|
-
|
|
365
|
-
slide ?? this.options.slide,
|
|
366
|
-
this.options.animationSpeed,
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
this.saveState();
|
|
366
|
+
public async closeNode(node: Node, slide?: boolean): Promise<void> {
|
|
367
|
+
if (!(node.isFolder() || node.isEmptyFolder)) {
|
|
368
|
+
return;
|
|
370
369
|
}
|
|
370
|
+
|
|
371
|
+
await this.createFolderElement(node).close(
|
|
372
|
+
slide ?? this.options.slide,
|
|
373
|
+
this.options.animationSpeed,
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
this.saveState();
|
|
371
377
|
}
|
|
372
378
|
|
|
373
379
|
/**
|
|
@@ -377,7 +383,7 @@ export default class TreeElement {
|
|
|
377
383
|
* @group Other
|
|
378
384
|
*/
|
|
379
385
|
public deinit(): void {
|
|
380
|
-
this.htmlElement.textContent =
|
|
386
|
+
this.htmlElement.textContent = "";
|
|
381
387
|
|
|
382
388
|
this.dataLoader.deinit();
|
|
383
389
|
this.keyHandler.deinit();
|
|
@@ -396,11 +402,7 @@ export default class TreeElement {
|
|
|
396
402
|
`li.${this.classNames.common}`,
|
|
397
403
|
);
|
|
398
404
|
|
|
399
|
-
|
|
400
|
-
return this.nodeMap.get(liElement) ?? null;
|
|
401
|
-
} else {
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
405
|
+
return liElement ? (this.nodeMap.get(liElement) ?? null) : null;
|
|
404
406
|
}
|
|
405
407
|
|
|
406
408
|
/**
|
|
@@ -465,11 +467,11 @@ export default class TreeElement {
|
|
|
465
467
|
}
|
|
466
468
|
|
|
467
469
|
/**
|
|
468
|
-
* Returns the selected node, or `
|
|
470
|
+
* Returns the selected node, or `null` when nothing is selected.
|
|
469
471
|
*
|
|
470
472
|
* @group Selection
|
|
471
473
|
*/
|
|
472
|
-
public getSelectedNode():
|
|
474
|
+
public getSelectedNode(): Node | null {
|
|
473
475
|
return this.selectNodeHandler.getSelectedNode();
|
|
474
476
|
}
|
|
475
477
|
|
|
@@ -571,11 +573,10 @@ export default class TreeElement {
|
|
|
571
573
|
* @param url - Defaults to the `dataUrl` option.
|
|
572
574
|
* @group Loading data
|
|
573
575
|
*/
|
|
574
|
-
public async loadDataFromUrl(
|
|
575
|
-
url
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const requestUrl = url ? new RequestUrl(url) : this.createRequestUrl(parentNode);
|
|
576
|
+
public async loadDataFromUrl(url?: string, parentNode?: Node): Promise<void> {
|
|
577
|
+
const requestUrl = url
|
|
578
|
+
? new RequestUrl(url)
|
|
579
|
+
: this.createRequestUrl(parentNode);
|
|
579
580
|
|
|
580
581
|
if (requestUrl) {
|
|
581
582
|
await this.dataLoader.loadFromUrl(requestUrl, parentNode);
|
|
@@ -588,10 +589,7 @@ export default class TreeElement {
|
|
|
588
589
|
* @group Selection
|
|
589
590
|
*/
|
|
590
591
|
public moveDown() {
|
|
591
|
-
|
|
592
|
-
if (selectedNode) {
|
|
593
|
-
this.keyHandler.moveDown(selectedNode);
|
|
594
|
-
}
|
|
592
|
+
this.keyHandler.moveDown();
|
|
595
593
|
}
|
|
596
594
|
|
|
597
595
|
/**
|
|
@@ -605,11 +603,7 @@ export default class TreeElement {
|
|
|
605
603
|
* @param position - `"before"`, `"after"` or `"inside"`.
|
|
606
604
|
* @group Changing the tree
|
|
607
605
|
*/
|
|
608
|
-
public moveNode(
|
|
609
|
-
node: Node,
|
|
610
|
-
targetNode: Node,
|
|
611
|
-
position: Position,
|
|
612
|
-
): void {
|
|
606
|
+
public moveNode(node: Node, targetNode: Node, position: Position): void {
|
|
613
607
|
this.tree.moveNode(node, targetNode, position);
|
|
614
608
|
this.refreshElements(null);
|
|
615
609
|
}
|
|
@@ -620,10 +614,7 @@ export default class TreeElement {
|
|
|
620
614
|
* @group Selection
|
|
621
615
|
*/
|
|
622
616
|
public moveUp() {
|
|
623
|
-
|
|
624
|
-
if (selectedNode) {
|
|
625
|
-
this.keyHandler.moveUp(selectedNode);
|
|
626
|
-
}
|
|
617
|
+
this.keyHandler.moveUp();
|
|
627
618
|
}
|
|
628
619
|
|
|
629
620
|
/**
|
|
@@ -640,15 +631,12 @@ export default class TreeElement {
|
|
|
640
631
|
* @param slide - Override the `slide` option for this call.
|
|
641
632
|
* @group Opening and closing
|
|
642
633
|
*/
|
|
643
|
-
public async openNode(
|
|
644
|
-
node: Node,
|
|
645
|
-
slide?: boolean
|
|
646
|
-
): Promise<void> {
|
|
634
|
+
public async openNode(node: Node, slide?: boolean): Promise<void> {
|
|
647
635
|
const mustSlide = slide ?? this.options.slide;
|
|
648
636
|
|
|
649
637
|
const doOpenNode = async (
|
|
650
638
|
openedNode: Node,
|
|
651
|
-
slideOption: boolean
|
|
639
|
+
slideOption: boolean,
|
|
652
640
|
): Promise<void> => {
|
|
653
641
|
if (!node.children.length) {
|
|
654
642
|
return;
|
|
@@ -656,10 +644,7 @@ export default class TreeElement {
|
|
|
656
644
|
|
|
657
645
|
const folderElement = this.createFolderElement(openedNode);
|
|
658
646
|
|
|
659
|
-
await folderElement.open(
|
|
660
|
-
slideOption,
|
|
661
|
-
this.options.animationSpeed,
|
|
662
|
-
);
|
|
647
|
+
await folderElement.open(slideOption, this.options.animationSpeed);
|
|
663
648
|
};
|
|
664
649
|
|
|
665
650
|
if (node.isFolder() || node.isEmptyFolder) {
|
|
@@ -726,6 +711,7 @@ export default class TreeElement {
|
|
|
726
711
|
this.selectNodeHandler.removeFromSelection(node);
|
|
727
712
|
|
|
728
713
|
this.getNodeElementForNode(node).deselect();
|
|
714
|
+
this.updateTabStop();
|
|
729
715
|
this.saveState();
|
|
730
716
|
}
|
|
731
717
|
|
|
@@ -752,9 +738,7 @@ export default class TreeElement {
|
|
|
752
738
|
return;
|
|
753
739
|
}
|
|
754
740
|
|
|
755
|
-
const top =
|
|
756
|
-
getOffsetTop(node.element) -
|
|
757
|
-
getOffsetTop(this.htmlElement);
|
|
741
|
+
const top = getOffsetTop(node.element) - getOffsetTop(this.htmlElement);
|
|
758
742
|
|
|
759
743
|
this.scrollHandler.scrollToY(top);
|
|
760
744
|
}
|
|
@@ -768,10 +752,7 @@ export default class TreeElement {
|
|
|
768
752
|
* `false`.
|
|
769
753
|
* @group Selection
|
|
770
754
|
*/
|
|
771
|
-
public selectNode(
|
|
772
|
-
node: Node | null,
|
|
773
|
-
options?: SelectNodeOptions,
|
|
774
|
-
): void {
|
|
755
|
+
public selectNode(node: Node | null, options?: SelectNodeOptions): void {
|
|
775
756
|
if (!node) {
|
|
776
757
|
// Called with empty node -> deselect current node
|
|
777
758
|
this.deselectCurrentNode();
|
|
@@ -779,7 +760,11 @@ export default class TreeElement {
|
|
|
779
760
|
return;
|
|
780
761
|
}
|
|
781
762
|
|
|
782
|
-
|
|
763
|
+
// Unlike a click, selecting the selected node again keeps it selected
|
|
764
|
+
this.selectNodeHandler.selectSingleNode(node, {
|
|
765
|
+
mustToggle: false,
|
|
766
|
+
...options,
|
|
767
|
+
});
|
|
783
768
|
}
|
|
784
769
|
|
|
785
770
|
/**
|
|
@@ -793,28 +778,32 @@ export default class TreeElement {
|
|
|
793
778
|
}
|
|
794
779
|
|
|
795
780
|
/**
|
|
796
|
-
* Applies a state to the tree
|
|
781
|
+
* Applies a state to the tree, and saves it to storage when `saveState` is
|
|
782
|
+
* enabled.
|
|
797
783
|
*
|
|
798
784
|
* @group State
|
|
799
785
|
*/
|
|
800
786
|
public setState(state: SavedState) {
|
|
801
787
|
this.saveStateHandler.setInitialState(state);
|
|
802
788
|
this.refreshElements(null);
|
|
789
|
+
this.saveState();
|
|
803
790
|
}
|
|
804
791
|
|
|
805
792
|
/**
|
|
806
|
-
* Closes an open node and opens a closed one.
|
|
793
|
+
* Closes an open node and opens a closed one. Await the promise when you
|
|
794
|
+
* need to know the node is really open or closed, like with `openNode` and
|
|
795
|
+
* `closeNode`.
|
|
807
796
|
*
|
|
808
797
|
* @param slide - Override the `slide` option for this call.
|
|
809
798
|
* @group Opening and closing
|
|
810
799
|
*/
|
|
811
|
-
public toggle(node: Node, slide: boolean | null = null) {
|
|
800
|
+
public async toggle(node: Node, slide: boolean | null = null): Promise<void> {
|
|
812
801
|
const mustSlide = slide ?? this.options.slide;
|
|
813
802
|
|
|
814
803
|
if (node.is_open) {
|
|
815
|
-
this.closeNode(node, mustSlide);
|
|
804
|
+
await this.closeNode(node, mustSlide);
|
|
816
805
|
} else {
|
|
817
|
-
|
|
806
|
+
await this.openNode(node, mustSlide);
|
|
818
807
|
}
|
|
819
808
|
}
|
|
820
809
|
|
|
@@ -860,7 +849,7 @@ export default class TreeElement {
|
|
|
860
849
|
if (
|
|
861
850
|
typeof data === "object" &&
|
|
862
851
|
data.children &&
|
|
863
|
-
data.children
|
|
852
|
+
Array.isArray(data.children)
|
|
864
853
|
) {
|
|
865
854
|
node.removeChildren();
|
|
866
855
|
|
|
@@ -872,6 +861,34 @@ export default class TreeElement {
|
|
|
872
861
|
this.refreshElements(node);
|
|
873
862
|
}
|
|
874
863
|
|
|
864
|
+
// Open the nodes up to the autoOpen level. A folder that is loaded on
|
|
865
|
+
// demand is fetched first, and its children are opened when the data has
|
|
866
|
+
// arrived.
|
|
867
|
+
private async autoOpenNodesOnDemand(): Promise<void> {
|
|
868
|
+
const maxLevel = this.getAutoOpenMaxLevel();
|
|
869
|
+
|
|
870
|
+
const openNodes = async (): Promise<void> => {
|
|
871
|
+
const loading: Promise<void>[] = [];
|
|
872
|
+
|
|
873
|
+
this.tree.iterate((node: Node, level: number) => {
|
|
874
|
+
if (node.load_on_demand) {
|
|
875
|
+
if (!node.is_loading) {
|
|
876
|
+
loading.push(this.openNode(node, false).then(openNodes));
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
void this.openNode(node, false);
|
|
882
|
+
|
|
883
|
+
return level !== maxLevel;
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
await Promise.all(loading);
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
await openNodes();
|
|
890
|
+
}
|
|
891
|
+
|
|
875
892
|
private createFolderElement(node: Node) {
|
|
876
893
|
const classNames = this.classNames;
|
|
877
894
|
const closedIconElement = this.renderer.closedIconElement;
|
|
@@ -883,12 +900,22 @@ export default class TreeElement {
|
|
|
883
900
|
const treeElement = this.htmlElement;
|
|
884
901
|
const triggerEvent = this.triggerEvent.bind(this);
|
|
885
902
|
|
|
903
|
+
// A selected node in a closed folder is rendered when the folder is
|
|
904
|
+
// opened, which can be after it was selected. It becomes the tab stop
|
|
905
|
+
// then, so the fallback tab stop must go.
|
|
906
|
+
const renderChildren = (folder: Node) => {
|
|
907
|
+
const ul = this.renderer.renderChildren(folder);
|
|
908
|
+
this.updateTabStop();
|
|
909
|
+
return ul;
|
|
910
|
+
};
|
|
911
|
+
|
|
886
912
|
return new FolderElement({
|
|
887
913
|
classNames,
|
|
888
914
|
closedIconElement,
|
|
889
915
|
getScrollLeft,
|
|
890
916
|
node,
|
|
891
917
|
openedIconElement,
|
|
918
|
+
renderChildren,
|
|
892
919
|
tabIndex,
|
|
893
920
|
treeElement,
|
|
894
921
|
triggerEvent,
|
|
@@ -913,19 +940,13 @@ export default class TreeElement {
|
|
|
913
940
|
}
|
|
914
941
|
|
|
915
942
|
/* Create a RequestUrl based on the url in the options.
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
943
|
+
* Add a 'node' query parameter for loading on demand
|
|
944
|
+
* Add a 'selected_node' query parameter if a node is selected.
|
|
945
|
+
*/
|
|
919
946
|
private createRequestUrl(node?: Node): null | RequestUrl {
|
|
920
947
|
const dataUrl = this.options.dataUrl;
|
|
921
948
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
if (typeof dataUrl === "function") {
|
|
925
|
-
url = dataUrl(node);
|
|
926
|
-
} else {
|
|
927
|
-
url = dataUrl;
|
|
928
|
-
}
|
|
949
|
+
const url = typeof dataUrl === "function" ? dataUrl(node) : dataUrl;
|
|
929
950
|
|
|
930
951
|
if (!url) {
|
|
931
952
|
return null;
|
|
@@ -935,12 +956,12 @@ export default class TreeElement {
|
|
|
935
956
|
|
|
936
957
|
if (node?.id) {
|
|
937
958
|
// Load on demand of a subtree; add node parameter
|
|
938
|
-
requestUrl.setSearchParam(
|
|
959
|
+
requestUrl.setSearchParam("node", node.id.toString());
|
|
939
960
|
} else {
|
|
940
961
|
// Add selected_node parameter
|
|
941
962
|
const selectedNodeId = this.getNodeIdToBeSelected();
|
|
942
963
|
if (selectedNodeId) {
|
|
943
|
-
requestUrl.setSearchParam(
|
|
964
|
+
requestUrl.setSearchParam("selected_node", selectedNodeId.toString());
|
|
944
965
|
}
|
|
945
966
|
}
|
|
946
967
|
|
|
@@ -967,9 +988,11 @@ export default class TreeElement {
|
|
|
967
988
|
private getAutoOpenMaxLevel(): number {
|
|
968
989
|
if (this.options.autoOpen === true) {
|
|
969
990
|
return -1;
|
|
970
|
-
}
|
|
991
|
+
}
|
|
992
|
+
if (typeof this.options.autoOpen === "number") {
|
|
971
993
|
return this.options.autoOpen;
|
|
972
|
-
}
|
|
994
|
+
}
|
|
995
|
+
if (typeof this.options.autoOpen === "string") {
|
|
973
996
|
return parseInt(this.options.autoOpen, 10);
|
|
974
997
|
}
|
|
975
998
|
|
|
@@ -977,27 +1000,55 @@ export default class TreeElement {
|
|
|
977
1000
|
return 0;
|
|
978
1001
|
}
|
|
979
1002
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1003
|
+
// The title span of the first node, or null when the tree is empty
|
|
1004
|
+
private getFirstTitleSpan(): HTMLElement | null {
|
|
1005
|
+
const firstElement = this.tree.children[0]?.element;
|
|
1006
|
+
|
|
1007
|
+
return firstElement
|
|
1008
|
+
? firstElement.querySelector<HTMLElement>(
|
|
1009
|
+
`:scope > .${this.classNames.element} > span.${this.classNames.title}`,
|
|
1010
|
+
)
|
|
1011
|
+
: null;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// The node of the HTML element that has the focus, if it is in this tree
|
|
1015
|
+
private getFocusedNode(): Node | null {
|
|
1016
|
+
const activeElement = document.activeElement;
|
|
1017
|
+
|
|
1018
|
+
/* istanbul ignore if */
|
|
1019
|
+
if (!activeElement) {
|
|
1020
|
+
return null;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// The keyboard must still work for input elements.
|
|
1024
|
+
const tagName = activeElement.tagName;
|
|
1025
|
+
if (tagName !== "A" && tagName !== "SPAN") {
|
|
985
1026
|
return null;
|
|
986
1027
|
}
|
|
1028
|
+
|
|
1029
|
+
const node = this.getNode(activeElement as HTMLElement);
|
|
1030
|
+
return node?.tree === this.tree ? node : null;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
private getNodeElement(element: HTMLElement): NodeElement | null {
|
|
1034
|
+
const node = this.getNode(element);
|
|
1035
|
+
return node ? this.getNodeElementForNode(node) : null;
|
|
987
1036
|
}
|
|
988
1037
|
|
|
989
1038
|
private getNodeElementForNode(node: Node): NodeElement {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
return this.createNodeElement(node);
|
|
994
|
-
}
|
|
1039
|
+
return node.isFolder()
|
|
1040
|
+
? this.createFolderElement(node)
|
|
1041
|
+
: this.createNodeElement(node);
|
|
995
1042
|
}
|
|
996
1043
|
|
|
997
1044
|
private getNodeIdToBeSelected(): NodeId | null {
|
|
998
1045
|
return this.saveStateHandler.getNodeIdToBeSelected();
|
|
999
1046
|
}
|
|
1000
1047
|
|
|
1048
|
+
private hasRenderedSelectedNode(): boolean {
|
|
1049
|
+
return this.getSelectedNodes().some((node) => Boolean(node.element));
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1001
1052
|
private initData(): void {
|
|
1002
1053
|
if (this.options.data) {
|
|
1003
1054
|
this.loadData(this.options.data);
|
|
@@ -1014,17 +1065,15 @@ export default class TreeElement {
|
|
|
1014
1065
|
|
|
1015
1066
|
private initTree(data: NodeData[]): void {
|
|
1016
1067
|
const doInit = (): void => {
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
this.triggerEvent("tree.init");
|
|
1068
|
+
if (this.isInitialized) {
|
|
1069
|
+
return;
|
|
1020
1070
|
}
|
|
1071
|
+
|
|
1072
|
+
this.isInitialized = true;
|
|
1073
|
+
this.triggerEvent("tree.init");
|
|
1021
1074
|
};
|
|
1022
1075
|
|
|
1023
|
-
this.tree = new this.options.nodeClass(
|
|
1024
|
-
null,
|
|
1025
|
-
true,
|
|
1026
|
-
this.options.nodeClass,
|
|
1027
|
-
);
|
|
1076
|
+
this.tree = new this.options.nodeClass(null, true, this.options.nodeClass);
|
|
1028
1077
|
|
|
1029
1078
|
this.selectNodeHandler.clear();
|
|
1030
1079
|
|
|
@@ -1044,41 +1093,29 @@ export default class TreeElement {
|
|
|
1044
1093
|
|
|
1045
1094
|
// Does an HTML element of the tree have the focus?
|
|
1046
1095
|
private isFocusOnTree(): boolean {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
/* istanbul ignore if */
|
|
1050
|
-
if (!activeElement) {
|
|
1051
|
-
return false;
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
// The keyboard must still work for input elements.
|
|
1055
|
-
const tagName = activeElement.tagName;
|
|
1056
|
-
if (tagName !== "A" && tagName !== "SPAN") {
|
|
1057
|
-
return false;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
const node = this.getNode(activeElement as HTMLElement);
|
|
1061
|
-
return node?.tree === this.tree;
|
|
1096
|
+
return this.getFocusedNode() !== null;
|
|
1062
1097
|
}
|
|
1063
1098
|
|
|
1064
1099
|
private isSelectedNodeInSubtree(subtree: Node): boolean {
|
|
1065
1100
|
const selectedNode = this.getSelectedNode();
|
|
1066
1101
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
return subtree === selectedNode || subtree.isParentOf(selectedNode);
|
|
1071
|
-
}
|
|
1102
|
+
return selectedNode
|
|
1103
|
+
? subtree === selectedNode || subtree.isParentOf(selectedNode)
|
|
1104
|
+
: false;
|
|
1072
1105
|
}
|
|
1073
1106
|
|
|
1074
|
-
private async loadFolderOnDemand(
|
|
1075
|
-
node: Node,
|
|
1076
|
-
slide: boolean,
|
|
1077
|
-
): Promise<void> {
|
|
1107
|
+
private async loadFolderOnDemand(node: Node, slide: boolean): Promise<void> {
|
|
1078
1108
|
node.is_loading = true;
|
|
1079
1109
|
|
|
1080
1110
|
await this.loadDataFromUrl(undefined, node);
|
|
1081
1111
|
|
|
1112
|
+
// Loading failed, or there is no url. Opening the node now would load it
|
|
1113
|
+
// again, without end. Leave it closed so the user can try again.
|
|
1114
|
+
if (node.load_on_demand) {
|
|
1115
|
+
node.is_loading = false;
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1082
1119
|
await this.openNode(node, slide);
|
|
1083
1120
|
}
|
|
1084
1121
|
|
|
@@ -1092,11 +1129,9 @@ export default class TreeElement {
|
|
|
1092
1129
|
}
|
|
1093
1130
|
|
|
1094
1131
|
private mouseCapture(positionInfo: PositionInfo): boolean | null {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
return this.dndHandler.mouseCapture(positionInfo);
|
|
1132
|
+
return this.options.dragAndDrop
|
|
1133
|
+
? this.dndHandler.mouseCapture(positionInfo)
|
|
1134
|
+
: false;
|
|
1100
1135
|
}
|
|
1101
1136
|
|
|
1102
1137
|
private mouseDrag(positionInfo: PositionInfo): boolean {
|
|
@@ -1112,6 +1147,7 @@ export default class TreeElement {
|
|
|
1112
1147
|
|
|
1113
1148
|
private mouseStart(positionInfo: PositionInfo): boolean {
|
|
1114
1149
|
/* istanbul ignore if */
|
|
1150
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
1115
1151
|
if (!this.options.dragAndDrop) {
|
|
1116
1152
|
return false;
|
|
1117
1153
|
}
|
|
@@ -1153,6 +1189,8 @@ export default class TreeElement {
|
|
|
1153
1189
|
this.selectCurrentNode(mustSetFocus);
|
|
1154
1190
|
}
|
|
1155
1191
|
|
|
1192
|
+
this.updateTabStop();
|
|
1193
|
+
|
|
1156
1194
|
this.triggerEvent("tree.refresh");
|
|
1157
1195
|
}
|
|
1158
1196
|
|
|
@@ -1162,10 +1200,12 @@ export default class TreeElement {
|
|
|
1162
1200
|
|
|
1163
1201
|
private selectCurrentNode(mustSetFocus: boolean): void {
|
|
1164
1202
|
const node = this.getSelectedNode();
|
|
1165
|
-
if (node) {
|
|
1166
|
-
|
|
1167
|
-
nodeElement.select(mustSetFocus);
|
|
1203
|
+
if (!node) {
|
|
1204
|
+
return;
|
|
1168
1205
|
}
|
|
1206
|
+
|
|
1207
|
+
const nodeElement = this.getNodeElementForNode(node);
|
|
1208
|
+
nodeElement.select(mustSetFocus);
|
|
1169
1209
|
}
|
|
1170
1210
|
|
|
1171
1211
|
// Set initial state, either by restoring the state or auto-opening nodes
|
|
@@ -1175,15 +1215,13 @@ export default class TreeElement {
|
|
|
1175
1215
|
// result: is state restored, must load on demand?
|
|
1176
1216
|
const state = this.saveStateHandler.getStateFromStorage();
|
|
1177
1217
|
|
|
1178
|
-
if (
|
|
1179
|
-
|
|
1180
|
-
} else {
|
|
1181
|
-
const mustLoadOnDemand =
|
|
1182
|
-
this.saveStateHandler.setInitialState(state);
|
|
1218
|
+
if (state) {
|
|
1219
|
+
const mustLoadOnDemand = this.saveStateHandler.setInitialState(state);
|
|
1183
1220
|
|
|
1184
1221
|
// return true: the state is restored
|
|
1185
1222
|
return [true, mustLoadOnDemand];
|
|
1186
1223
|
}
|
|
1224
|
+
return [false, false];
|
|
1187
1225
|
};
|
|
1188
1226
|
|
|
1189
1227
|
const autoOpenNodes = (): boolean => {
|
|
@@ -1199,12 +1237,12 @@ export default class TreeElement {
|
|
|
1199
1237
|
if (node.load_on_demand) {
|
|
1200
1238
|
mustLoadOnDemand = true;
|
|
1201
1239
|
return false;
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
} else {
|
|
1240
|
+
}
|
|
1241
|
+
if (node.hasChildren()) {
|
|
1205
1242
|
node.is_open = true;
|
|
1206
1243
|
return level !== maxLevel;
|
|
1207
1244
|
}
|
|
1245
|
+
return false;
|
|
1208
1246
|
});
|
|
1209
1247
|
|
|
1210
1248
|
return mustLoadOnDemand;
|
|
@@ -1221,62 +1259,13 @@ export default class TreeElement {
|
|
|
1221
1259
|
|
|
1222
1260
|
// Set the initial state for nodes that are loaded on demand
|
|
1223
1261
|
private async setInitialStateOnDemand(): Promise<void> {
|
|
1224
|
-
|
|
1225
|
-
const restoreState = (): boolean => {
|
|
1226
|
-
const state = this.saveStateHandler.getStateFromStorage();
|
|
1227
|
-
|
|
1228
|
-
if (!state) {
|
|
1229
|
-
return false;
|
|
1230
|
-
} else {
|
|
1231
|
-
void this.saveStateHandler.setInitialStateOnDemand(
|
|
1232
|
-
state,
|
|
1233
|
-
).then(() => {
|
|
1234
|
-
resolve();
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
return true;
|
|
1238
|
-
}
|
|
1239
|
-
};
|
|
1240
|
-
|
|
1241
|
-
const autoOpenNodes = (): void => {
|
|
1242
|
-
const maxLevel = this.getAutoOpenMaxLevel();
|
|
1243
|
-
let loadingCount = 0;
|
|
1244
|
-
|
|
1245
|
-
const loadAndOpenNode = (node: Node): void => {
|
|
1246
|
-
loadingCount += 1;
|
|
1247
|
-
void this.openNode(node, false).then(() => {
|
|
1248
|
-
loadingCount -= 1;
|
|
1249
|
-
openNodes();
|
|
1250
|
-
});
|
|
1251
|
-
};
|
|
1252
|
-
|
|
1253
|
-
const openNodes = (): void => {
|
|
1254
|
-
this.tree.iterate((node: Node, level: number) => {
|
|
1255
|
-
if (node.load_on_demand) {
|
|
1256
|
-
if (!node.is_loading) {
|
|
1257
|
-
loadAndOpenNode(node);
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
return false;
|
|
1261
|
-
} else {
|
|
1262
|
-
void this.openNode(node, false);
|
|
1263
|
-
|
|
1264
|
-
return level !== maxLevel;
|
|
1265
|
-
}
|
|
1266
|
-
});
|
|
1267
|
-
|
|
1268
|
-
if (loadingCount === 0) {
|
|
1269
|
-
resolve();
|
|
1270
|
-
}
|
|
1271
|
-
};
|
|
1272
|
-
|
|
1273
|
-
openNodes();
|
|
1274
|
-
};
|
|
1262
|
+
const state = this.saveStateHandler.getStateFromStorage();
|
|
1275
1263
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1264
|
+
if (state) {
|
|
1265
|
+
await this.saveStateHandler.setInitialStateOnDemand(state);
|
|
1266
|
+
} else {
|
|
1267
|
+
await this.autoOpenNodesOnDemand();
|
|
1268
|
+
}
|
|
1280
1269
|
}
|
|
1281
1270
|
|
|
1282
1271
|
// Set this HTML element to this node in the node map.
|
|
@@ -1284,7 +1273,48 @@ export default class TreeElement {
|
|
|
1284
1273
|
this.nodeMap.set(element, node);
|
|
1285
1274
|
}
|
|
1286
1275
|
|
|
1287
|
-
private triggerEvent<Name extends TreeEventName>(
|
|
1288
|
-
|
|
1276
|
+
private triggerEvent<Name extends TreeEventName>(
|
|
1277
|
+
eventName: Name,
|
|
1278
|
+
values?: TreeEvents[Name],
|
|
1279
|
+
): boolean {
|
|
1280
|
+
return this.triggerEventProvider(this.htmlElement, eventName, values);
|
|
1289
1281
|
}
|
|
1290
|
-
|
|
1282
|
+
|
|
1283
|
+
/* Keyboard users reach the tree through the tab stop. The selected node is
|
|
1284
|
+
* the tab stop, see NodeElement.select. Without a selected node, the first
|
|
1285
|
+
* node is the tab stop, so the tree can always be reached.
|
|
1286
|
+
*/
|
|
1287
|
+
private updateTabStop(): void {
|
|
1288
|
+
const tabIndex = this.options.tabIndex;
|
|
1289
|
+
const previousTabStop = this.fallbackTabStop;
|
|
1290
|
+
const tabStop =
|
|
1291
|
+
tabIndex == null || this.hasRenderedSelectedNode()
|
|
1292
|
+
? null
|
|
1293
|
+
: this.getFirstTitleSpan();
|
|
1294
|
+
|
|
1295
|
+
if (
|
|
1296
|
+
previousTabStop &&
|
|
1297
|
+
previousTabStop !== tabStop &&
|
|
1298
|
+
previousTabStop.getAttribute("aria-selected") !== "true"
|
|
1299
|
+
) {
|
|
1300
|
+
previousTabStop.setAttribute("tabindex", "-1");
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
if (tabStop && tabIndex != null) {
|
|
1304
|
+
tabStop.setAttribute("tabindex", String(tabIndex));
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
this.fallbackTabStop = tabStop;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
export {
|
|
1312
|
+
type MoveInfo,
|
|
1313
|
+
type TreeEvent,
|
|
1314
|
+
type TreeEventName,
|
|
1315
|
+
type TreeEvents,
|
|
1316
|
+
} from "./events";
|
|
1317
|
+
export { type Node, type NodeData, type NodeId, type Position } from "./node";
|
|
1318
|
+
export { type TreeElementOptions } from "./options";
|
|
1319
|
+
export { type SavedState } from "./saveStateHandler";
|
|
1320
|
+
export { type SelectNodeOptions } from "./selectNodeHandler";
|