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.
- package/.eslintrc +13 -3
- package/.github/workflows/ci.yml +6 -6
- package/.github/workflows/codeql-analysis.yml +4 -4
- package/.github/workflows/size.yml +3 -3
- package/.github/workflows/static.yml +1 -1
- package/bower.json +1 -1
- package/config/babel.config.json +1 -1
- package/config/jest.config.js +4 -0
- package/config/jest.polyfills.js +14 -0
- package/config/production +2 -0
- package/devserver/devserver_scroll.js +8 -0
- package/devserver/test_index.html +9 -0
- package/devserver/test_scroll.html +28 -0
- package/devserver/test_scroll_container.html +39 -0
- package/docs/.ruby-version +1 -1
- package/docs/_config.yml +1 -1
- package/docs/_entries/general/changelog.md +11 -0
- package/docs/_entries/multiple_selection/get-selected-nodes.md +1 -1
- package/docs/_entries/node/getnextnode.md +3 -6
- package/docs/_entries/node/getnextsibling.md +1 -1
- package/docs/_entries/node/getnextvisiblenode.md +8 -5
- package/docs/_entries/node/getpreviousnode.md +12 -0
- package/docs/_entries/node/getprevioussibling.md +1 -1
- package/docs/_entries/node/getpreviousvisiblenode.md +6 -5
- package/package.json +35 -29
- package/src/dataLoader.ts +57 -34
- package/src/dragAndDropHandler/dragElement.ts +54 -0
- package/src/dragAndDropHandler/generateHitAreas.ts +176 -0
- package/src/dragAndDropHandler/index.ts +454 -0
- package/src/dragAndDropHandler/iterateVisibleNodes.ts +91 -0
- package/src/dragAndDropHandler/types.ts +13 -0
- package/src/elementsRenderer.ts +75 -40
- package/src/jqtreeMethodTypes.ts +40 -0
- package/src/jqtreeOptions.ts +43 -25
- package/src/keyHandler.ts +59 -30
- package/src/mouseHandler.ts +385 -0
- package/src/mouseUtils.ts +23 -0
- package/src/node.ts +1 -29
- package/src/nodeElement/borderDropHint.ts +32 -0
- package/src/nodeElement/folderElement.ts +133 -0
- package/src/nodeElement/ghostDropHint.ts +69 -0
- package/src/nodeElement/index.ts +102 -0
- package/src/playwright/coverage.ts +4 -7
- package/src/playwright/playwright.test.ts +150 -53
- package/src/playwright/testUtils.ts +28 -5
- package/src/position.ts +28 -0
- package/src/saveStateHandler.ts +75 -26
- package/src/scrollHandler/containerScrollParent.ts +13 -23
- package/src/scrollHandler/createScrollParent.ts +22 -22
- package/src/scrollHandler/documentScrollParent.ts +16 -13
- package/src/scrollHandler.ts +13 -15
- package/src/selectNodeHandler.ts +10 -16
- package/src/test/jqTree/events.test.ts +97 -30
- package/src/test/jqTree/keyboard.test.ts +18 -23
- package/src/test/jqTree/loadOnDemand.test.ts +22 -15
- package/src/test/jqTree/methods.test.ts +40 -14
- package/src/test/jqTree/mouse.test.ts +82 -0
- package/src/test/jqTree/options.test.ts +24 -12
- package/src/test/node.test.ts +3 -2
- package/src/test/{nodeUtil.test.ts → position.test.ts} +1 -1
- package/src/tree.jquery.ts +314 -208
- package/src/util.ts +12 -0
- package/src/version.ts +1 -1
- package/tree.jquery.debug.js +2594 -3419
- package/tree.jquery.debug.js.map +1 -1
- package/tree.jquery.js +3 -3
- package/tree.jquery.js.map +1 -1
- package/tsconfig.json +5 -3
- package/docs/_entries/functions/get-selected-nodes.md +0 -10
- package/lib/dataLoader.js +0 -123
- package/lib/dragAndDropHandler.js +0 -588
- package/lib/elementsRenderer.js +0 -267
- package/lib/jqtreeOptions.js +0 -1
- package/lib/keyHandler.js +0 -111
- package/lib/mouse.widget.js +0 -255
- package/lib/node.js +0 -708
- package/lib/nodeElement.js +0 -274
- package/lib/nodeUtils.js +0 -10
- package/lib/playwright/coverage.js +0 -99
- package/lib/playwright/playwright.test.js +0 -606
- package/lib/playwright/testUtils.js +0 -210
- package/lib/saveStateHandler.js +0 -277
- package/lib/scrollHandler/containerScrollParent.js +0 -160
- package/lib/scrollHandler/createScrollParent.js +0 -57
- package/lib/scrollHandler/documentScrollParent.js +0 -169
- package/lib/scrollHandler/scrollParent.js +0 -58
- package/lib/scrollHandler/types.js +0 -1
- package/lib/scrollHandler.js +0 -71
- package/lib/selectNodeHandler.js +0 -128
- package/lib/simple.widget.js +0 -158
- package/lib/test/global.d.js +0 -3
- package/lib/test/jqTree/accessibility.test.js +0 -37
- package/lib/test/jqTree/create.test.js +0 -48
- package/lib/test/jqTree/events.test.js +0 -210
- package/lib/test/jqTree/keyboard.test.js +0 -225
- package/lib/test/jqTree/loadOnDemand.test.js +0 -218
- package/lib/test/jqTree/methods.test.js +0 -1348
- package/lib/test/jqTree/options.test.js +0 -548
- package/lib/test/jqTree/scrollHandler/containerScrollParent.test.js +0 -94
- package/lib/test/node.test.js +0 -1202
- package/lib/test/nodeUtil.test.js +0 -27
- package/lib/test/nodeUtils.test.js +0 -20
- package/lib/test/support/exampleData.js +0 -35
- package/lib/test/support/jqTreeMatchers.js +0 -70
- package/lib/test/support/matchers.d.js +0 -1
- package/lib/test/support/setupTests.js +0 -7
- package/lib/test/support/testUtil.js +0 -29
- package/lib/test/support/treeStructure.js +0 -38
- package/lib/test/util.test.js +0 -26
- package/lib/tree.jquery.d.js +0 -1
- package/lib/tree.jquery.js +0 -1105
- package/lib/types.js +0 -1
- package/lib/typings.d.js +0 -2
- package/lib/util.js +0 -15
- package/lib/version.js +0 -8
- package/src/dragAndDropHandler.ts +0 -713
- package/src/mouse.widget.ts +0 -266
- package/src/nodeElement.ts +0 -272
- 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;
|