tree-element 0.1.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.
Files changed (182) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +221 -0
  3. package/lib/animation.d.ts +5 -0
  4. package/lib/animation.d.ts.map +1 -0
  5. package/lib/animation.js +39 -0
  6. package/lib/animation.js.map +1 -0
  7. package/lib/classNames.d.ts +34 -0
  8. package/lib/classNames.d.ts.map +1 -0
  9. package/lib/classNames.js +39 -0
  10. package/lib/classNames.js.map +1 -0
  11. package/lib/dataLoader.d.ts +25 -0
  12. package/lib/dataLoader.d.ts.map +1 -0
  13. package/lib/dataLoader.js +69 -0
  14. package/lib/dataLoader.js.map +1 -0
  15. package/lib/dragAndDropHandler/binarySearch.d.ts +3 -0
  16. package/lib/dragAndDropHandler/binarySearch.d.ts.map +1 -0
  17. package/lib/dragAndDropHandler/binarySearch.js +23 -0
  18. package/lib/dragAndDropHandler/binarySearch.js.map +1 -0
  19. package/lib/dragAndDropHandler/dragElement.d.ts +20 -0
  20. package/lib/dragAndDropHandler/dragElement.d.ts.map +1 -0
  21. package/lib/dragAndDropHandler/dragElement.js +39 -0
  22. package/lib/dragAndDropHandler/dragElement.js.map +1 -0
  23. package/lib/dragAndDropHandler/generateHitAreas.d.ts +13 -0
  24. package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -0
  25. package/lib/dragAndDropHandler/generateHitAreas.js +130 -0
  26. package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -0
  27. package/lib/dragAndDropHandler/index.d.ts +76 -0
  28. package/lib/dragAndDropHandler/index.d.ts.map +1 -0
  29. package/lib/dragAndDropHandler/index.js +311 -0
  30. package/lib/dragAndDropHandler/index.js.map +1 -0
  31. package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts +11 -0
  32. package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts.map +1 -0
  33. package/lib/dragAndDropHandler/iterateVisibleNodes.js +54 -0
  34. package/lib/dragAndDropHandler/iterateVisibleNodes.js.map +1 -0
  35. package/lib/dragAndDropHandler/types.d.ts +11 -0
  36. package/lib/dragAndDropHandler/types.d.ts.map +1 -0
  37. package/lib/elementsRenderer.d.ts +53 -0
  38. package/lib/elementsRenderer.d.ts.map +1 -0
  39. package/lib/elementsRenderer.js +251 -0
  40. package/lib/elementsRenderer.js.map +1 -0
  41. package/lib/events.d.ts +224 -0
  42. package/lib/events.d.ts.map +1 -0
  43. package/lib/index.d.ts +367 -0
  44. package/lib/index.d.ts.map +1 -0
  45. package/lib/index.js +1098 -0
  46. package/lib/index.js.map +1 -0
  47. package/lib/keyHandler.d.ts +29 -0
  48. package/lib/keyHandler.d.ts.map +1 -0
  49. package/lib/keyHandler.js +107 -0
  50. package/lib/keyHandler.js.map +1 -0
  51. package/lib/methodTypes.d.ts +21 -0
  52. package/lib/methodTypes.d.ts.map +1 -0
  53. package/lib/mouseHandler.d.ts +60 -0
  54. package/lib/mouseHandler.d.ts.map +1 -0
  55. package/lib/mouseHandler.js +283 -0
  56. package/lib/mouseHandler.js.map +1 -0
  57. package/lib/mouseUtils.d.ts +9 -0
  58. package/lib/mouseUtils.d.ts.map +1 -0
  59. package/lib/mouseUtils.js +15 -0
  60. package/lib/mouseUtils.js.map +1 -0
  61. package/lib/node.d.ts +320 -0
  62. package/lib/node.d.ts.map +1 -0
  63. package/lib/node.js +737 -0
  64. package/lib/node.js.map +1 -0
  65. package/lib/nodeElement/borderDropHint.d.ts +9 -0
  66. package/lib/nodeElement/borderDropHint.d.ts.map +1 -0
  67. package/lib/nodeElement/borderDropHint.js +24 -0
  68. package/lib/nodeElement/borderDropHint.js.map +1 -0
  69. package/lib/nodeElement/folderElement.d.ts +22 -0
  70. package/lib/nodeElement/folderElement.d.ts.map +1 -0
  71. package/lib/nodeElement/folderElement.js +100 -0
  72. package/lib/nodeElement/folderElement.js.map +1 -0
  73. package/lib/nodeElement/ghostDropHint.d.ts +18 -0
  74. package/lib/nodeElement/ghostDropHint.d.ts.map +1 -0
  75. package/lib/nodeElement/ghostDropHint.js +67 -0
  76. package/lib/nodeElement/ghostDropHint.js.map +1 -0
  77. package/lib/nodeElement/index.d.ts +28 -0
  78. package/lib/nodeElement/index.d.ts.map +1 -0
  79. package/lib/nodeElement/index.js +66 -0
  80. package/lib/nodeElement/index.js.map +1 -0
  81. package/lib/nodeUtils.d.ts +7 -0
  82. package/lib/nodeUtils.d.ts.map +1 -0
  83. package/lib/nodeUtils.js +4 -0
  84. package/lib/nodeUtils.js.map +1 -0
  85. package/lib/options.d.ts +310 -0
  86. package/lib/options.d.ts.map +1 -0
  87. package/lib/package.json +3 -0
  88. package/lib/positionUtils.d.ts +8 -0
  89. package/lib/positionUtils.d.ts.map +1 -0
  90. package/lib/positionUtils.js +14 -0
  91. package/lib/positionUtils.js.map +1 -0
  92. package/lib/requestUrl.d.ts +9 -0
  93. package/lib/requestUrl.d.ts.map +1 -0
  94. package/lib/requestUrl.js +37 -0
  95. package/lib/requestUrl.js.map +1 -0
  96. package/lib/saveStateHandler.d.ts +46 -0
  97. package/lib/saveStateHandler.d.ts.map +1 -0
  98. package/lib/saveStateHandler.js +204 -0
  99. package/lib/saveStateHandler.js.map +1 -0
  100. package/lib/scrollHandler/containerScrollParent.d.ts +12 -0
  101. package/lib/scrollHandler/containerScrollParent.d.ts.map +1 -0
  102. package/lib/scrollHandler/containerScrollParent.js +49 -0
  103. package/lib/scrollHandler/containerScrollParent.js.map +1 -0
  104. package/lib/scrollHandler/createScrollParent.d.ts +4 -0
  105. package/lib/scrollHandler/createScrollParent.d.ts.map +1 -0
  106. package/lib/scrollHandler/createScrollParent.js +38 -0
  107. package/lib/scrollHandler/createScrollParent.js.map +1 -0
  108. package/lib/scrollHandler/documentScrollParent.d.ts +22 -0
  109. package/lib/scrollHandler/documentScrollParent.d.ts.map +1 -0
  110. package/lib/scrollHandler/documentScrollParent.js +71 -0
  111. package/lib/scrollHandler/documentScrollParent.js.map +1 -0
  112. package/lib/scrollHandler/scrollParent.d.ts +26 -0
  113. package/lib/scrollHandler/scrollParent.d.ts.map +1 -0
  114. package/lib/scrollHandler/scrollParent.js +79 -0
  115. package/lib/scrollHandler/scrollParent.js.map +1 -0
  116. package/lib/scrollHandler.d.ts +20 -0
  117. package/lib/scrollHandler.d.ts.map +1 -0
  118. package/lib/scrollHandler.js +41 -0
  119. package/lib/scrollHandler.js.map +1 -0
  120. package/lib/selectNodeHandler.d.ts +38 -0
  121. package/lib/selectNodeHandler.d.ts.map +1 -0
  122. package/lib/selectNodeHandler.js +166 -0
  123. package/lib/selectNodeHandler.js.map +1 -0
  124. package/lib/setDefaultOptions.d.ts +4 -0
  125. package/lib/setDefaultOptions.d.ts.map +1 -0
  126. package/lib/setDefaultOptions.js +86 -0
  127. package/lib/setDefaultOptions.js.map +1 -0
  128. package/lib/triggerCustomEvent.d.ts +3 -0
  129. package/lib/triggerCustomEvent.d.ts.map +1 -0
  130. package/lib/triggerCustomEvent.js +13 -0
  131. package/lib/triggerCustomEvent.js.map +1 -0
  132. package/lib/util.d.ts +3 -0
  133. package/lib/util.d.ts.map +1 -0
  134. package/lib/util.js +5 -0
  135. package/lib/util.js.map +1 -0
  136. package/lib/version.d.ts +3 -0
  137. package/lib/version.d.ts.map +1 -0
  138. package/lib/version.js +5 -0
  139. package/lib/version.js.map +1 -0
  140. package/package.json +110 -0
  141. package/src/animation.ts +48 -0
  142. package/src/classNames.ts +70 -0
  143. package/src/dataLoader.ts +92 -0
  144. package/src/dragAndDropHandler/binarySearch.ts +27 -0
  145. package/src/dragAndDropHandler/dragElement.ts +62 -0
  146. package/src/dragAndDropHandler/generateHitAreas.ts +195 -0
  147. package/src/dragAndDropHandler/index.ts +442 -0
  148. package/src/dragAndDropHandler/iterateVisibleNodes.ts +91 -0
  149. package/src/dragAndDropHandler/types.ts +12 -0
  150. package/src/elementsRenderer.ts +367 -0
  151. package/src/events.ts +237 -0
  152. package/src/header.txt +16 -0
  153. package/src/index.ts +1290 -0
  154. package/src/keyHandler.ts +139 -0
  155. package/src/methodTypes.ts +44 -0
  156. package/src/mouseHandler.ts +391 -0
  157. package/src/mouseUtils.ts +23 -0
  158. package/src/node.ts +831 -0
  159. package/src/nodeElement/borderDropHint.ts +37 -0
  160. package/src/nodeElement/folderElement.ts +140 -0
  161. package/src/nodeElement/ghostDropHint.ts +84 -0
  162. package/src/nodeElement/index.ts +103 -0
  163. package/src/nodeUtils.ts +12 -0
  164. package/src/options.ts +325 -0
  165. package/src/positionUtils.ts +18 -0
  166. package/src/requestUrl.ts +41 -0
  167. package/src/saveStateHandler.ts +279 -0
  168. package/src/scrollHandler/containerScrollParent.ts +71 -0
  169. package/src/scrollHandler/createScrollParent.ts +51 -0
  170. package/src/scrollHandler/documentScrollParent.ts +104 -0
  171. package/src/scrollHandler/scrollParent.ts +117 -0
  172. package/src/scrollHandler.ts +55 -0
  173. package/src/selectNodeHandler.ts +192 -0
  174. package/src/setDefaultOptions.ts +79 -0
  175. package/src/triggerCustomEvent.ts +14 -0
  176. package/src/util.ts +5 -0
  177. package/src/version.ts +4 -0
  178. package/tree_element.css +195 -0
  179. package/tree_element.debug.js +4153 -0
  180. package/tree_element.debug.js.map +1 -0
  181. package/tree_element.js +21 -0
  182. package/tree_element.js.map +1 -0
@@ -0,0 +1,442 @@
1
+ import type { ClassNames } from "../classNames";
2
+ import type {
3
+ GetScrollLeft,
4
+ GetTree,
5
+ OpenNode,
6
+ RefreshElements,
7
+ TriggerEvent,
8
+ } from "../methodTypes";
9
+ import type { PositionInfo } from "../mouseUtils";
10
+ import type { Node } from "../node";
11
+ import type NodeElement from "../nodeElement";
12
+ import type {
13
+ DragMethod,
14
+ OnCanMove,
15
+ OnCanMoveTo,
16
+ OnIsMoveHandle,
17
+ } from "../options";
18
+ import type { DropHint, HitArea } from "./types";
19
+
20
+ import { getElementPosition } from "../positionUtils";
21
+ import binarySearch from "./binarySearch";
22
+ import DragElement from "./dragElement";
23
+ import generateHitAreas from "./generateHitAreas";
24
+
25
+ interface Dimensions {
26
+ bottom: number;
27
+ left: number;
28
+ right: number;
29
+ top: number;
30
+ }
31
+
32
+ interface DragAndDropHandlerParams {
33
+ autoEscape?: boolean;
34
+ classNames: ClassNames;
35
+ getNodeElement: GetNodeElement;
36
+ getNodeElementForNode: GetNodeElementForNode;
37
+ getScrollLeft: GetScrollLeft;
38
+ getTree: GetTree;
39
+ onCanMove?: OnCanMove;
40
+ onCanMoveTo?: OnCanMoveTo;
41
+ onDragMove?: DragMethod;
42
+ onDragStop?: DragMethod;
43
+ onIsMoveHandle?: OnIsMoveHandle;
44
+ openFolderDelay: false | number;
45
+ openNode: OpenNode;
46
+ refreshElements: RefreshElements;
47
+ slide: boolean;
48
+ treeElement: HTMLElement;
49
+ triggerEvent: TriggerEvent;
50
+ }
51
+ type GetNodeElement = (element: HTMLElement) => NodeElement | null;
52
+
53
+ type GetNodeElementForNode = (node: Node) => NodeElement;
54
+
55
+ export class DragAndDropHandler {
56
+ public currentItem: NodeElement | null;
57
+ public hitAreas: HitArea[];
58
+ public hoveredArea: HitArea | null;
59
+ public isDragging: boolean;
60
+
61
+ private autoEscape?: boolean;
62
+ private classNames: ClassNames;
63
+ private dragElement: DragElement | null;
64
+ private getNodeElement: GetNodeElement;
65
+ private getNodeElementForNode: GetNodeElementForNode;
66
+ private getScrollLeft: GetScrollLeft;
67
+ private getTree: GetTree;
68
+ private onCanMove?: OnCanMove;
69
+ private onCanMoveTo?: OnCanMoveTo;
70
+ private onDragMove?: DragMethod;
71
+ private onDragStop?: DragMethod;
72
+ private onIsMoveHandle?: OnIsMoveHandle;
73
+ private openFolderDelay: false | number;
74
+ private openFolderTimer: null | number;
75
+ private openNode: OpenNode;
76
+ private previousGhost: DropHint | null;
77
+ private refreshElements: RefreshElements;
78
+ private slide: boolean;
79
+ private treeElement: HTMLElement;
80
+ private triggerEvent: TriggerEvent;
81
+
82
+ constructor({
83
+ autoEscape,
84
+ classNames,
85
+ getNodeElement,
86
+ getNodeElementForNode,
87
+ getScrollLeft,
88
+ getTree,
89
+ onCanMove,
90
+ onCanMoveTo,
91
+ onDragMove,
92
+ onDragStop,
93
+ onIsMoveHandle,
94
+ openFolderDelay,
95
+ openNode,
96
+ refreshElements,
97
+ slide,
98
+ treeElement,
99
+ triggerEvent,
100
+ }: DragAndDropHandlerParams) {
101
+ this.autoEscape = autoEscape;
102
+ this.classNames = classNames;
103
+ this.getNodeElement = getNodeElement;
104
+ this.getNodeElementForNode = getNodeElementForNode;
105
+ this.getScrollLeft = getScrollLeft;
106
+ this.getTree = getTree;
107
+ this.onCanMove = onCanMove;
108
+ this.onCanMoveTo = onCanMoveTo;
109
+ this.onDragMove = onDragMove;
110
+ this.onDragStop = onDragStop;
111
+ this.onIsMoveHandle = onIsMoveHandle;
112
+ this.openFolderDelay = openFolderDelay;
113
+ this.openNode = openNode;
114
+ this.refreshElements = refreshElements;
115
+ this.slide = slide;
116
+ this.treeElement = treeElement;
117
+ this.triggerEvent = triggerEvent;
118
+
119
+ this.hoveredArea = null;
120
+ this.hitAreas = [];
121
+ this.isDragging = false;
122
+ this.currentItem = null;
123
+
124
+ this.dragElement = null;
125
+ this.openFolderTimer = null;
126
+ this.previousGhost = null;
127
+ }
128
+
129
+ public mouseCapture(positionInfo: PositionInfo): boolean | null {
130
+ const element = positionInfo.target;
131
+
132
+ if (!this.mustCaptureElement(element)) {
133
+ return null;
134
+ }
135
+
136
+ if (this.onIsMoveHandle && !this.onIsMoveHandle(element)) {
137
+ return null;
138
+ }
139
+
140
+ let nodeElement = this.getNodeElement(element);
141
+
142
+ if (nodeElement && this.onCanMove) {
143
+ if (!this.onCanMove(nodeElement.node)) {
144
+ nodeElement = null;
145
+ }
146
+ }
147
+
148
+ this.currentItem = nodeElement;
149
+ return this.currentItem != null;
150
+ }
151
+
152
+ public mouseDrag(positionInfo: PositionInfo): boolean {
153
+ if (!this.currentItem || !this.dragElement) {
154
+ return false;
155
+ }
156
+
157
+ this.dragElement.move(positionInfo.pageX, positionInfo.pageY);
158
+
159
+ const area = this.findHoveredArea(
160
+ positionInfo.pageX,
161
+ positionInfo.pageY,
162
+ );
163
+
164
+ if (area && this.canMoveToArea(area, this.currentItem)) {
165
+ if (!area.node.isFolder()) {
166
+ this.stopOpenFolderTimer();
167
+ }
168
+
169
+ if (this.hoveredArea !== area) {
170
+ this.hoveredArea = area;
171
+
172
+ // If this is a closed folder, start timer to open it
173
+ if (this.mustOpenFolderTimer(area)) {
174
+ this.startOpenFolderTimer(area.node);
175
+ } else {
176
+ this.stopOpenFolderTimer();
177
+ }
178
+
179
+ this.updateDropHint();
180
+ }
181
+ } else {
182
+ this.removeDropHint();
183
+ this.stopOpenFolderTimer();
184
+ this.hoveredArea = area;
185
+ }
186
+
187
+ if (!area) {
188
+ if (this.onDragMove) {
189
+ this.onDragMove(
190
+ this.currentItem.node,
191
+ positionInfo.originalEvent,
192
+ );
193
+ }
194
+ }
195
+
196
+ return true;
197
+ }
198
+
199
+ public mouseStart(positionInfo: PositionInfo): boolean {
200
+ if (!this.currentItem) {
201
+ return false;
202
+ }
203
+
204
+ this.refresh();
205
+
206
+ const { left, top } = getElementPosition(positionInfo.target);
207
+
208
+ const node = this.currentItem.node;
209
+
210
+ this.dragElement = new DragElement({
211
+ autoEscape: this.autoEscape ?? true,
212
+ classNames: this.classNames,
213
+ nodeName: node.name,
214
+ offsetX: positionInfo.pageX - left,
215
+ offsetY: positionInfo.pageY - top,
216
+ treeElement: this.treeElement,
217
+ });
218
+
219
+ this.isDragging = true;
220
+ this.currentItem.element.classList.add(this.classNames.moving);
221
+
222
+ return true;
223
+ }
224
+
225
+ public mouseStop(positionInfo: PositionInfo): boolean {
226
+ this.moveItem(positionInfo);
227
+ this.clear();
228
+ this.removeHover();
229
+ this.removeDropHint();
230
+ this.removeHitAreas();
231
+
232
+ const currentItem = this.currentItem;
233
+
234
+ if (this.currentItem) {
235
+ this.currentItem.element.classList.remove(this.classNames.moving);
236
+ this.currentItem = null;
237
+ }
238
+
239
+ this.isDragging = false;
240
+
241
+ if (!this.hoveredArea && currentItem) {
242
+ if (this.onDragStop) {
243
+ this.onDragStop(currentItem.node, positionInfo.originalEvent);
244
+ }
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+ public refresh(): void {
251
+ this.removeHitAreas();
252
+
253
+ if (this.currentItem) {
254
+ const currentNode = this.currentItem.node;
255
+ this.generateHitAreas(currentNode);
256
+ this.currentItem = this.getNodeElementForNode(currentNode);
257
+
258
+ if (this.isDragging) {
259
+ this.currentItem.element.classList.add(this.classNames.moving);
260
+ }
261
+ }
262
+ }
263
+
264
+ private canMoveToArea(area: HitArea, currentItem: NodeElement): boolean {
265
+ if (!this.onCanMoveTo) {
266
+ return true;
267
+ }
268
+
269
+ return this.onCanMoveTo(currentItem.node, area.node, area.position);
270
+ }
271
+
272
+ private clear(): void {
273
+ if (this.dragElement) {
274
+ this.dragElement.remove();
275
+ this.dragElement = null;
276
+ }
277
+ }
278
+
279
+ private findHoveredArea(x: number, y: number): HitArea | null {
280
+ const dimensions = this.getTreeDimensions();
281
+
282
+ if (
283
+ x < dimensions.left ||
284
+ y < dimensions.top ||
285
+ x > dimensions.right ||
286
+ y > dimensions.bottom
287
+ ) {
288
+ return null;
289
+ }
290
+
291
+ return binarySearch<HitArea>(this.hitAreas, (area) => {
292
+ if (y < area.top) {
293
+ return 1;
294
+ } else if (y > area.bottom) {
295
+ return -1;
296
+ } else {
297
+ return 0;
298
+ }
299
+ });
300
+ }
301
+
302
+ private generateHitAreas(currentNode: Node): void {
303
+ const tree = this.getTree();
304
+
305
+ if (!tree) {
306
+ this.hitAreas = [];
307
+ } else {
308
+ this.hitAreas = generateHitAreas(
309
+ tree,
310
+ currentNode,
311
+ this.getTreeDimensions().bottom,
312
+ );
313
+ }
314
+ }
315
+
316
+ private getTreeDimensions(): Dimensions {
317
+ // Return the dimensions of the tree. Add a margin to the bottom to allow
318
+ // to drag-and-drop after the last element.
319
+ const treePosition = getElementPosition(this.treeElement);
320
+ const left = treePosition.left + this.getScrollLeft();
321
+ const top = treePosition.top;
322
+
323
+ return {
324
+ bottom: top + this.treeElement.clientHeight + 16,
325
+ left,
326
+ right: left + this.treeElement.clientWidth,
327
+ top,
328
+ };
329
+ }
330
+
331
+ /* Move the dragged node to the selected position in the tree. */
332
+ private moveItem(positionInfo: PositionInfo): void {
333
+ if (
334
+ this.currentItem &&
335
+ this.hoveredArea?.position &&
336
+ this.canMoveToArea(this.hoveredArea, this.currentItem)
337
+ ) {
338
+ const movedNode = this.currentItem.node;
339
+ const targetNode = this.hoveredArea.node;
340
+ const position = this.hoveredArea.position;
341
+ const previousParent = movedNode.parent;
342
+
343
+ if (position === "inside") {
344
+ this.hoveredArea.node.is_open = true;
345
+ }
346
+
347
+ const doMove = (): void => {
348
+ const tree = this.getTree();
349
+
350
+ if (tree) {
351
+ tree.moveNode(movedNode, targetNode, position);
352
+
353
+ this.treeElement.textContent = "";
354
+ this.refreshElements(null);
355
+ }
356
+ };
357
+
358
+ if (this.triggerEvent("tree.move", {
359
+ moveInfo: {
360
+ doMove,
361
+ movedNode,
362
+ originalEvent: positionInfo.originalEvent,
363
+ position,
364
+ previousParent,
365
+ targetNode,
366
+ },
367
+ })) {
368
+ doMove();
369
+ }
370
+ }
371
+ }
372
+
373
+ private mustCaptureElement(element: HTMLElement): boolean {
374
+ const nodeName = element.nodeName;
375
+
376
+ return (
377
+ nodeName !== "INPUT" &&
378
+ nodeName !== "SELECT" &&
379
+ nodeName !== "TEXTAREA"
380
+ );
381
+ }
382
+
383
+ private mustOpenFolderTimer(area: HitArea): boolean {
384
+ const node = area.node;
385
+
386
+ return node.isFolder() && !node.is_open && area.position === "inside";
387
+ }
388
+
389
+ private removeDropHint(): void {
390
+ if (this.previousGhost) {
391
+ this.previousGhost.remove();
392
+ }
393
+ }
394
+
395
+ private removeHitAreas(): void {
396
+ this.hitAreas = [];
397
+ }
398
+
399
+ private removeHover(): void {
400
+ this.hoveredArea = null;
401
+ }
402
+
403
+ private startOpenFolderTimer(folder: Node): void {
404
+ const openFolder = (): void => {
405
+ void this.openNode(folder, this.slide).then(() => {
406
+ this.refresh();
407
+ this.updateDropHint();
408
+ });
409
+ };
410
+
411
+ this.stopOpenFolderTimer();
412
+
413
+ const openFolderDelay = this.openFolderDelay;
414
+
415
+ if (openFolderDelay !== false) {
416
+ this.openFolderTimer = window.setTimeout(
417
+ openFolder,
418
+ openFolderDelay,
419
+ );
420
+ }
421
+ }
422
+
423
+ private stopOpenFolderTimer(): void {
424
+ if (this.openFolderTimer) {
425
+ clearTimeout(this.openFolderTimer);
426
+ this.openFolderTimer = null;
427
+ }
428
+ }
429
+
430
+ private updateDropHint(): void {
431
+ if (!this.hoveredArea) {
432
+ return;
433
+ }
434
+
435
+ // remove previous drop hint
436
+ this.removeDropHint();
437
+
438
+ // add new drop hint
439
+ const nodeElement = this.getNodeElementForNode(this.hoveredArea.node);
440
+ this.previousGhost = nodeElement.addDropHint(this.hoveredArea.position);
441
+ }
442
+ }
@@ -0,0 +1,91 @@
1
+ import type { Node } from "../node";
2
+
3
+ interface Options {
4
+ handleAfterOpenFolder: (node: Node, nextNode: Node | null) => void;
5
+ handleClosedFolder: (
6
+ node: Node,
7
+ nextNode: Node | null,
8
+ element: HTMLElement,
9
+ ) => void;
10
+ handleFirstNode: (node: Node) => void;
11
+ handleNode: (
12
+ node: Node,
13
+ nextNode: Node | null,
14
+ element: HTMLElement,
15
+ ) => void;
16
+
17
+ /*
18
+ override
19
+ return
20
+ - true: continue iterating
21
+ - false: stop iterating
22
+ */
23
+ handleOpenFolder: (node: Node, element: HTMLElement) => boolean;
24
+ }
25
+
26
+ const iterateVisibleNodes = (
27
+ tree: Node,
28
+ {
29
+ handleAfterOpenFolder,
30
+ handleClosedFolder,
31
+ handleFirstNode,
32
+ handleNode,
33
+ handleOpenFolder,
34
+ }: Options,
35
+ ) => {
36
+ let isFirstNode = true;
37
+
38
+ const iterate = (node: Node, nextNode: Node | null): void => {
39
+ let mustIterateInside =
40
+ (node.is_open === true || !node.element) && node.hasChildren();
41
+
42
+ let element: HTMLElement | null = null;
43
+
44
+ // Is the element visible?
45
+ if (node.element?.offsetParent) {
46
+ element = node.element;
47
+
48
+ if (isFirstNode) {
49
+ handleFirstNode(node);
50
+ isFirstNode = false;
51
+ }
52
+
53
+ if (!node.hasChildren()) {
54
+ handleNode(node, nextNode, node.element);
55
+ } else if (node.is_open) {
56
+ if (!handleOpenFolder(node, node.element)) {
57
+ mustIterateInside = false;
58
+ }
59
+ } else {
60
+ handleClosedFolder(node, nextNode, element);
61
+ }
62
+ }
63
+
64
+ if (mustIterateInside) {
65
+ const childrenLength = node.children.length;
66
+ node.children.forEach((_, i) => {
67
+ const child = node.children[i];
68
+
69
+ if (child) {
70
+ if (i === childrenLength - 1) {
71
+ iterate(child, null);
72
+ } else {
73
+ const nextChild = node.children[i + 1];
74
+
75
+ if (nextChild) {
76
+ iterate(child, nextChild);
77
+ }
78
+ }
79
+ }
80
+ });
81
+
82
+ if (node.is_open && element) {
83
+ handleAfterOpenFolder(node, nextNode);
84
+ }
85
+ }
86
+ };
87
+
88
+ iterate(tree, null);
89
+ };
90
+
91
+ export default iterateVisibleNodes;
@@ -0,0 +1,12 @@
1
+ import type { Node, Position } from "../node";
2
+
3
+ export interface DropHint {
4
+ remove: () => void;
5
+ }
6
+
7
+ export interface HitArea {
8
+ bottom: number;
9
+ node: Node;
10
+ position: Position;
11
+ top: number;
12
+ }