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/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
|
|
2
|
+
Tree-element 0.3.0
|
|
3
3
|
|
|
4
4
|
Copyright 2026 Marco Braak
|
|
5
5
|
|
|
@@ -41,11 +41,10 @@ import version from './version.js';
|
|
|
41
41
|
// as its global, instead of an object of named exports.
|
|
42
42
|
|
|
43
43
|
class TreeElement {
|
|
44
|
-
/** @hidden */
|
|
45
|
-
tree;
|
|
46
44
|
_classNames;
|
|
47
45
|
_dataLoader;
|
|
48
46
|
_dndHandler;
|
|
47
|
+
_fallbackTabStop;
|
|
49
48
|
_htmlElement;
|
|
50
49
|
_isInitialized;
|
|
51
50
|
_keyHandler;
|
|
@@ -56,9 +55,12 @@ class TreeElement {
|
|
|
56
55
|
_saveStateHandler;
|
|
57
56
|
_scrollHandler;
|
|
58
57
|
_selectNodeHandler;
|
|
58
|
+
_tree;
|
|
59
59
|
_triggerEventProvider;
|
|
60
60
|
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
@hidden
|
|
63
|
+
*/
|
|
62
64
|
constructor({
|
|
63
65
|
htmlElement,
|
|
64
66
|
overrideTriggerEventProvider,
|
|
@@ -69,9 +71,12 @@ class TreeElement {
|
|
|
69
71
|
this._classNames = createClassNames(this._options);
|
|
70
72
|
this._triggerEventProvider = overrideTriggerEventProvider ?? triggerCustomEvent;
|
|
71
73
|
this._isInitialized = false;
|
|
72
|
-
this.
|
|
74
|
+
this._tree = new Node({}, true);
|
|
73
75
|
this._nodeMap = new WeakMap();
|
|
74
|
-
|
|
76
|
+
this._fallbackTabStop = null;
|
|
77
|
+
let {
|
|
78
|
+
ariaLabel,
|
|
79
|
+
ariaLabelledBy,
|
|
75
80
|
autoEscape,
|
|
76
81
|
buttonLeft,
|
|
77
82
|
closedIcon,
|
|
@@ -94,125 +99,131 @@ class TreeElement {
|
|
|
94
99
|
slide,
|
|
95
100
|
tabIndex
|
|
96
101
|
} = this._options;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
let classNames = this._classNames;
|
|
103
|
+
let closeNode = this.closeNode.bind(this);
|
|
104
|
+
let getNodeElement = this._getNodeElement.bind(this);
|
|
105
|
+
let getNodeElementForNode = this._getNodeElementForNode.bind(this);
|
|
106
|
+
let getFocusedNode = this._getFocusedNode.bind(this);
|
|
107
|
+
let getNodeById = this.getNodeById.bind(this);
|
|
108
|
+
let getSelectedNode = this.getSelectedNode.bind(this);
|
|
109
|
+
let getTree = this.getTree.bind(this);
|
|
110
|
+
let loadData = this.loadData.bind(this);
|
|
111
|
+
let openNode = this.openNode.bind(this);
|
|
112
|
+
let openParents = this._openParents.bind(this);
|
|
113
|
+
let refreshElements = this._refreshElements.bind(this);
|
|
114
|
+
let refreshHitAreas = this.refreshHitAreas.bind(this);
|
|
115
|
+
let setNodeElement = this._setNodeElement.bind(this);
|
|
116
|
+
let treeElement = this._htmlElement;
|
|
117
|
+
let triggerEvent = this._triggerEvent.bind(this);
|
|
118
|
+
let updateTabStop = this._updateTabStop.bind(this);
|
|
119
|
+
let saveState = () => {
|
|
120
|
+
saveStateHandler._saveState();
|
|
115
121
|
};
|
|
116
|
-
|
|
117
|
-
getNodeById,
|
|
118
|
-
getNodeElementForNode,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
openParents,
|
|
122
|
-
saveState,
|
|
123
|
-
triggerEvent
|
|
122
|
+
let selectNodeHandler = new SelectNodeHandler({
|
|
123
|
+
_getNodeById: getNodeById,
|
|
124
|
+
_getNodeElementForNode: getNodeElementForNode,
|
|
125
|
+
_getOnCanSelectNode: () => this._options.onCanSelectNode,
|
|
126
|
+
_getSelectable: () => this._options.selectable,
|
|
127
|
+
_openParents: openParents,
|
|
128
|
+
_saveState: saveState,
|
|
129
|
+
_triggerEvent: triggerEvent,
|
|
130
|
+
_updateTabStop: updateTabStop
|
|
124
131
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
classNames,
|
|
133
|
-
dataFilter,
|
|
134
|
-
loadData,
|
|
135
|
-
treeElement,
|
|
136
|
-
triggerEvent
|
|
132
|
+
let addToSelection = selectNodeHandler._addToSelection.bind(selectNodeHandler);
|
|
133
|
+
let getSelectedNodes = selectNodeHandler._getSelectedNodes.bind(selectNodeHandler);
|
|
134
|
+
let isNodeSelected = selectNodeHandler._isNodeSelected.bind(selectNodeHandler);
|
|
135
|
+
let removeFromSelection = selectNodeHandler._removeFromSelection.bind(selectNodeHandler);
|
|
136
|
+
let selectNode = selectNodeHandler._selectSingleNode.bind(selectNodeHandler);
|
|
137
|
+
let getMouseDelay = () => this._options.startDndDelay ?? 0;
|
|
138
|
+
let dataLoader = new DataLoader({
|
|
139
|
+
_classNames: classNames,
|
|
140
|
+
_dataFilter: dataFilter,
|
|
141
|
+
_loadData: loadData,
|
|
142
|
+
_treeElement: treeElement,
|
|
143
|
+
_triggerEvent: triggerEvent
|
|
137
144
|
});
|
|
138
|
-
|
|
139
|
-
addToSelection,
|
|
140
|
-
getNodeById,
|
|
141
|
-
getSelectedNodes,
|
|
142
|
-
getTree,
|
|
143
|
-
onGetStateFromStorage,
|
|
144
|
-
onSetStateFromStorage,
|
|
145
|
-
openNode,
|
|
146
|
-
refreshElements,
|
|
147
|
-
removeFromSelection,
|
|
148
|
-
|
|
145
|
+
let saveStateHandler = new SaveStateHandler({
|
|
146
|
+
_addToSelection: addToSelection,
|
|
147
|
+
_getNodeById: getNodeById,
|
|
148
|
+
_getSelectedNodes: getSelectedNodes,
|
|
149
|
+
_getTree: getTree,
|
|
150
|
+
_onGetStateFromStorage: onGetStateFromStorage,
|
|
151
|
+
_onSetStateFromStorage: onSetStateFromStorage,
|
|
152
|
+
_openNode: openNode,
|
|
153
|
+
_refreshElements: refreshElements,
|
|
154
|
+
_removeFromSelection: removeFromSelection,
|
|
155
|
+
_saveState: saveStateOption
|
|
149
156
|
});
|
|
150
|
-
|
|
151
|
-
refreshHitAreas,
|
|
152
|
-
treeElement
|
|
157
|
+
let scrollHandler = new ScrollHandler({
|
|
158
|
+
_refreshHitAreas: refreshHitAreas,
|
|
159
|
+
_treeElement: treeElement
|
|
153
160
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
autoEscape,
|
|
157
|
-
classNames,
|
|
158
|
-
getNodeElement,
|
|
159
|
-
getNodeElementForNode,
|
|
160
|
-
getScrollLeft,
|
|
161
|
-
getTree,
|
|
162
|
-
onCanMove,
|
|
163
|
-
onCanMoveTo,
|
|
164
|
-
onDragMove,
|
|
165
|
-
onDragStop,
|
|
166
|
-
onIsMoveHandle,
|
|
167
|
-
openFolderDelay,
|
|
168
|
-
openNode,
|
|
169
|
-
refreshElements,
|
|
170
|
-
slide,
|
|
171
|
-
treeElement,
|
|
172
|
-
triggerEvent
|
|
161
|
+
let getScrollLeft = scrollHandler._getScrollLeft.bind(scrollHandler);
|
|
162
|
+
let dndHandler = new DragAndDropHandler({
|
|
163
|
+
_autoEscape: autoEscape,
|
|
164
|
+
_classNames: classNames,
|
|
165
|
+
_getNodeElement: getNodeElement,
|
|
166
|
+
_getNodeElementForNode: getNodeElementForNode,
|
|
167
|
+
_getScrollLeft: getScrollLeft,
|
|
168
|
+
_getTree: getTree,
|
|
169
|
+
_onCanMove: onCanMove,
|
|
170
|
+
_onCanMoveTo: onCanMoveTo,
|
|
171
|
+
_onDragMove: onDragMove,
|
|
172
|
+
_onDragStop: onDragStop,
|
|
173
|
+
_onIsMoveHandle: onIsMoveHandle,
|
|
174
|
+
_openFolderDelay: openFolderDelay,
|
|
175
|
+
_openNode: openNode,
|
|
176
|
+
_refreshElements: refreshElements,
|
|
177
|
+
_slide: slide,
|
|
178
|
+
_treeElement: treeElement,
|
|
179
|
+
_triggerEvent: triggerEvent
|
|
173
180
|
});
|
|
174
|
-
|
|
175
|
-
closeNode,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
let keyHandler = new KeyHandler({
|
|
182
|
+
_closeNode: closeNode,
|
|
183
|
+
_getFocusedNode: getFocusedNode,
|
|
184
|
+
_getSelectedNode: getSelectedNode,
|
|
185
|
+
_getTree: getTree,
|
|
186
|
+
_keyboardSupport: keyboardSupport,
|
|
187
|
+
_openNode: openNode,
|
|
188
|
+
_selectNode: selectNode,
|
|
189
|
+
_triggerEvent: triggerEvent
|
|
181
190
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
191
|
+
let renderer = new ElementsRenderer({
|
|
192
|
+
_ariaLabel: ariaLabel,
|
|
193
|
+
_ariaLabelledBy: ariaLabelledBy,
|
|
194
|
+
_autoEscape: autoEscape,
|
|
195
|
+
_buttonLeft: buttonLeft,
|
|
196
|
+
_classNames: classNames,
|
|
197
|
+
_closedIcon: closedIcon,
|
|
198
|
+
_dragAndDrop: dragAndDrop,
|
|
199
|
+
_getTree: getTree,
|
|
200
|
+
_htmlElement: treeElement,
|
|
201
|
+
_isNodeSelected: isNodeSelected,
|
|
202
|
+
_onCreateLi: onCreateLi,
|
|
203
|
+
_openedIcon: openedIcon,
|
|
204
|
+
_rtl: rtl,
|
|
205
|
+
_setNodeElement: setNodeElement,
|
|
206
|
+
_showEmptyFolder: showEmptyFolder,
|
|
207
|
+
_tabIndex: tabIndex
|
|
197
208
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
classNames,
|
|
205
|
-
|
|
206
|
-
getMouseDelay,
|
|
207
|
-
getNode,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
onMouseCapture,
|
|
211
|
-
onMouseDrag,
|
|
212
|
-
onMouseStart,
|
|
213
|
-
onMouseStop,
|
|
214
|
-
triggerEvent,
|
|
215
|
-
|
|
209
|
+
let getNode = this.getNode.bind(this);
|
|
210
|
+
let onMouseCapture = this._mouseCapture.bind(this);
|
|
211
|
+
let onMouseDrag = this._mouseDrag.bind(this);
|
|
212
|
+
let onMouseStart = this._mouseStart.bind(this);
|
|
213
|
+
let onMouseStop = this._mouseStop.bind(this);
|
|
214
|
+
let mouseHandler = new MouseHandler({
|
|
215
|
+
_classNames: classNames,
|
|
216
|
+
_element: treeElement,
|
|
217
|
+
_getMouseDelay: getMouseDelay,
|
|
218
|
+
_getNode: getNode,
|
|
219
|
+
_onClickButton: this.toggle.bind(this),
|
|
220
|
+
_onClickTitle: selectNode,
|
|
221
|
+
_onMouseCapture: onMouseCapture,
|
|
222
|
+
_onMouseDrag: onMouseDrag,
|
|
223
|
+
_onMouseStart: onMouseStart,
|
|
224
|
+
_onMouseStop: onMouseStop,
|
|
225
|
+
_triggerEvent: triggerEvent,
|
|
226
|
+
_useContextMenu: this._options.useContextMenu
|
|
216
227
|
});
|
|
217
228
|
this._dataLoader = dataLoader;
|
|
218
229
|
this._dndHandler = dndHandler;
|
|
@@ -232,7 +243,7 @@ class TreeElement {
|
|
|
232
243
|
* @group Changing the tree
|
|
233
244
|
*/
|
|
234
245
|
addNodeAfter(nodeData, existingNode) {
|
|
235
|
-
|
|
246
|
+
let newNode = existingNode.addAfter(nodeData);
|
|
236
247
|
if (newNode) {
|
|
237
248
|
this._refreshElements(existingNode.parent);
|
|
238
249
|
}
|
|
@@ -246,7 +257,7 @@ class TreeElement {
|
|
|
246
257
|
* @group Changing the tree
|
|
247
258
|
*/
|
|
248
259
|
addNodeBefore(nodeData, existingNode) {
|
|
249
|
-
|
|
260
|
+
let newNode = existingNode.addBefore(nodeData);
|
|
250
261
|
if (newNode) {
|
|
251
262
|
this._refreshElements(existingNode.parent);
|
|
252
263
|
}
|
|
@@ -261,7 +272,7 @@ class TreeElement {
|
|
|
261
272
|
* @group Changing the tree
|
|
262
273
|
*/
|
|
263
274
|
addParentNode(nodeData, existingNode) {
|
|
264
|
-
|
|
275
|
+
let newNode = existingNode.addParent(nodeData);
|
|
265
276
|
if (newNode) {
|
|
266
277
|
this._refreshElements(newNode.parent);
|
|
267
278
|
}
|
|
@@ -274,10 +285,11 @@ class TreeElement {
|
|
|
274
285
|
* @param mustSetFocus - Move the focus to the node. Default `true`.
|
|
275
286
|
* @group Selection
|
|
276
287
|
*/
|
|
277
|
-
addToSelection(node, mustSetFocus) {
|
|
278
|
-
this._selectNodeHandler.
|
|
288
|
+
addToSelection(node, mustSetFocus = true) {
|
|
289
|
+
this._selectNodeHandler._addToSelection(node);
|
|
279
290
|
this._openParents(node);
|
|
280
|
-
this._getNodeElementForNode(node).
|
|
291
|
+
this._getNodeElementForNode(node)._select(mustSetFocus);
|
|
292
|
+
this._updateTabStop();
|
|
281
293
|
this._saveState();
|
|
282
294
|
}
|
|
283
295
|
|
|
@@ -293,22 +305,30 @@ class TreeElement {
|
|
|
293
305
|
* @group Changing the tree
|
|
294
306
|
*/
|
|
295
307
|
appendNode(nodeData, parentNode) {
|
|
296
|
-
|
|
308
|
+
let node = parentNode.append(nodeData);
|
|
297
309
|
this._refreshElements(parentNode);
|
|
298
310
|
return node;
|
|
299
311
|
}
|
|
300
312
|
|
|
301
313
|
/**
|
|
302
|
-
* Closes a folder.
|
|
314
|
+
* Closes a folder. Await the promise when you need to know the closing
|
|
315
|
+
* animation has finished.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```js
|
|
319
|
+
* await tree.closeNode(node);
|
|
320
|
+
* console.log("closed");
|
|
321
|
+
* ```
|
|
303
322
|
*
|
|
304
323
|
* @param slide - Override the `slide` option for this call.
|
|
305
324
|
* @group Opening and closing
|
|
306
325
|
*/
|
|
307
|
-
closeNode(node, slide) {
|
|
308
|
-
if (node.isFolder() || node.isEmptyFolder) {
|
|
309
|
-
|
|
310
|
-
this._saveState();
|
|
326
|
+
async closeNode(node, slide) {
|
|
327
|
+
if (!(node.isFolder() || node.isEmptyFolder)) {
|
|
328
|
+
return;
|
|
311
329
|
}
|
|
330
|
+
await this._createFolderElement(node)._close(slide ?? this._options.slide, this._options.animationSpeed);
|
|
331
|
+
this._saveState();
|
|
312
332
|
}
|
|
313
333
|
|
|
314
334
|
/**
|
|
@@ -318,11 +338,11 @@ class TreeElement {
|
|
|
318
338
|
* @group Other
|
|
319
339
|
*/
|
|
320
340
|
deinit() {
|
|
321
|
-
this._htmlElement.textContent =
|
|
322
|
-
this._dataLoader.
|
|
323
|
-
this._keyHandler.
|
|
324
|
-
this._mouseHandler.
|
|
325
|
-
this.
|
|
341
|
+
this._htmlElement.textContent = "";
|
|
342
|
+
this._dataLoader._deinit();
|
|
343
|
+
this._keyHandler._deinit();
|
|
344
|
+
this._mouseHandler._deinit();
|
|
345
|
+
this._tree = new Node({}, true);
|
|
326
346
|
}
|
|
327
347
|
|
|
328
348
|
/**
|
|
@@ -331,12 +351,8 @@ class TreeElement {
|
|
|
331
351
|
* @group Finding nodes
|
|
332
352
|
*/
|
|
333
353
|
getNode(element) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
return this._nodeMap.get(liElement) ?? null;
|
|
337
|
-
} else {
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
354
|
+
let liElement = element.closest(`li.${this._classNames.common}`);
|
|
355
|
+
return liElement ? this._nodeMap.get(liElement) ?? null : null;
|
|
340
356
|
}
|
|
341
357
|
|
|
342
358
|
/**
|
|
@@ -350,7 +366,7 @@ class TreeElement {
|
|
|
350
366
|
* @group Finding nodes
|
|
351
367
|
*/
|
|
352
368
|
getNodeByCallback(callback) {
|
|
353
|
-
return this.
|
|
369
|
+
return this._tree.getNodeByCallback(callback);
|
|
354
370
|
}
|
|
355
371
|
|
|
356
372
|
/**
|
|
@@ -364,7 +380,7 @@ class TreeElement {
|
|
|
364
380
|
* @group Finding nodes
|
|
365
381
|
*/
|
|
366
382
|
getNodeById(id) {
|
|
367
|
-
return this.
|
|
383
|
+
return this._tree.getNodeById(id);
|
|
368
384
|
}
|
|
369
385
|
|
|
370
386
|
/**
|
|
@@ -373,7 +389,7 @@ class TreeElement {
|
|
|
373
389
|
* @group Finding nodes
|
|
374
390
|
*/
|
|
375
391
|
getNodeByName(name) {
|
|
376
|
-
return this.
|
|
392
|
+
return this._tree.getNodeByName(name);
|
|
377
393
|
}
|
|
378
394
|
|
|
379
395
|
/**
|
|
@@ -383,7 +399,7 @@ class TreeElement {
|
|
|
383
399
|
* @group Finding nodes
|
|
384
400
|
*/
|
|
385
401
|
getNodeByNameMustExist(name) {
|
|
386
|
-
return this.
|
|
402
|
+
return this._tree.getNodeByNameMustExist(name);
|
|
387
403
|
}
|
|
388
404
|
|
|
389
405
|
/**
|
|
@@ -397,16 +413,16 @@ class TreeElement {
|
|
|
397
413
|
* @group Finding nodes
|
|
398
414
|
*/
|
|
399
415
|
getNodesByProperty(key, value) {
|
|
400
|
-
return this.
|
|
416
|
+
return this._tree.getNodesByProperty(key, value);
|
|
401
417
|
}
|
|
402
418
|
|
|
403
419
|
/**
|
|
404
|
-
* Returns the selected node, or `
|
|
420
|
+
* Returns the selected node, or `null` when nothing is selected.
|
|
405
421
|
*
|
|
406
422
|
* @group Selection
|
|
407
423
|
*/
|
|
408
424
|
getSelectedNode() {
|
|
409
|
-
return this._selectNodeHandler.
|
|
425
|
+
return this._selectNodeHandler._getSelectedNode();
|
|
410
426
|
}
|
|
411
427
|
|
|
412
428
|
/**
|
|
@@ -415,7 +431,7 @@ class TreeElement {
|
|
|
415
431
|
* @group Selection
|
|
416
432
|
*/
|
|
417
433
|
getSelectedNodes() {
|
|
418
|
-
return this._selectNodeHandler.
|
|
434
|
+
return this._selectNodeHandler._getSelectedNodes();
|
|
419
435
|
}
|
|
420
436
|
|
|
421
437
|
/**
|
|
@@ -425,7 +441,7 @@ class TreeElement {
|
|
|
425
441
|
* @group State
|
|
426
442
|
*/
|
|
427
443
|
getState() {
|
|
428
|
-
return this._saveStateHandler.
|
|
444
|
+
return this._saveStateHandler._getState();
|
|
429
445
|
}
|
|
430
446
|
|
|
431
447
|
/**
|
|
@@ -434,7 +450,7 @@ class TreeElement {
|
|
|
434
450
|
* @group State
|
|
435
451
|
*/
|
|
436
452
|
getStateFromStorage() {
|
|
437
|
-
return this._saveStateHandler.
|
|
453
|
+
return this._saveStateHandler._getStateFromStorage();
|
|
438
454
|
}
|
|
439
455
|
|
|
440
456
|
/**
|
|
@@ -444,7 +460,7 @@ class TreeElement {
|
|
|
444
460
|
* @group Finding nodes
|
|
445
461
|
*/
|
|
446
462
|
getTree() {
|
|
447
|
-
return this.
|
|
463
|
+
return this._tree;
|
|
448
464
|
}
|
|
449
465
|
|
|
450
466
|
/**
|
|
@@ -462,7 +478,7 @@ class TreeElement {
|
|
|
462
478
|
* @group Other
|
|
463
479
|
*/
|
|
464
480
|
isDragging() {
|
|
465
|
-
return this._dndHandler.
|
|
481
|
+
return this._dndHandler._isDragging;
|
|
466
482
|
}
|
|
467
483
|
|
|
468
484
|
/**
|
|
@@ -471,7 +487,7 @@ class TreeElement {
|
|
|
471
487
|
* @group Selection
|
|
472
488
|
*/
|
|
473
489
|
isNodeSelected(node) {
|
|
474
|
-
return this._selectNodeHandler.
|
|
490
|
+
return this._selectNodeHandler._isNodeSelected(node);
|
|
475
491
|
}
|
|
476
492
|
|
|
477
493
|
/**
|
|
@@ -490,7 +506,7 @@ class TreeElement {
|
|
|
490
506
|
this._initTree(data);
|
|
491
507
|
}
|
|
492
508
|
if (this.isDragging()) {
|
|
493
|
-
this._dndHandler.
|
|
509
|
+
this._dndHandler._refresh();
|
|
494
510
|
}
|
|
495
511
|
}
|
|
496
512
|
this._triggerEvent("tree.set_data", {
|
|
@@ -506,9 +522,9 @@ class TreeElement {
|
|
|
506
522
|
* @group Loading data
|
|
507
523
|
*/
|
|
508
524
|
async loadDataFromUrl(url, parentNode) {
|
|
509
|
-
|
|
525
|
+
let requestUrl = url ? new RequestUrl(url) : this._createRequestUrl(parentNode);
|
|
510
526
|
if (requestUrl) {
|
|
511
|
-
await this._dataLoader.
|
|
527
|
+
await this._dataLoader._loadFromUrl(requestUrl, parentNode);
|
|
512
528
|
}
|
|
513
529
|
}
|
|
514
530
|
|
|
@@ -518,10 +534,7 @@ class TreeElement {
|
|
|
518
534
|
* @group Selection
|
|
519
535
|
*/
|
|
520
536
|
moveDown() {
|
|
521
|
-
|
|
522
|
-
if (selectedNode) {
|
|
523
|
-
this._keyHandler.moveDown(selectedNode);
|
|
524
|
-
}
|
|
537
|
+
this._keyHandler._moveDown();
|
|
525
538
|
}
|
|
526
539
|
|
|
527
540
|
/**
|
|
@@ -536,7 +549,7 @@ class TreeElement {
|
|
|
536
549
|
* @group Changing the tree
|
|
537
550
|
*/
|
|
538
551
|
moveNode(node, targetNode, position) {
|
|
539
|
-
this.
|
|
552
|
+
this._tree.moveNode(node, targetNode, position);
|
|
540
553
|
this._refreshElements(null);
|
|
541
554
|
}
|
|
542
555
|
|
|
@@ -546,10 +559,7 @@ class TreeElement {
|
|
|
546
559
|
* @group Selection
|
|
547
560
|
*/
|
|
548
561
|
moveUp() {
|
|
549
|
-
|
|
550
|
-
if (selectedNode) {
|
|
551
|
-
this._keyHandler.moveUp(selectedNode);
|
|
552
|
-
}
|
|
562
|
+
this._keyHandler._moveUp();
|
|
553
563
|
}
|
|
554
564
|
|
|
555
565
|
/**
|
|
@@ -567,13 +577,13 @@ class TreeElement {
|
|
|
567
577
|
* @group Opening and closing
|
|
568
578
|
*/
|
|
569
579
|
async openNode(node, slide) {
|
|
570
|
-
|
|
571
|
-
|
|
580
|
+
let mustSlide = slide ?? this._options.slide;
|
|
581
|
+
let doOpenNode = async (openedNode, slideOption) => {
|
|
572
582
|
if (!node.children.length) {
|
|
573
583
|
return;
|
|
574
584
|
}
|
|
575
|
-
|
|
576
|
-
await folderElement.
|
|
585
|
+
let folderElement = this._createFolderElement(openedNode);
|
|
586
|
+
await folderElement._open(slideOption, this._options.animationSpeed);
|
|
577
587
|
};
|
|
578
588
|
if (node.isFolder() || node.isEmptyFolder) {
|
|
579
589
|
if (node.load_on_demand) {
|
|
@@ -600,7 +610,7 @@ class TreeElement {
|
|
|
600
610
|
* @group Changing the tree
|
|
601
611
|
*/
|
|
602
612
|
prependNode(nodeData, parentNode) {
|
|
603
|
-
|
|
613
|
+
let node = parentNode.prepend(nodeData);
|
|
604
614
|
this._refreshElements(parentNode);
|
|
605
615
|
return node;
|
|
606
616
|
}
|
|
@@ -622,7 +632,7 @@ class TreeElement {
|
|
|
622
632
|
* @group Other
|
|
623
633
|
*/
|
|
624
634
|
refreshHitAreas() {
|
|
625
|
-
this._dndHandler.
|
|
635
|
+
this._dndHandler._refresh();
|
|
626
636
|
}
|
|
627
637
|
|
|
628
638
|
/**
|
|
@@ -631,8 +641,9 @@ class TreeElement {
|
|
|
631
641
|
* @group Selection
|
|
632
642
|
*/
|
|
633
643
|
removeFromSelection(node) {
|
|
634
|
-
this._selectNodeHandler.
|
|
635
|
-
this._getNodeElementForNode(node).
|
|
644
|
+
this._selectNodeHandler._removeFromSelection(node);
|
|
645
|
+
this._getNodeElementForNode(node)._deselect();
|
|
646
|
+
this._updateTabStop();
|
|
636
647
|
this._saveState();
|
|
637
648
|
}
|
|
638
649
|
|
|
@@ -642,9 +653,9 @@ class TreeElement {
|
|
|
642
653
|
* @group Changing the tree
|
|
643
654
|
*/
|
|
644
655
|
removeNode(node) {
|
|
645
|
-
this._selectNodeHandler.
|
|
656
|
+
this._selectNodeHandler._removeFromSelection(node, true); // including children
|
|
646
657
|
|
|
647
|
-
|
|
658
|
+
let parent = node.parent;
|
|
648
659
|
node.remove();
|
|
649
660
|
this._refreshElements(parent);
|
|
650
661
|
}
|
|
@@ -658,8 +669,8 @@ class TreeElement {
|
|
|
658
669
|
if (!node.element) {
|
|
659
670
|
return;
|
|
660
671
|
}
|
|
661
|
-
|
|
662
|
-
this._scrollHandler.
|
|
672
|
+
let top = getOffsetTop(node.element) - getOffsetTop(this._htmlElement);
|
|
673
|
+
this._scrollHandler._scrollToY(top);
|
|
663
674
|
}
|
|
664
675
|
|
|
665
676
|
/**
|
|
@@ -675,10 +686,15 @@ class TreeElement {
|
|
|
675
686
|
if (!node) {
|
|
676
687
|
// Called with empty node -> deselect current node
|
|
677
688
|
this._deselectCurrentNode();
|
|
678
|
-
this._saveStateHandler.
|
|
689
|
+
this._saveStateHandler._saveState();
|
|
679
690
|
return;
|
|
680
691
|
}
|
|
681
|
-
|
|
692
|
+
|
|
693
|
+
// Unlike a click, selecting the selected node again keeps it selected
|
|
694
|
+
this._selectNodeHandler._selectSingleNode(node, {
|
|
695
|
+
mustToggle: false,
|
|
696
|
+
...options
|
|
697
|
+
});
|
|
682
698
|
}
|
|
683
699
|
|
|
684
700
|
/**
|
|
@@ -692,27 +708,31 @@ class TreeElement {
|
|
|
692
708
|
}
|
|
693
709
|
|
|
694
710
|
/**
|
|
695
|
-
* Applies a state to the tree
|
|
711
|
+
* Applies a state to the tree, and saves it to storage when `saveState` is
|
|
712
|
+
* enabled.
|
|
696
713
|
*
|
|
697
714
|
* @group State
|
|
698
715
|
*/
|
|
699
716
|
setState(state) {
|
|
700
|
-
this._saveStateHandler.
|
|
717
|
+
this._saveStateHandler._setInitialState(state);
|
|
701
718
|
this._refreshElements(null);
|
|
719
|
+
this._saveState();
|
|
702
720
|
}
|
|
703
721
|
|
|
704
722
|
/**
|
|
705
|
-
* Closes an open node and opens a closed one.
|
|
723
|
+
* Closes an open node and opens a closed one. Await the promise when you
|
|
724
|
+
* need to know the node is really open or closed, like with `openNode` and
|
|
725
|
+
* `closeNode`.
|
|
706
726
|
*
|
|
707
727
|
* @param slide - Override the `slide` option for this call.
|
|
708
728
|
* @group Opening and closing
|
|
709
729
|
*/
|
|
710
|
-
toggle(node, slide = null) {
|
|
711
|
-
|
|
730
|
+
async toggle(node, slide = null) {
|
|
731
|
+
let mustSlide = slide ?? this._options.slide;
|
|
712
732
|
if (node.is_open) {
|
|
713
|
-
this.closeNode(node, mustSlide);
|
|
733
|
+
await this.closeNode(node, mustSlide);
|
|
714
734
|
} else {
|
|
715
|
-
|
|
735
|
+
await this.openNode(node, mustSlide);
|
|
716
736
|
}
|
|
717
737
|
}
|
|
718
738
|
|
|
@@ -723,7 +743,7 @@ class TreeElement {
|
|
|
723
743
|
* @group Other
|
|
724
744
|
*/
|
|
725
745
|
toJson() {
|
|
726
|
-
return JSON.stringify(this.
|
|
746
|
+
return JSON.stringify(this._tree.getData());
|
|
727
747
|
}
|
|
728
748
|
|
|
729
749
|
/**
|
|
@@ -742,15 +762,15 @@ class TreeElement {
|
|
|
742
762
|
* @group Changing the tree
|
|
743
763
|
*/
|
|
744
764
|
updateNode(node, data) {
|
|
745
|
-
|
|
765
|
+
let idIsChanged = typeof data === "object" && data.id && data.id !== node.id;
|
|
746
766
|
if (idIsChanged) {
|
|
747
|
-
this.
|
|
767
|
+
this._tree.removeNodeFromIndex(node);
|
|
748
768
|
}
|
|
749
769
|
node.setData(data);
|
|
750
770
|
if (idIsChanged) {
|
|
751
|
-
this.
|
|
771
|
+
this._tree.addNodeToIndex(node);
|
|
752
772
|
}
|
|
753
|
-
if (typeof data === "object" && data.children && data.children
|
|
773
|
+
if (typeof data === "object" && data.children && Array.isArray(data.children)) {
|
|
754
774
|
node.removeChildren();
|
|
755
775
|
if (data.children.length) {
|
|
756
776
|
node.loadFromData(data.children);
|
|
@@ -758,69 +778,96 @@ class TreeElement {
|
|
|
758
778
|
}
|
|
759
779
|
this._refreshElements(node);
|
|
760
780
|
}
|
|
781
|
+
|
|
782
|
+
// Open the nodes up to the autoOpen level. A folder that is loaded on
|
|
783
|
+
// demand is fetched first, and its children are opened when the data has
|
|
784
|
+
// arrived.
|
|
785
|
+
async _autoOpenNodesOnDemand() {
|
|
786
|
+
let maxLevel = this._getAutoOpenMaxLevel();
|
|
787
|
+
let openNodes = async () => {
|
|
788
|
+
let loading = [];
|
|
789
|
+
this._tree.iterate((node, level) => {
|
|
790
|
+
if (node.load_on_demand) {
|
|
791
|
+
if (!node.is_loading) {
|
|
792
|
+
loading.push(this.openNode(node, false).then(openNodes));
|
|
793
|
+
}
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
void this.openNode(node, false);
|
|
797
|
+
return level !== maxLevel;
|
|
798
|
+
});
|
|
799
|
+
await Promise.all(loading);
|
|
800
|
+
};
|
|
801
|
+
await openNodes();
|
|
802
|
+
}
|
|
761
803
|
_createFolderElement(node) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
804
|
+
let classNames = this._classNames;
|
|
805
|
+
let closedIconElement = this._renderer._closedIconElement;
|
|
806
|
+
let getScrollLeft = this._scrollHandler._getScrollLeft.bind(this._scrollHandler);
|
|
807
|
+
let openedIconElement = this._renderer._openedIconElement;
|
|
808
|
+
let tabIndex = this._options.tabIndex;
|
|
809
|
+
let treeElement = this._htmlElement;
|
|
810
|
+
let triggerEvent = this._triggerEvent.bind(this);
|
|
811
|
+
|
|
812
|
+
// A selected node in a closed folder is rendered when the folder is
|
|
813
|
+
// opened, which can be after it was selected. It becomes the tab stop
|
|
814
|
+
// then, so the fallback tab stop must go.
|
|
815
|
+
let renderChildren = folder => {
|
|
816
|
+
let ul = this._renderer._renderChildren(folder);
|
|
817
|
+
this._updateTabStop();
|
|
818
|
+
return ul;
|
|
819
|
+
};
|
|
769
820
|
return new FolderElement({
|
|
770
|
-
classNames,
|
|
771
|
-
closedIconElement,
|
|
772
|
-
getScrollLeft,
|
|
773
|
-
node,
|
|
774
|
-
openedIconElement,
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
821
|
+
_classNames: classNames,
|
|
822
|
+
_closedIconElement: closedIconElement,
|
|
823
|
+
_getScrollLeft: getScrollLeft,
|
|
824
|
+
_node: node,
|
|
825
|
+
_openedIconElement: openedIconElement,
|
|
826
|
+
_renderChildren: renderChildren,
|
|
827
|
+
_tabIndex: tabIndex,
|
|
828
|
+
_treeElement: treeElement,
|
|
829
|
+
_triggerEvent: triggerEvent
|
|
778
830
|
});
|
|
779
831
|
}
|
|
780
832
|
_createNodeElement(node) {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
833
|
+
let classNames = this._classNames;
|
|
834
|
+
let getScrollLeft = this._scrollHandler._getScrollLeft.bind(this._scrollHandler);
|
|
835
|
+
let tabIndex = this._options.tabIndex;
|
|
836
|
+
let treeElement = this._htmlElement;
|
|
785
837
|
return new NodeElement({
|
|
786
|
-
classNames,
|
|
787
|
-
getScrollLeft,
|
|
788
|
-
node,
|
|
789
|
-
tabIndex,
|
|
790
|
-
treeElement
|
|
838
|
+
_classNames: classNames,
|
|
839
|
+
_getScrollLeft: getScrollLeft,
|
|
840
|
+
_node: node,
|
|
841
|
+
_tabIndex: tabIndex,
|
|
842
|
+
_treeElement: treeElement
|
|
791
843
|
});
|
|
792
844
|
}
|
|
793
845
|
|
|
794
846
|
/* Create a RequestUrl based on the url in the options.
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
847
|
+
* Add a 'node' query parameter for loading on demand
|
|
848
|
+
* Add a 'selected_node' query parameter if a node is selected.
|
|
849
|
+
*/
|
|
798
850
|
_createRequestUrl(node) {
|
|
799
|
-
|
|
800
|
-
let url;
|
|
801
|
-
if (typeof dataUrl === "function") {
|
|
802
|
-
url = dataUrl(node);
|
|
803
|
-
} else {
|
|
804
|
-
url = dataUrl;
|
|
805
|
-
}
|
|
851
|
+
let dataUrl = this._options.dataUrl;
|
|
852
|
+
let url = typeof dataUrl === "function" ? dataUrl(node) : dataUrl;
|
|
806
853
|
if (!url) {
|
|
807
854
|
return null;
|
|
808
855
|
}
|
|
809
|
-
|
|
856
|
+
let requestUrl = new RequestUrl(url);
|
|
810
857
|
if (node?.id) {
|
|
811
858
|
// Load on demand of a subtree; add node parameter
|
|
812
|
-
requestUrl.
|
|
859
|
+
requestUrl._setSearchParam("node", node.id.toString());
|
|
813
860
|
} else {
|
|
814
861
|
// Add selected_node parameter
|
|
815
|
-
|
|
862
|
+
let selectedNodeId = this._getNodeIdToBeSelected();
|
|
816
863
|
if (selectedNodeId) {
|
|
817
|
-
requestUrl.
|
|
864
|
+
requestUrl._setSearchParam("selected_node", selectedNodeId.toString());
|
|
818
865
|
}
|
|
819
866
|
}
|
|
820
867
|
return requestUrl;
|
|
821
868
|
}
|
|
822
869
|
_deselectCurrentNode() {
|
|
823
|
-
|
|
870
|
+
let node = this.getSelectedNode();
|
|
824
871
|
if (node) {
|
|
825
872
|
this.removeFromSelection(node);
|
|
826
873
|
}
|
|
@@ -828,9 +875,9 @@ class TreeElement {
|
|
|
828
875
|
|
|
829
876
|
// Deselect the children of the node.
|
|
830
877
|
_deselectNodes(parentNode) {
|
|
831
|
-
|
|
832
|
-
for (
|
|
833
|
-
this._selectNodeHandler.
|
|
878
|
+
let selectedNodesUnderParent = this._selectNodeHandler._getSelectedNodesUnder(parentNode);
|
|
879
|
+
for (let n of selectedNodesUnderParent) {
|
|
880
|
+
this._selectNodeHandler._removeFromSelection(n);
|
|
834
881
|
}
|
|
835
882
|
}
|
|
836
883
|
|
|
@@ -838,38 +885,59 @@ class TreeElement {
|
|
|
838
885
|
_getAutoOpenMaxLevel() {
|
|
839
886
|
if (this._options.autoOpen === true) {
|
|
840
887
|
return -1;
|
|
841
|
-
}
|
|
888
|
+
}
|
|
889
|
+
if (typeof this._options.autoOpen === "number") {
|
|
842
890
|
return this._options.autoOpen;
|
|
843
|
-
}
|
|
891
|
+
}
|
|
892
|
+
if (typeof this._options.autoOpen === "string") {
|
|
844
893
|
return parseInt(this._options.autoOpen, 10);
|
|
845
894
|
}
|
|
846
895
|
|
|
847
896
|
/* istanbul ignore next @preserve */
|
|
848
897
|
return 0;
|
|
849
898
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
}
|
|
899
|
+
|
|
900
|
+
// The title span of the first node, or null when the tree is empty
|
|
901
|
+
_getFirstTitleSpan() {
|
|
902
|
+
let firstElement = this._tree.children[0]?.element;
|
|
903
|
+
return firstElement ? firstElement.querySelector(`:scope > .${this._classNames.element} > span.${this._classNames.title}`) : null;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// The node of the HTML element that has the focus, if it is in this tree
|
|
907
|
+
_getFocusedNode() {
|
|
908
|
+
let activeElement = document.activeElement;
|
|
909
|
+
|
|
910
|
+
/* istanbul ignore if */
|
|
911
|
+
if (!activeElement) {
|
|
855
912
|
return null;
|
|
856
913
|
}
|
|
914
|
+
|
|
915
|
+
// The keyboard must still work for input elements.
|
|
916
|
+
let tagName = activeElement.tagName;
|
|
917
|
+
if (tagName !== "A" && tagName !== "SPAN") {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
let node = this.getNode(activeElement);
|
|
921
|
+
return node?.tree === this._tree ? node : null;
|
|
922
|
+
}
|
|
923
|
+
_getNodeElement(element) {
|
|
924
|
+
let node = this.getNode(element);
|
|
925
|
+
return node ? this._getNodeElementForNode(node) : null;
|
|
857
926
|
}
|
|
858
927
|
_getNodeElementForNode(node) {
|
|
859
|
-
|
|
860
|
-
return this._createFolderElement(node);
|
|
861
|
-
} else {
|
|
862
|
-
return this._createNodeElement(node);
|
|
863
|
-
}
|
|
928
|
+
return node.isFolder() ? this._createFolderElement(node) : this._createNodeElement(node);
|
|
864
929
|
}
|
|
865
930
|
_getNodeIdToBeSelected() {
|
|
866
|
-
return this._saveStateHandler.
|
|
931
|
+
return this._saveStateHandler._getNodeIdToBeSelected();
|
|
932
|
+
}
|
|
933
|
+
_hasRenderedSelectedNode() {
|
|
934
|
+
return this.getSelectedNodes().some(node => Boolean(node.element));
|
|
867
935
|
}
|
|
868
936
|
_initData() {
|
|
869
937
|
if (this._options.data) {
|
|
870
938
|
this.loadData(this._options.data);
|
|
871
939
|
} else {
|
|
872
|
-
|
|
940
|
+
let dataUrl = this._createRequestUrl();
|
|
873
941
|
if (dataUrl) {
|
|
874
942
|
void this.loadDataFromUrl();
|
|
875
943
|
} else {
|
|
@@ -878,16 +946,17 @@ class TreeElement {
|
|
|
878
946
|
}
|
|
879
947
|
}
|
|
880
948
|
_initTree(data) {
|
|
881
|
-
|
|
882
|
-
if (
|
|
883
|
-
|
|
884
|
-
this._triggerEvent("tree.init");
|
|
949
|
+
let doInit = () => {
|
|
950
|
+
if (this._isInitialized) {
|
|
951
|
+
return;
|
|
885
952
|
}
|
|
953
|
+
this._isInitialized = true;
|
|
954
|
+
this._triggerEvent("tree.init");
|
|
886
955
|
};
|
|
887
|
-
this.
|
|
888
|
-
this._selectNodeHandler.
|
|
889
|
-
this.
|
|
890
|
-
|
|
956
|
+
this._tree = new this._options.nodeClass(null, true, this._options.nodeClass);
|
|
957
|
+
this._selectNodeHandler._clear();
|
|
958
|
+
this._tree.loadFromData(data);
|
|
959
|
+
let mustLoadOnDemand = this._setInitialState();
|
|
891
960
|
this._refreshElements(null);
|
|
892
961
|
if (mustLoadOnDemand) {
|
|
893
962
|
// Load data on demand and then init the tree
|
|
@@ -899,32 +968,22 @@ class TreeElement {
|
|
|
899
968
|
|
|
900
969
|
// Does an HTML element of the tree have the focus?
|
|
901
970
|
_isFocusOnTree() {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
/* istanbul ignore if */
|
|
905
|
-
if (!activeElement) {
|
|
906
|
-
return false;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
// The keyboard must still work for input elements.
|
|
910
|
-
const tagName = activeElement.tagName;
|
|
911
|
-
if (tagName !== "A" && tagName !== "SPAN") {
|
|
912
|
-
return false;
|
|
913
|
-
}
|
|
914
|
-
const node = this.getNode(activeElement);
|
|
915
|
-
return node?.tree === this.tree;
|
|
971
|
+
return this._getFocusedNode() !== null;
|
|
916
972
|
}
|
|
917
973
|
_isSelectedNodeInSubtree(subtree) {
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
return false;
|
|
921
|
-
} else {
|
|
922
|
-
return subtree === selectedNode || subtree.isParentOf(selectedNode);
|
|
923
|
-
}
|
|
974
|
+
let selectedNode = this.getSelectedNode();
|
|
975
|
+
return selectedNode ? subtree === selectedNode || subtree.isParentOf(selectedNode) : false;
|
|
924
976
|
}
|
|
925
977
|
async _loadFolderOnDemand(node, slide) {
|
|
926
978
|
node.is_loading = true;
|
|
927
979
|
await this.loadDataFromUrl(undefined, node);
|
|
980
|
+
|
|
981
|
+
// Loading failed, or there is no url. Opening the node now would load it
|
|
982
|
+
// again, without end. Leave it closed so the user can try again.
|
|
983
|
+
if (node.load_on_demand) {
|
|
984
|
+
node.is_loading = false;
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
928
987
|
await this.openNode(node, slide);
|
|
929
988
|
}
|
|
930
989
|
_loadSubtree(data, parentNode) {
|
|
@@ -934,37 +993,35 @@ class TreeElement {
|
|
|
934
993
|
this._refreshElements(parentNode);
|
|
935
994
|
}
|
|
936
995
|
_mouseCapture(positionInfo) {
|
|
937
|
-
|
|
938
|
-
return false;
|
|
939
|
-
}
|
|
940
|
-
return this._dndHandler.mouseCapture(positionInfo);
|
|
996
|
+
return this._options.dragAndDrop ? this._dndHandler._mouseCapture(positionInfo) : false;
|
|
941
997
|
}
|
|
942
998
|
_mouseDrag(positionInfo) {
|
|
943
999
|
/* istanbul ignore if */
|
|
944
1000
|
if (!this._options.dragAndDrop) {
|
|
945
1001
|
return false;
|
|
946
1002
|
}
|
|
947
|
-
|
|
948
|
-
this._scrollHandler.
|
|
1003
|
+
let result = this._dndHandler._mouseDrag(positionInfo);
|
|
1004
|
+
this._scrollHandler._checkScrolling(positionInfo);
|
|
949
1005
|
return result;
|
|
950
1006
|
}
|
|
951
1007
|
_mouseStart(positionInfo) {
|
|
952
1008
|
/* istanbul ignore if */
|
|
1009
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
953
1010
|
if (!this._options.dragAndDrop) {
|
|
954
1011
|
return false;
|
|
955
1012
|
}
|
|
956
|
-
return this._dndHandler.
|
|
1013
|
+
return this._dndHandler._mouseStart(positionInfo);
|
|
957
1014
|
}
|
|
958
1015
|
_mouseStop(positionInfo) {
|
|
959
1016
|
/* istanbul ignore if */
|
|
960
1017
|
if (!this._options.dragAndDrop) {
|
|
961
1018
|
return false;
|
|
962
1019
|
}
|
|
963
|
-
this._scrollHandler.
|
|
964
|
-
return this._dndHandler.
|
|
1020
|
+
this._scrollHandler._stopScrolling();
|
|
1021
|
+
return this._dndHandler._mouseStop(positionInfo);
|
|
965
1022
|
}
|
|
966
1023
|
_openParents(node) {
|
|
967
|
-
|
|
1024
|
+
let parent = node.parent;
|
|
968
1025
|
if (parent?.parent && !parent.is_open) {
|
|
969
1026
|
void this.openNode(parent, false);
|
|
970
1027
|
}
|
|
@@ -975,57 +1032,58 @@ class TreeElement {
|
|
|
975
1032
|
from_node: redraw this subtree
|
|
976
1033
|
*/
|
|
977
1034
|
_refreshElements(fromNode) {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
this._renderer.
|
|
1035
|
+
let mustSetFocus = this._isFocusOnTree();
|
|
1036
|
+
let mustSelect = fromNode ? this._isSelectedNodeInSubtree(fromNode) : false;
|
|
1037
|
+
this._renderer._render(fromNode);
|
|
981
1038
|
if (mustSelect) {
|
|
982
1039
|
this._selectCurrentNode(mustSetFocus);
|
|
983
1040
|
}
|
|
1041
|
+
this._updateTabStop();
|
|
984
1042
|
this._triggerEvent("tree.refresh");
|
|
985
1043
|
}
|
|
986
1044
|
_saveState() {
|
|
987
|
-
this._saveStateHandler.
|
|
1045
|
+
this._saveStateHandler._saveState();
|
|
988
1046
|
}
|
|
989
1047
|
_selectCurrentNode(mustSetFocus) {
|
|
990
|
-
|
|
991
|
-
if (node) {
|
|
992
|
-
|
|
993
|
-
nodeElement.select(mustSetFocus);
|
|
1048
|
+
let node = this.getSelectedNode();
|
|
1049
|
+
if (!node) {
|
|
1050
|
+
return;
|
|
994
1051
|
}
|
|
1052
|
+
let nodeElement = this._getNodeElementForNode(node);
|
|
1053
|
+
nodeElement._select(mustSetFocus);
|
|
995
1054
|
}
|
|
996
1055
|
|
|
997
1056
|
// Set initial state, either by restoring the state or auto-opening nodes
|
|
998
1057
|
// result: must load nodes on demand?
|
|
999
1058
|
_setInitialState() {
|
|
1000
|
-
|
|
1059
|
+
let restoreState = () => {
|
|
1001
1060
|
// result: is state restored, must load on demand?
|
|
1002
|
-
|
|
1003
|
-
if (
|
|
1004
|
-
|
|
1005
|
-
} else {
|
|
1006
|
-
const mustLoadOnDemand = this._saveStateHandler.setInitialState(state);
|
|
1061
|
+
let state = this._saveStateHandler._getStateFromStorage();
|
|
1062
|
+
if (state) {
|
|
1063
|
+
let mustLoadOnDemand = this._saveStateHandler._setInitialState(state);
|
|
1007
1064
|
|
|
1008
1065
|
// return true: the state is restored
|
|
1009
1066
|
return [true, mustLoadOnDemand];
|
|
1010
1067
|
}
|
|
1068
|
+
return [false, false];
|
|
1011
1069
|
};
|
|
1012
|
-
|
|
1070
|
+
let autoOpenNodes = () => {
|
|
1013
1071
|
// result: must load on demand?
|
|
1014
1072
|
if (this._options.autoOpen === false) {
|
|
1015
1073
|
return false;
|
|
1016
1074
|
}
|
|
1017
|
-
|
|
1075
|
+
let maxLevel = this._getAutoOpenMaxLevel();
|
|
1018
1076
|
let mustLoadOnDemand = false;
|
|
1019
|
-
this.
|
|
1077
|
+
this._tree.iterate((node, level) => {
|
|
1020
1078
|
if (node.load_on_demand) {
|
|
1021
1079
|
mustLoadOnDemand = true;
|
|
1022
1080
|
return false;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
} else {
|
|
1081
|
+
}
|
|
1082
|
+
if (node.hasChildren()) {
|
|
1026
1083
|
node.is_open = true;
|
|
1027
1084
|
return level !== maxLevel;
|
|
1028
1085
|
}
|
|
1086
|
+
return false;
|
|
1029
1087
|
});
|
|
1030
1088
|
return mustLoadOnDemand;
|
|
1031
1089
|
};
|
|
@@ -1039,50 +1097,12 @@ class TreeElement {
|
|
|
1039
1097
|
|
|
1040
1098
|
// Set the initial state for nodes that are loaded on demand
|
|
1041
1099
|
async _setInitialStateOnDemand() {
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
void this._saveStateHandler.setInitialStateOnDemand(state).then(() => {
|
|
1049
|
-
resolve();
|
|
1050
|
-
});
|
|
1051
|
-
return true;
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
const autoOpenNodes = () => {
|
|
1055
|
-
const maxLevel = this._getAutoOpenMaxLevel();
|
|
1056
|
-
let loadingCount = 0;
|
|
1057
|
-
const loadAndOpenNode = node => {
|
|
1058
|
-
loadingCount += 1;
|
|
1059
|
-
void this.openNode(node, false).then(() => {
|
|
1060
|
-
loadingCount -= 1;
|
|
1061
|
-
openNodes();
|
|
1062
|
-
});
|
|
1063
|
-
};
|
|
1064
|
-
const openNodes = () => {
|
|
1065
|
-
this.tree.iterate((node, level) => {
|
|
1066
|
-
if (node.load_on_demand) {
|
|
1067
|
-
if (!node.is_loading) {
|
|
1068
|
-
loadAndOpenNode(node);
|
|
1069
|
-
}
|
|
1070
|
-
return false;
|
|
1071
|
-
} else {
|
|
1072
|
-
void this.openNode(node, false);
|
|
1073
|
-
return level !== maxLevel;
|
|
1074
|
-
}
|
|
1075
|
-
});
|
|
1076
|
-
if (loadingCount === 0) {
|
|
1077
|
-
resolve();
|
|
1078
|
-
}
|
|
1079
|
-
};
|
|
1080
|
-
openNodes();
|
|
1081
|
-
};
|
|
1082
|
-
if (!restoreState()) {
|
|
1083
|
-
autoOpenNodes();
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1100
|
+
let state = this._saveStateHandler._getStateFromStorage();
|
|
1101
|
+
if (state) {
|
|
1102
|
+
await this._saveStateHandler._setInitialStateOnDemand(state);
|
|
1103
|
+
} else {
|
|
1104
|
+
await this._autoOpenNodesOnDemand();
|
|
1105
|
+
}
|
|
1086
1106
|
}
|
|
1087
1107
|
|
|
1088
1108
|
// Set this HTML element to this node in the node map.
|
|
@@ -1092,6 +1112,23 @@ class TreeElement {
|
|
|
1092
1112
|
_triggerEvent(eventName, values) {
|
|
1093
1113
|
return this._triggerEventProvider(this._htmlElement, eventName, values);
|
|
1094
1114
|
}
|
|
1115
|
+
|
|
1116
|
+
/* Keyboard users reach the tree through the tab stop. The selected node is
|
|
1117
|
+
* the tab stop, see NodeElement.select. Without a selected node, the first
|
|
1118
|
+
* node is the tab stop, so the tree can always be reached.
|
|
1119
|
+
*/
|
|
1120
|
+
_updateTabStop() {
|
|
1121
|
+
let tabIndex = this._options.tabIndex;
|
|
1122
|
+
let previousTabStop = this._fallbackTabStop;
|
|
1123
|
+
let tabStop = tabIndex == null || this._hasRenderedSelectedNode() ? null : this._getFirstTitleSpan();
|
|
1124
|
+
if (previousTabStop && previousTabStop !== tabStop && previousTabStop.getAttribute("aria-selected") !== "true") {
|
|
1125
|
+
previousTabStop.setAttribute("tabindex", "-1");
|
|
1126
|
+
}
|
|
1127
|
+
if (tabStop && tabIndex != null) {
|
|
1128
|
+
tabStop.setAttribute("tabindex", String(tabIndex));
|
|
1129
|
+
}
|
|
1130
|
+
this._fallbackTabStop = tabStop;
|
|
1131
|
+
}
|
|
1095
1132
|
}
|
|
1096
1133
|
|
|
1097
1134
|
export { TreeElement as default };
|