jqtree 1.7.5 → 1.8.1

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 (119) hide show
  1. package/.eslintrc +13 -3
  2. package/.github/workflows/ci.yml +6 -6
  3. package/.github/workflows/codeql-analysis.yml +4 -4
  4. package/.github/workflows/size.yml +3 -3
  5. package/.github/workflows/static.yml +1 -1
  6. package/bower.json +1 -1
  7. package/config/babel.config.json +1 -1
  8. package/config/jest.config.js +4 -0
  9. package/config/jest.polyfills.js +14 -0
  10. package/config/production +2 -0
  11. package/devserver/devserver_scroll.js +8 -0
  12. package/devserver/test_index.html +9 -0
  13. package/devserver/test_scroll.html +28 -0
  14. package/devserver/test_scroll_container.html +39 -0
  15. package/docs/.ruby-version +1 -1
  16. package/docs/_config.yml +1 -1
  17. package/docs/_entries/general/changelog.md +11 -0
  18. package/docs/_entries/multiple_selection/get-selected-nodes.md +1 -1
  19. package/docs/_entries/node/getnextnode.md +3 -6
  20. package/docs/_entries/node/getnextsibling.md +1 -1
  21. package/docs/_entries/node/getnextvisiblenode.md +8 -5
  22. package/docs/_entries/node/getpreviousnode.md +12 -0
  23. package/docs/_entries/node/getprevioussibling.md +1 -1
  24. package/docs/_entries/node/getpreviousvisiblenode.md +6 -5
  25. package/package.json +35 -29
  26. package/src/dataLoader.ts +57 -34
  27. package/src/dragAndDropHandler/dragElement.ts +54 -0
  28. package/src/dragAndDropHandler/generateHitAreas.ts +176 -0
  29. package/src/dragAndDropHandler/index.ts +454 -0
  30. package/src/dragAndDropHandler/iterateVisibleNodes.ts +91 -0
  31. package/src/dragAndDropHandler/types.ts +13 -0
  32. package/src/elementsRenderer.ts +75 -40
  33. package/src/jqtreeMethodTypes.ts +40 -0
  34. package/src/jqtreeOptions.ts +43 -25
  35. package/src/keyHandler.ts +59 -30
  36. package/src/mouseHandler.ts +385 -0
  37. package/src/mouseUtils.ts +23 -0
  38. package/src/node.ts +1 -29
  39. package/src/nodeElement/borderDropHint.ts +32 -0
  40. package/src/nodeElement/folderElement.ts +133 -0
  41. package/src/nodeElement/ghostDropHint.ts +69 -0
  42. package/src/nodeElement/index.ts +102 -0
  43. package/src/playwright/coverage.ts +4 -7
  44. package/src/playwright/playwright.test.ts +150 -53
  45. package/src/playwright/testUtils.ts +28 -5
  46. package/src/position.ts +28 -0
  47. package/src/saveStateHandler.ts +75 -26
  48. package/src/scrollHandler/containerScrollParent.ts +13 -23
  49. package/src/scrollHandler/createScrollParent.ts +22 -22
  50. package/src/scrollHandler/documentScrollParent.ts +16 -13
  51. package/src/scrollHandler.ts +13 -15
  52. package/src/selectNodeHandler.ts +10 -16
  53. package/src/test/jqTree/events.test.ts +97 -30
  54. package/src/test/jqTree/keyboard.test.ts +18 -23
  55. package/src/test/jqTree/loadOnDemand.test.ts +22 -15
  56. package/src/test/jqTree/methods.test.ts +40 -14
  57. package/src/test/jqTree/mouse.test.ts +82 -0
  58. package/src/test/jqTree/options.test.ts +24 -12
  59. package/src/test/node.test.ts +3 -2
  60. package/src/test/{nodeUtil.test.ts → position.test.ts} +1 -1
  61. package/src/tree.jquery.ts +314 -208
  62. package/src/util.ts +12 -0
  63. package/src/version.ts +1 -1
  64. package/tree.jquery.debug.js +2594 -3419
  65. package/tree.jquery.debug.js.map +1 -1
  66. package/tree.jquery.js +3 -3
  67. package/tree.jquery.js.map +1 -1
  68. package/tsconfig.json +5 -3
  69. package/docs/_entries/functions/get-selected-nodes.md +0 -10
  70. package/lib/dataLoader.js +0 -123
  71. package/lib/dragAndDropHandler.js +0 -588
  72. package/lib/elementsRenderer.js +0 -267
  73. package/lib/jqtreeOptions.js +0 -1
  74. package/lib/keyHandler.js +0 -111
  75. package/lib/mouse.widget.js +0 -255
  76. package/lib/node.js +0 -708
  77. package/lib/nodeElement.js +0 -274
  78. package/lib/nodeUtils.js +0 -10
  79. package/lib/playwright/coverage.js +0 -99
  80. package/lib/playwright/playwright.test.js +0 -606
  81. package/lib/playwright/testUtils.js +0 -210
  82. package/lib/saveStateHandler.js +0 -277
  83. package/lib/scrollHandler/containerScrollParent.js +0 -160
  84. package/lib/scrollHandler/createScrollParent.js +0 -57
  85. package/lib/scrollHandler/documentScrollParent.js +0 -169
  86. package/lib/scrollHandler/scrollParent.js +0 -58
  87. package/lib/scrollHandler/types.js +0 -1
  88. package/lib/scrollHandler.js +0 -71
  89. package/lib/selectNodeHandler.js +0 -128
  90. package/lib/simple.widget.js +0 -158
  91. package/lib/test/global.d.js +0 -3
  92. package/lib/test/jqTree/accessibility.test.js +0 -37
  93. package/lib/test/jqTree/create.test.js +0 -48
  94. package/lib/test/jqTree/events.test.js +0 -210
  95. package/lib/test/jqTree/keyboard.test.js +0 -225
  96. package/lib/test/jqTree/loadOnDemand.test.js +0 -218
  97. package/lib/test/jqTree/methods.test.js +0 -1348
  98. package/lib/test/jqTree/options.test.js +0 -548
  99. package/lib/test/jqTree/scrollHandler/containerScrollParent.test.js +0 -94
  100. package/lib/test/node.test.js +0 -1202
  101. package/lib/test/nodeUtil.test.js +0 -27
  102. package/lib/test/nodeUtils.test.js +0 -20
  103. package/lib/test/support/exampleData.js +0 -35
  104. package/lib/test/support/jqTreeMatchers.js +0 -70
  105. package/lib/test/support/matchers.d.js +0 -1
  106. package/lib/test/support/setupTests.js +0 -7
  107. package/lib/test/support/testUtil.js +0 -29
  108. package/lib/test/support/treeStructure.js +0 -38
  109. package/lib/test/util.test.js +0 -26
  110. package/lib/tree.jquery.d.js +0 -1
  111. package/lib/tree.jquery.js +0 -1105
  112. package/lib/types.js +0 -1
  113. package/lib/typings.d.js +0 -2
  114. package/lib/util.js +0 -15
  115. package/lib/version.js +0 -8
  116. package/src/dragAndDropHandler.ts +0 -713
  117. package/src/mouse.widget.ts +0 -266
  118. package/src/nodeElement.ts +0 -272
  119. package/src/types.ts +0 -19
@@ -0,0 +1,385 @@
1
+ import {
2
+ getPositionInfoFromMouseEvent,
3
+ getPositionInfoFromTouch,
4
+ PositionInfo,
5
+ } from "./mouseUtils";
6
+ import { Node } from "./node";
7
+ import { TriggerEvent } from "./jqtreeMethodTypes";
8
+
9
+ interface ClickTarget {
10
+ node: Node;
11
+ type: "button" | "label";
12
+ }
13
+
14
+ type GetNode = (element: HTMLElement) => null | Node;
15
+
16
+ interface MouseHandlerParams {
17
+ element: HTMLElement;
18
+ getMouseDelay: () => number;
19
+ getNode: GetNode;
20
+ onClickButton: (node: Node) => void;
21
+ onClickTitle: (node: Node) => void;
22
+ onMouseCapture: (positionInfo: PositionInfo) => boolean | null;
23
+ onMouseDrag: (positionInfo: PositionInfo) => void;
24
+ onMouseStart: (positionInfo: PositionInfo) => boolean;
25
+ onMouseStop: (positionInfo: PositionInfo) => void;
26
+ triggerEvent: TriggerEvent;
27
+ useContextMenu: boolean;
28
+ }
29
+
30
+ class MouseHandler {
31
+ private element: HTMLElement;
32
+ private getMouseDelay: () => number;
33
+ private getNode: GetNode;
34
+ private isMouseDelayMet: boolean;
35
+ private isMouseStarted: boolean;
36
+ private mouseDelayTimer: number | null;
37
+ private mouseDownInfo: PositionInfo | null;
38
+ private onClickButton: (node: Node) => void;
39
+ private onClickTitle: (node: Node) => void;
40
+ private onMouseCapture: (positionInfo: PositionInfo) => boolean | null;
41
+ private onMouseDrag: (positionInfo: PositionInfo) => void;
42
+ private onMouseStart: (positionInfo: PositionInfo) => boolean;
43
+ private onMouseStop: (positionInfo: PositionInfo) => void;
44
+ private triggerEvent: TriggerEvent;
45
+ private useContextMenu: boolean;
46
+
47
+ constructor({
48
+ element,
49
+ getMouseDelay,
50
+ getNode,
51
+ onClickButton,
52
+ onClickTitle,
53
+ onMouseCapture,
54
+ onMouseDrag,
55
+ onMouseStart,
56
+ onMouseStop,
57
+ triggerEvent,
58
+ useContextMenu,
59
+ }: MouseHandlerParams) {
60
+ this.element = element;
61
+ this.getMouseDelay = getMouseDelay;
62
+ this.getNode = getNode;
63
+ this.onClickButton = onClickButton;
64
+ this.onClickTitle = onClickTitle;
65
+ this.onMouseCapture = onMouseCapture;
66
+ this.onMouseDrag = onMouseDrag;
67
+ this.onMouseStart = onMouseStart;
68
+ this.onMouseStop = onMouseStop;
69
+ this.triggerEvent = triggerEvent;
70
+ this.useContextMenu = useContextMenu;
71
+
72
+ element.addEventListener("click", this.handleClick);
73
+ element.addEventListener("dblclick", this.handleDblclick);
74
+ element.addEventListener("mousedown", this.mouseDown, {
75
+ passive: false,
76
+ });
77
+ element.addEventListener("touchstart", this.touchStart, {
78
+ passive: false,
79
+ });
80
+
81
+ if (useContextMenu) {
82
+ element.addEventListener("contextmenu", this.handleContextmenu);
83
+ }
84
+
85
+ this.isMouseStarted = false;
86
+ this.mouseDelayTimer = null;
87
+ this.isMouseDelayMet = false;
88
+ this.mouseDownInfo = null;
89
+ }
90
+
91
+ public deinit(): void {
92
+ this.element.removeEventListener("click", this.handleClick);
93
+ this.element.removeEventListener("dblclick", this.handleDblclick);
94
+
95
+ if (this.useContextMenu) {
96
+ this.element.removeEventListener(
97
+ "contextmenu",
98
+ this.handleContextmenu,
99
+ );
100
+ }
101
+
102
+ this.element.removeEventListener("mousedown", this.mouseDown);
103
+ this.element.removeEventListener("touchstart", this.touchStart);
104
+ this.removeMouseMoveEventListeners();
105
+ }
106
+
107
+ private mouseDown = (e: MouseEvent): void => {
108
+ // Left mouse button?
109
+ if (e.button !== 0) {
110
+ return;
111
+ }
112
+
113
+ const result = this.handleMouseDown(getPositionInfoFromMouseEvent(e));
114
+
115
+ if (result && e.cancelable) {
116
+ e.preventDefault();
117
+ }
118
+ };
119
+
120
+ private handleMouseDown(positionInfo: PositionInfo): boolean {
121
+ // We may have missed mouseup (out of window)
122
+ if (this.isMouseStarted) {
123
+ this.handleMouseUp(positionInfo);
124
+ }
125
+
126
+ this.mouseDownInfo = positionInfo;
127
+
128
+ if (!this.onMouseCapture(positionInfo)) {
129
+ return false;
130
+ }
131
+
132
+ this.handleStartMouse();
133
+
134
+ return true;
135
+ }
136
+
137
+ private handleStartMouse(): void {
138
+ document.addEventListener("mousemove", this.mouseMove, {
139
+ passive: false,
140
+ });
141
+ document.addEventListener("touchmove", this.touchMove, {
142
+ passive: false,
143
+ });
144
+ document.addEventListener("mouseup", this.mouseUp, { passive: false });
145
+ document.addEventListener("touchend", this.touchEnd, {
146
+ passive: false,
147
+ });
148
+
149
+ const mouseDelay = this.getMouseDelay();
150
+
151
+ if (mouseDelay) {
152
+ this.startMouseDelayTimer(mouseDelay);
153
+ } else {
154
+ this.isMouseDelayMet = true;
155
+ }
156
+ }
157
+
158
+ private startMouseDelayTimer(mouseDelay: number): void {
159
+ if (this.mouseDelayTimer) {
160
+ clearTimeout(this.mouseDelayTimer);
161
+ }
162
+
163
+ this.mouseDelayTimer = window.setTimeout(() => {
164
+ if (this.mouseDownInfo) {
165
+ this.isMouseDelayMet = true;
166
+ }
167
+ }, mouseDelay);
168
+
169
+ this.isMouseDelayMet = false;
170
+ }
171
+
172
+ private mouseMove = (e: MouseEvent): void => {
173
+ this.handleMouseMove(e, getPositionInfoFromMouseEvent(e));
174
+ };
175
+
176
+ private handleMouseMove(
177
+ e: MouseEvent | TouchEvent,
178
+ positionInfo: PositionInfo,
179
+ ): void {
180
+ if (this.isMouseStarted) {
181
+ this.onMouseDrag(positionInfo);
182
+
183
+ if (e.cancelable) {
184
+ e.preventDefault();
185
+ }
186
+ return;
187
+ }
188
+
189
+ if (!this.isMouseDelayMet) {
190
+ return;
191
+ }
192
+
193
+ if (this.mouseDownInfo) {
194
+ this.isMouseStarted =
195
+ this.onMouseStart(this.mouseDownInfo) !== false;
196
+ }
197
+
198
+ if (this.isMouseStarted) {
199
+ this.onMouseDrag(positionInfo);
200
+
201
+ if (e.cancelable) {
202
+ e.preventDefault();
203
+ }
204
+ } else {
205
+ this.handleMouseUp(positionInfo);
206
+ }
207
+ }
208
+
209
+ private mouseUp = (e: MouseEvent): void => {
210
+ this.handleMouseUp(getPositionInfoFromMouseEvent(e));
211
+ };
212
+
213
+ private handleMouseUp(positionInfo: PositionInfo): void {
214
+ this.removeMouseMoveEventListeners();
215
+ this.isMouseDelayMet = false;
216
+ this.mouseDownInfo = null;
217
+
218
+ if (this.isMouseStarted) {
219
+ this.isMouseStarted = false;
220
+ this.onMouseStop(positionInfo);
221
+ }
222
+ }
223
+
224
+ private removeMouseMoveEventListeners() {
225
+ document.removeEventListener("mousemove", this.mouseMove);
226
+ document.removeEventListener("touchmove", this.touchMove);
227
+ document.removeEventListener("mouseup", this.mouseUp);
228
+ document.removeEventListener("touchend", this.touchEnd);
229
+ }
230
+
231
+ private touchStart = (e: TouchEvent): void => {
232
+ if (!e) {
233
+ return;
234
+ }
235
+
236
+ if (e.touches.length > 1) {
237
+ return;
238
+ }
239
+
240
+ const touch = e.touches[0];
241
+
242
+ if (!touch) {
243
+ return;
244
+ }
245
+
246
+ this.handleMouseDown(getPositionInfoFromTouch(touch, e));
247
+ };
248
+
249
+ private touchMove = (e: TouchEvent): void => {
250
+ if (!e) {
251
+ return;
252
+ }
253
+
254
+ if (e.touches.length > 1) {
255
+ return;
256
+ }
257
+
258
+ const touch = e.touches[0];
259
+
260
+ if (!touch) {
261
+ return;
262
+ }
263
+
264
+ this.handleMouseMove(e, getPositionInfoFromTouch(touch, e));
265
+ };
266
+
267
+ private touchEnd = (e: TouchEvent): void => {
268
+ if (!e) {
269
+ return;
270
+ }
271
+
272
+ if (e.touches.length > 1) {
273
+ return;
274
+ }
275
+
276
+ const touch = e.touches[0];
277
+
278
+ if (!touch) {
279
+ return;
280
+ }
281
+
282
+ this.handleMouseUp(getPositionInfoFromTouch(touch, e));
283
+ };
284
+
285
+ private handleClick = (e: MouseEvent): void => {
286
+ if (!e.target) {
287
+ return;
288
+ }
289
+
290
+ const clickTarget = this.getClickTarget(e.target as HTMLElement);
291
+
292
+ if (!clickTarget) {
293
+ return;
294
+ }
295
+
296
+ if (clickTarget.type === "button") {
297
+ this.onClickButton(clickTarget.node);
298
+
299
+ e.preventDefault();
300
+ e.stopPropagation();
301
+ } else if (clickTarget.type === "label") {
302
+ const event = this.triggerEvent("tree.click", {
303
+ node: clickTarget.node,
304
+ click_event: e,
305
+ });
306
+
307
+ if (!event.isDefaultPrevented()) {
308
+ this.onClickTitle(clickTarget.node);
309
+ }
310
+ }
311
+ };
312
+
313
+ private handleDblclick = (e: MouseEvent): void => {
314
+ if (!e.target) {
315
+ return;
316
+ }
317
+
318
+ const clickTarget = this.getClickTarget(e.target as HTMLElement);
319
+
320
+ if (clickTarget?.type === "label") {
321
+ this.triggerEvent("tree.dblclick", {
322
+ node: clickTarget.node,
323
+ click_event: e,
324
+ });
325
+ }
326
+ };
327
+
328
+ private handleContextmenu = (e: MouseEvent) => {
329
+ if (!e.target) {
330
+ return;
331
+ }
332
+
333
+ const div = (e.target as HTMLElement).closest<HTMLElement>(
334
+ "ul.jqtree-tree .jqtree-element",
335
+ );
336
+
337
+ if (div) {
338
+ const node = this.getNode(div);
339
+ if (node) {
340
+ e.preventDefault();
341
+ e.stopPropagation();
342
+
343
+ this.triggerEvent("tree.contextmenu", {
344
+ node,
345
+ click_event: e,
346
+ });
347
+ return false;
348
+ }
349
+ }
350
+
351
+ return null;
352
+ };
353
+
354
+ private getClickTarget(element: HTMLElement): ClickTarget | null {
355
+ const button = element.closest<HTMLElement>(".jqtree-toggler");
356
+
357
+ if (button) {
358
+ const node = this.getNode(button);
359
+
360
+ if (node) {
361
+ return {
362
+ type: "button",
363
+ node,
364
+ };
365
+ }
366
+ } else {
367
+ const jqTreeElement =
368
+ element.closest<HTMLElement>(".jqtree-element");
369
+
370
+ if (jqTreeElement) {
371
+ const node = this.getNode(jqTreeElement);
372
+ if (node) {
373
+ return {
374
+ type: "label",
375
+ node,
376
+ };
377
+ }
378
+ }
379
+ }
380
+
381
+ return null;
382
+ }
383
+ }
384
+
385
+ export default MouseHandler;
@@ -0,0 +1,23 @@
1
+ export interface PositionInfo {
2
+ originalEvent: Event;
3
+ pageX: number;
4
+ pageY: number;
5
+ target: HTMLElement;
6
+ }
7
+
8
+ export const getPositionInfoFromMouseEvent = (e: MouseEvent): PositionInfo => ({
9
+ originalEvent: e,
10
+ pageX: e.pageX,
11
+ pageY: e.pageY,
12
+ target: e.target as HTMLElement,
13
+ });
14
+
15
+ export const getPositionInfoFromTouch = (
16
+ touch: Touch,
17
+ e: TouchEvent,
18
+ ): PositionInfo => ({
19
+ originalEvent: e,
20
+ pageX: touch.pageX,
21
+ pageY: touch.pageY,
22
+ target: touch.target as HTMLElement,
23
+ });
package/src/node.ts CHANGED
@@ -1,36 +1,8 @@
1
1
  import { isNodeRecordWithChildren } from "./nodeUtils";
2
-
3
- export enum Position {
4
- Before = 1,
5
- After,
6
- Inside,
7
- None,
8
- }
9
-
10
- const positionNames: Record<string, Position> = {
11
- before: Position.Before,
12
- after: Position.After,
13
- inside: Position.Inside,
14
- none: Position.None,
15
- };
2
+ import { Position } from "./position";
16
3
 
17
4
  type IterateCallback = (node: Node, level: number) => boolean;
18
5
 
19
- export const getPositionName = (position: Position): string => {
20
- for (const name in positionNames) {
21
- if (Object.prototype.hasOwnProperty.call(positionNames, name)) {
22
- if (positionNames[name] === position) {
23
- return name;
24
- }
25
- }
26
- }
27
-
28
- return "";
29
- };
30
-
31
- export const getPosition = (name: string): Position | undefined =>
32
- positionNames[name];
33
-
34
6
  export class Node implements INode {
35
7
  public id?: NodeId;
36
8
  public name: string;
@@ -0,0 +1,32 @@
1
+ import { DropHint } from "../dragAndDropHandler/types";
2
+
3
+ class BorderDropHint implements DropHint {
4
+ private hint?: HTMLElement;
5
+
6
+ constructor(element: HTMLElement, scrollLeft: number) {
7
+ const div = element.querySelector(":scope > .jqtree-element");
8
+
9
+ if (!div) {
10
+ this.hint = undefined;
11
+ return;
12
+ }
13
+
14
+ const width = Math.max(element.offsetWidth + scrollLeft - 4, 0);
15
+ const height = Math.max(element.clientHeight - 4, 0);
16
+
17
+ const hint = document.createElement("span");
18
+ hint.className = "jqtree-border";
19
+ hint.style.width = `${width}px`;
20
+ hint.style.height = `${height}px`;
21
+
22
+ this.hint = hint;
23
+
24
+ div.append(this.hint);
25
+ }
26
+
27
+ public remove(): void {
28
+ this.hint?.remove();
29
+ }
30
+ }
31
+
32
+ export default BorderDropHint;
@@ -0,0 +1,133 @@
1
+ import { Position } from "../position";
2
+ import NodeElement, { NodeElementParams } from "./index";
3
+ import { OnFinishOpenNode, TriggerEvent } from "../jqtreeMethodTypes";
4
+
5
+ interface FolderElementParams extends NodeElementParams {
6
+ closedIconElement?: HTMLElement | Text;
7
+ openedIconElement?: HTMLElement | Text;
8
+ triggerEvent: TriggerEvent;
9
+ }
10
+
11
+ class FolderElement extends NodeElement {
12
+ private closedIconElement?: HTMLElement | Text;
13
+ private openedIconElement?: HTMLElement | Text;
14
+ private triggerEvent: TriggerEvent;
15
+
16
+ constructor({
17
+ closedIconElement,
18
+ getScrollLeft,
19
+ node,
20
+ openedIconElement,
21
+ tabIndex,
22
+ $treeElement,
23
+ triggerEvent,
24
+ }: FolderElementParams) {
25
+ super({
26
+ getScrollLeft,
27
+ node,
28
+ tabIndex,
29
+ $treeElement,
30
+ });
31
+
32
+ this.closedIconElement = closedIconElement;
33
+ this.openedIconElement = openedIconElement;
34
+ this.triggerEvent = triggerEvent;
35
+ }
36
+
37
+ public open(
38
+ onFinished: OnFinishOpenNode | undefined,
39
+ slide = true,
40
+ animationSpeed: JQuery.Duration = "fast",
41
+ ): void {
42
+ if (this.node.is_open) {
43
+ return;
44
+ }
45
+
46
+ this.node.is_open = true;
47
+
48
+ const button = this.getButton();
49
+ button.classList.remove("jqtree-closed");
50
+ button.innerHTML = "";
51
+
52
+ const openedIconElement = this.openedIconElement;
53
+
54
+ if (openedIconElement) {
55
+ const icon = openedIconElement.cloneNode(true);
56
+ button.appendChild(icon);
57
+ }
58
+
59
+ const doOpen = (): void => {
60
+ this.element.classList.remove("jqtree-closed");
61
+
62
+ const titleSpan = this.getTitleSpan();
63
+ titleSpan.setAttribute("aria-expanded", "true");
64
+
65
+ if (onFinished) {
66
+ onFinished(this.node);
67
+ }
68
+
69
+ this.triggerEvent("tree.open", {
70
+ node: this.node,
71
+ });
72
+ };
73
+
74
+ if (slide) {
75
+ jQuery(this.getUl()).slideDown(animationSpeed, doOpen);
76
+ } else {
77
+ jQuery(this.getUl()).show();
78
+ doOpen();
79
+ }
80
+ }
81
+
82
+ public close(
83
+ slide = true,
84
+ animationSpeed: JQuery.Duration | undefined = "fast",
85
+ ): void {
86
+ if (!this.node.is_open) {
87
+ return;
88
+ }
89
+
90
+ this.node.is_open = false;
91
+
92
+ const button = this.getButton();
93
+ button.classList.add("jqtree-closed");
94
+ button.innerHTML = "";
95
+
96
+ const closedIconElement = this.closedIconElement;
97
+
98
+ if (closedIconElement) {
99
+ const icon = closedIconElement.cloneNode(true);
100
+ button.appendChild(icon);
101
+ }
102
+
103
+ const doClose = (): void => {
104
+ this.element.classList.add("jqtree-closed");
105
+
106
+ const titleSpan = this.getTitleSpan();
107
+ titleSpan.setAttribute("aria-expanded", "false");
108
+
109
+ this.triggerEvent("tree.close", {
110
+ node: this.node,
111
+ });
112
+ };
113
+
114
+ if (slide) {
115
+ jQuery(this.getUl()).slideUp(animationSpeed, doClose);
116
+ } else {
117
+ jQuery(this.getUl()).hide();
118
+ doClose();
119
+ }
120
+ }
121
+
122
+ protected mustShowBorderDropHint(position: Position): boolean {
123
+ return !this.node.is_open && position === Position.Inside;
124
+ }
125
+
126
+ private getButton(): HTMLLinkElement {
127
+ return this.element.querySelector(
128
+ ":scope > .jqtree-element > a.jqtree-toggler",
129
+ ) as HTMLLinkElement;
130
+ }
131
+ }
132
+
133
+ export default FolderElement;
@@ -0,0 +1,69 @@
1
+ import { Node } from "../node";
2
+ import { Position } from "../position";
3
+ import { DropHint } from "../dragAndDropHandler/types";
4
+
5
+ class GhostDropHint implements DropHint {
6
+ private element: HTMLElement;
7
+ private node: Node;
8
+ private ghost: HTMLElement;
9
+
10
+ constructor(node: Node, element: HTMLElement, position: Position) {
11
+ this.element = element;
12
+ this.node = node;
13
+ this.ghost = this.createGhostElement();
14
+
15
+ if (position === Position.After) {
16
+ this.moveAfter();
17
+ } else if (position === Position.Before) {
18
+ this.moveBefore();
19
+ } else if (position === Position.Inside) {
20
+ if (node.isFolder() && node.is_open) {
21
+ this.moveInsideOpenFolder();
22
+ } else {
23
+ this.moveInside();
24
+ }
25
+ }
26
+ }
27
+
28
+ public remove(): void {
29
+ this.ghost.remove();
30
+ }
31
+
32
+ private moveAfter(): void {
33
+ this.element.after(this.ghost);
34
+ }
35
+
36
+ private moveBefore(): void {
37
+ this.element.before(this.ghost);
38
+ }
39
+
40
+ private moveInsideOpenFolder(): void {
41
+ const childElement = this.node.children[0]?.element;
42
+
43
+ if (childElement) {
44
+ childElement.before(this.ghost);
45
+ }
46
+ }
47
+
48
+ private moveInside(): void {
49
+ this.element.after(this.ghost);
50
+ this.ghost.classList.add("jqtree-inside");
51
+ }
52
+
53
+ private createGhostElement() {
54
+ const ghost = document.createElement("li");
55
+ ghost.className = "jqtree_common jqtree-ghost";
56
+
57
+ const circleSpan = document.createElement("span");
58
+ circleSpan.className = "jqtree_common jqtree-circle";
59
+ ghost.append(circleSpan);
60
+
61
+ const lineSpan = document.createElement("span");
62
+ lineSpan.className = "jqtree_common jqtree-line";
63
+ ghost.append(lineSpan);
64
+
65
+ return ghost;
66
+ }
67
+ }
68
+
69
+ export default GhostDropHint;