jqtree 1.7.5 → 1.8.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/bower.json +1 -1
- package/config/babel.config.json +1 -1
- package/config/production +2 -0
- package/devserver/devserver_scroll.js +8 -0
- package/devserver/test_scroll.html +28 -0
- package/devserver/test_scroll_container.html +39 -0
- package/docs/_config.yml +1 -1
- package/docs/_entries/general/changelog.md +7 -0
- package/package.json +5 -1
- package/src/dataLoader.ts +44 -19
- package/src/dragAndDropHandler/dragElement.ts +42 -0
- package/src/dragAndDropHandler/hitAreasGenerator.ts +175 -0
- package/src/dragAndDropHandler/index.ts +470 -0
- package/src/dragAndDropHandler/types.ts +12 -0
- package/src/dragAndDropHandler/visibleNodeIterator.ts +97 -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/mouse.widget.ts +3 -3
- package/src/mouseWidgetTypes.ts +6 -0
- package/src/nodeElement/borderDropHint.ts +32 -0
- package/src/nodeElement/folderElement.ts +133 -0
- package/src/nodeElement/ghostDropHint.ts +68 -0
- package/src/nodeElement/index.ts +101 -0
- package/src/playwright/coverage.ts +1 -4
- package/src/playwright/playwright.test.ts +0 -4
- package/src/saveStateHandler.ts +75 -26
- package/src/scrollHandler.ts +13 -7
- package/src/selectNodeHandler.ts +10 -16
- package/src/test/jqTree/keyboard.test.ts +18 -23
- package/src/test/jqTree/methods.test.ts +32 -3
- package/src/test/jqTree/options.test.ts +15 -4
- package/src/test/node.test.ts +1 -1
- package/src/tree.jquery.ts +239 -57
- package/src/util.ts +3 -0
- package/src/version.ts +1 -1
- package/tree.jquery.debug.js +1728 -2576
- package/tree.jquery.debug.js.map +1 -1
- package/tree.jquery.js +2 -2
- package/tree.jquery.js.map +1 -1
- 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/nodeElement.ts +0 -272
- package/src/types.ts +0 -19
|
@@ -1,713 +0,0 @@
|
|
|
1
|
-
import { getPositionName, Node, Position } from "./node";
|
|
2
|
-
import { DropHint, HitArea, PositionInfo } from "./types";
|
|
3
|
-
import { NodeElement } from "./nodeElement";
|
|
4
|
-
import { JqTreeWidget } from "./tree.jquery";
|
|
5
|
-
|
|
6
|
-
interface Dimensions {
|
|
7
|
-
left: number;
|
|
8
|
-
top: number;
|
|
9
|
-
right: number;
|
|
10
|
-
bottom: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class DragAndDropHandler {
|
|
14
|
-
public hitAreas: HitArea[];
|
|
15
|
-
public isDragging: boolean;
|
|
16
|
-
public currentItem: NodeElement | null;
|
|
17
|
-
public hoveredArea: HitArea | null;
|
|
18
|
-
|
|
19
|
-
private treeWidget: JqTreeWidget;
|
|
20
|
-
private dragElement: DragElement | null;
|
|
21
|
-
private previousGhost: DropHint | null;
|
|
22
|
-
private openFolderTimer: number | null;
|
|
23
|
-
|
|
24
|
-
constructor(treeWidget: JqTreeWidget) {
|
|
25
|
-
this.treeWidget = treeWidget;
|
|
26
|
-
|
|
27
|
-
this.hoveredArea = null;
|
|
28
|
-
this.hitAreas = [];
|
|
29
|
-
this.isDragging = false;
|
|
30
|
-
this.currentItem = null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public mouseCapture(positionInfo: PositionInfo): boolean | null {
|
|
34
|
-
const $element = jQuery(positionInfo.target);
|
|
35
|
-
|
|
36
|
-
if (!this.mustCaptureElement($element)) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
this.treeWidget.options.onIsMoveHandle &&
|
|
42
|
-
!this.treeWidget.options.onIsMoveHandle($element)
|
|
43
|
-
) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let nodeElement = this.treeWidget._getNodeElement($element);
|
|
48
|
-
|
|
49
|
-
if (nodeElement && this.treeWidget.options.onCanMove) {
|
|
50
|
-
if (!this.treeWidget.options.onCanMove(nodeElement.node)) {
|
|
51
|
-
nodeElement = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
this.currentItem = nodeElement;
|
|
56
|
-
return this.currentItem != null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public mouseStart(positionInfo: PositionInfo): boolean {
|
|
60
|
-
if (
|
|
61
|
-
!this.currentItem ||
|
|
62
|
-
positionInfo.pageX === undefined ||
|
|
63
|
-
positionInfo.pageY === undefined
|
|
64
|
-
) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
this.refresh();
|
|
69
|
-
|
|
70
|
-
const offset = jQuery(positionInfo.target).offset();
|
|
71
|
-
const left = offset ? offset.left : 0;
|
|
72
|
-
const top = offset ? offset.top : 0;
|
|
73
|
-
|
|
74
|
-
const node = this.currentItem.node;
|
|
75
|
-
|
|
76
|
-
this.dragElement = new DragElement(
|
|
77
|
-
node.name,
|
|
78
|
-
positionInfo.pageX - left,
|
|
79
|
-
positionInfo.pageY - top,
|
|
80
|
-
this.treeWidget.element,
|
|
81
|
-
this.treeWidget.options.autoEscape ?? true,
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
this.isDragging = true;
|
|
85
|
-
this.currentItem.$element.addClass("jqtree-moving");
|
|
86
|
-
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
public mouseDrag(positionInfo: PositionInfo): boolean {
|
|
91
|
-
if (
|
|
92
|
-
!this.currentItem ||
|
|
93
|
-
!this.dragElement ||
|
|
94
|
-
positionInfo.pageX === undefined ||
|
|
95
|
-
positionInfo.pageY === undefined
|
|
96
|
-
) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this.dragElement.move(positionInfo.pageX, positionInfo.pageY);
|
|
101
|
-
|
|
102
|
-
const area = this.findHoveredArea(
|
|
103
|
-
positionInfo.pageX,
|
|
104
|
-
positionInfo.pageY,
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
if (area && this.canMoveToArea(area)) {
|
|
108
|
-
if (!area.node.isFolder()) {
|
|
109
|
-
this.stopOpenFolderTimer();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (this.hoveredArea !== area) {
|
|
113
|
-
this.hoveredArea = area;
|
|
114
|
-
|
|
115
|
-
// If this is a closed folder, start timer to open it
|
|
116
|
-
if (this.mustOpenFolderTimer(area)) {
|
|
117
|
-
this.startOpenFolderTimer(area.node);
|
|
118
|
-
} else {
|
|
119
|
-
this.stopOpenFolderTimer();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.updateDropHint();
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
this.removeDropHint();
|
|
126
|
-
this.stopOpenFolderTimer();
|
|
127
|
-
this.hoveredArea = area;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!area) {
|
|
131
|
-
if (this.treeWidget.options.onDragMove) {
|
|
132
|
-
this.treeWidget.options.onDragMove(
|
|
133
|
-
this.currentItem.node,
|
|
134
|
-
positionInfo.originalEvent,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
public mouseStop(positionInfo: PositionInfo): boolean {
|
|
143
|
-
this.moveItem(positionInfo);
|
|
144
|
-
this.clear();
|
|
145
|
-
this.removeHover();
|
|
146
|
-
this.removeDropHint();
|
|
147
|
-
this.removeHitAreas();
|
|
148
|
-
|
|
149
|
-
const currentItem = this.currentItem;
|
|
150
|
-
|
|
151
|
-
if (this.currentItem) {
|
|
152
|
-
this.currentItem.$element.removeClass("jqtree-moving");
|
|
153
|
-
this.currentItem = null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
this.isDragging = false;
|
|
157
|
-
|
|
158
|
-
if (!this.hoveredArea && currentItem) {
|
|
159
|
-
if (this.treeWidget.options.onDragStop) {
|
|
160
|
-
this.treeWidget.options.onDragStop(
|
|
161
|
-
currentItem.node,
|
|
162
|
-
positionInfo.originalEvent,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
public refresh(): void {
|
|
171
|
-
this.removeHitAreas();
|
|
172
|
-
|
|
173
|
-
if (this.currentItem) {
|
|
174
|
-
this.generateHitAreas();
|
|
175
|
-
|
|
176
|
-
this.currentItem = this.treeWidget._getNodeElementForNode(
|
|
177
|
-
this.currentItem.node,
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
if (this.isDragging) {
|
|
181
|
-
this.currentItem.$element.addClass("jqtree-moving");
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private generateHitAreas(): void {
|
|
187
|
-
if (!this.currentItem) {
|
|
188
|
-
this.hitAreas = [];
|
|
189
|
-
} else {
|
|
190
|
-
const hitAreasGenerator = new HitAreasGenerator(
|
|
191
|
-
this.treeWidget.tree,
|
|
192
|
-
this.currentItem.node,
|
|
193
|
-
this.getTreeDimensions().bottom,
|
|
194
|
-
);
|
|
195
|
-
this.hitAreas = hitAreasGenerator.generate();
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private mustCaptureElement($element: JQuery<HTMLElement>): boolean {
|
|
200
|
-
return !$element.is("input,select,textarea");
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private canMoveToArea(area: HitArea): boolean {
|
|
204
|
-
if (!this.treeWidget.options.onCanMoveTo) {
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (!this.currentItem) {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const positionName = getPositionName(area.position);
|
|
213
|
-
|
|
214
|
-
return this.treeWidget.options.onCanMoveTo(
|
|
215
|
-
this.currentItem.node,
|
|
216
|
-
area.node,
|
|
217
|
-
positionName,
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private removeHitAreas(): void {
|
|
222
|
-
this.hitAreas = [];
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
private clear(): void {
|
|
226
|
-
if (this.dragElement) {
|
|
227
|
-
this.dragElement.remove();
|
|
228
|
-
this.dragElement = null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
private removeDropHint(): void {
|
|
233
|
-
if (this.previousGhost) {
|
|
234
|
-
this.previousGhost.remove();
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private removeHover(): void {
|
|
239
|
-
this.hoveredArea = null;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
private findHoveredArea(x: number, y: number): HitArea | null {
|
|
243
|
-
const dimensions = this.getTreeDimensions();
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
x < dimensions.left ||
|
|
247
|
-
y < dimensions.top ||
|
|
248
|
-
x > dimensions.right ||
|
|
249
|
-
y > dimensions.bottom
|
|
250
|
-
) {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
let low = 0;
|
|
255
|
-
let high = this.hitAreas.length;
|
|
256
|
-
while (low < high) {
|
|
257
|
-
const mid = (low + high) >> 1;
|
|
258
|
-
const area = this.hitAreas[mid];
|
|
259
|
-
|
|
260
|
-
if (!area) {
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (y < area.top) {
|
|
265
|
-
high = mid;
|
|
266
|
-
} else if (y > area.bottom) {
|
|
267
|
-
low = mid + 1;
|
|
268
|
-
} else {
|
|
269
|
-
return area;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
private mustOpenFolderTimer(area: HitArea): boolean {
|
|
277
|
-
const node = area.node;
|
|
278
|
-
|
|
279
|
-
return (
|
|
280
|
-
node.isFolder() &&
|
|
281
|
-
!node.is_open &&
|
|
282
|
-
area.position === Position.Inside
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
private updateDropHint(): void {
|
|
287
|
-
if (!this.hoveredArea) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// remove previous drop hint
|
|
292
|
-
this.removeDropHint();
|
|
293
|
-
|
|
294
|
-
// add new drop hint
|
|
295
|
-
const nodeElement = this.treeWidget._getNodeElementForNode(
|
|
296
|
-
this.hoveredArea.node,
|
|
297
|
-
);
|
|
298
|
-
this.previousGhost = nodeElement.addDropHint(this.hoveredArea.position);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
private startOpenFolderTimer(folder: Node): void {
|
|
302
|
-
const openFolder = (): void => {
|
|
303
|
-
this.treeWidget._openNode(
|
|
304
|
-
folder,
|
|
305
|
-
this.treeWidget.options.slide,
|
|
306
|
-
() => {
|
|
307
|
-
this.refresh();
|
|
308
|
-
this.updateDropHint();
|
|
309
|
-
},
|
|
310
|
-
);
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
this.stopOpenFolderTimer();
|
|
314
|
-
|
|
315
|
-
const openFolderDelay = this.treeWidget.options.openFolderDelay;
|
|
316
|
-
|
|
317
|
-
if (openFolderDelay !== false) {
|
|
318
|
-
this.openFolderTimer = window.setTimeout(
|
|
319
|
-
openFolder,
|
|
320
|
-
openFolderDelay,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
private stopOpenFolderTimer(): void {
|
|
326
|
-
if (this.openFolderTimer) {
|
|
327
|
-
clearTimeout(this.openFolderTimer);
|
|
328
|
-
this.openFolderTimer = null;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
private moveItem(positionInfo: PositionInfo): void {
|
|
333
|
-
if (
|
|
334
|
-
this.currentItem &&
|
|
335
|
-
this.hoveredArea &&
|
|
336
|
-
this.hoveredArea.position !== Position.None &&
|
|
337
|
-
this.canMoveToArea(this.hoveredArea)
|
|
338
|
-
) {
|
|
339
|
-
const movedNode = this.currentItem.node;
|
|
340
|
-
const targetNode = this.hoveredArea.node;
|
|
341
|
-
const position = this.hoveredArea.position;
|
|
342
|
-
const previousParent = movedNode.parent;
|
|
343
|
-
|
|
344
|
-
if (position === Position.Inside) {
|
|
345
|
-
this.hoveredArea.node.is_open = true;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const doMove = (): void => {
|
|
349
|
-
this.treeWidget.tree.moveNode(movedNode, targetNode, position);
|
|
350
|
-
this.treeWidget.element.empty();
|
|
351
|
-
this.treeWidget._refreshElements(null);
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
const event = this.treeWidget._triggerEvent("tree.move", {
|
|
355
|
-
move_info: {
|
|
356
|
-
moved_node: movedNode,
|
|
357
|
-
target_node: targetNode,
|
|
358
|
-
position: getPositionName(position),
|
|
359
|
-
previous_parent: previousParent,
|
|
360
|
-
do_move: doMove,
|
|
361
|
-
original_event: positionInfo.originalEvent,
|
|
362
|
-
},
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
if (!event.isDefaultPrevented()) {
|
|
366
|
-
doMove();
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private getTreeDimensions(): Dimensions {
|
|
372
|
-
// Return the dimensions of the tree. Add a margin to the bottom to allow
|
|
373
|
-
// to drag-and-drop after the last element.
|
|
374
|
-
const offset = this.treeWidget.element.offset();
|
|
375
|
-
|
|
376
|
-
if (!offset) {
|
|
377
|
-
return { left: 0, top: 0, right: 0, bottom: 0 };
|
|
378
|
-
} else {
|
|
379
|
-
const el = this.treeWidget.element;
|
|
380
|
-
const width = el.width() || 0;
|
|
381
|
-
const height = el.height() || 0;
|
|
382
|
-
const left = offset.left + this.treeWidget._getScrollLeft();
|
|
383
|
-
|
|
384
|
-
return {
|
|
385
|
-
left,
|
|
386
|
-
top: offset.top,
|
|
387
|
-
right: left + width,
|
|
388
|
-
bottom: offset.top + height + 16,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
abstract class VisibleNodeIterator {
|
|
395
|
-
private tree: Node;
|
|
396
|
-
|
|
397
|
-
constructor(tree: Node) {
|
|
398
|
-
this.tree = tree;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
protected iterate(): void {
|
|
402
|
-
let isFirstNode = true;
|
|
403
|
-
|
|
404
|
-
const _iterateNode = (node: Node, nextNode: Node | null): void => {
|
|
405
|
-
let mustIterateInside =
|
|
406
|
-
(node.is_open || !node.element) && node.hasChildren();
|
|
407
|
-
|
|
408
|
-
let $element: JQuery<HTMLElement> | null = null;
|
|
409
|
-
|
|
410
|
-
if (node.element) {
|
|
411
|
-
$element = jQuery(node.element);
|
|
412
|
-
|
|
413
|
-
if (!$element.is(":visible")) {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (isFirstNode) {
|
|
418
|
-
this.handleFirstNode(node);
|
|
419
|
-
isFirstNode = false;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (!node.hasChildren()) {
|
|
423
|
-
this.handleNode(node, nextNode, $element);
|
|
424
|
-
} else if (node.is_open) {
|
|
425
|
-
if (!this.handleOpenFolder(node, $element)) {
|
|
426
|
-
mustIterateInside = false;
|
|
427
|
-
}
|
|
428
|
-
} else {
|
|
429
|
-
this.handleClosedFolder(node, nextNode, $element);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (mustIterateInside) {
|
|
434
|
-
const childrenLength = node.children.length;
|
|
435
|
-
node.children.forEach((_, i) => {
|
|
436
|
-
const child = node.children[i];
|
|
437
|
-
|
|
438
|
-
if (child) {
|
|
439
|
-
if (i === childrenLength - 1) {
|
|
440
|
-
_iterateNode(child, null);
|
|
441
|
-
} else {
|
|
442
|
-
const nextChild = node.children[i + 1];
|
|
443
|
-
|
|
444
|
-
if (nextChild) {
|
|
445
|
-
_iterateNode(child, nextChild);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
if (node.is_open && $element) {
|
|
452
|
-
this.handleAfterOpenFolder(node, nextNode);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
_iterateNode(this.tree, null);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
protected abstract handleNode(
|
|
461
|
-
node: Node,
|
|
462
|
-
nextNode: Node | null,
|
|
463
|
-
$element: JQuery,
|
|
464
|
-
): void;
|
|
465
|
-
|
|
466
|
-
/*
|
|
467
|
-
override
|
|
468
|
-
return
|
|
469
|
-
- true: continue iterating
|
|
470
|
-
- false: stop iterating
|
|
471
|
-
*/
|
|
472
|
-
protected abstract handleOpenFolder(node: Node, $element: JQuery): boolean;
|
|
473
|
-
|
|
474
|
-
protected abstract handleClosedFolder(
|
|
475
|
-
node: Node,
|
|
476
|
-
nextNode: Node | null,
|
|
477
|
-
$element: JQuery,
|
|
478
|
-
): void;
|
|
479
|
-
|
|
480
|
-
protected abstract handleAfterOpenFolder(
|
|
481
|
-
node: Node,
|
|
482
|
-
nextNode: Node | null,
|
|
483
|
-
): void;
|
|
484
|
-
|
|
485
|
-
protected abstract handleFirstNode(node: Node): void;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
export class HitAreasGenerator extends VisibleNodeIterator {
|
|
489
|
-
private currentNode: Node;
|
|
490
|
-
private treeBottom: number;
|
|
491
|
-
private positions: HitArea[];
|
|
492
|
-
private lastTop: number;
|
|
493
|
-
|
|
494
|
-
constructor(tree: Node, currentNode: Node, treeBottom: number) {
|
|
495
|
-
super(tree);
|
|
496
|
-
|
|
497
|
-
this.currentNode = currentNode;
|
|
498
|
-
this.treeBottom = treeBottom;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
public generate(): HitArea[] {
|
|
502
|
-
this.positions = [];
|
|
503
|
-
this.lastTop = 0;
|
|
504
|
-
|
|
505
|
-
this.iterate();
|
|
506
|
-
|
|
507
|
-
return this.generateHitAreas(this.positions);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
protected generateHitAreas(positions: HitArea[]): HitArea[] {
|
|
511
|
-
let previousTop = positions[0]?.top ?? 0;
|
|
512
|
-
let group = [];
|
|
513
|
-
const hitAreas: HitArea[] = [];
|
|
514
|
-
|
|
515
|
-
for (const position of positions) {
|
|
516
|
-
if (position.top !== previousTop && group.length) {
|
|
517
|
-
this.generateHitAreasForGroup(
|
|
518
|
-
hitAreas,
|
|
519
|
-
group,
|
|
520
|
-
previousTop,
|
|
521
|
-
position.top,
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
previousTop = position.top;
|
|
525
|
-
group = [];
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
group.push(position);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
this.generateHitAreasForGroup(
|
|
532
|
-
hitAreas,
|
|
533
|
-
group,
|
|
534
|
-
previousTop,
|
|
535
|
-
this.treeBottom,
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
return hitAreas;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
protected handleOpenFolder(node: Node, $element: JQuery): boolean {
|
|
542
|
-
if (node === this.currentNode) {
|
|
543
|
-
// Cannot move inside current item
|
|
544
|
-
|
|
545
|
-
// Dnd over the current element is not possible: add a position with type None for the top and the bottom.
|
|
546
|
-
const top = this.getTop($element);
|
|
547
|
-
const height = $element.height() || 0;
|
|
548
|
-
this.addPosition(node, Position.None, top);
|
|
549
|
-
|
|
550
|
-
if (height > 5) {
|
|
551
|
-
// Subtract 5 pixels to allow more space for the next element.
|
|
552
|
-
this.addPosition(node, Position.None, top + height - 5);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// Stop iterating
|
|
556
|
-
return false;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Cannot move before current item
|
|
560
|
-
if (node.children[0] !== this.currentNode) {
|
|
561
|
-
this.addPosition(node, Position.Inside, this.getTop($element));
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Continue iterating
|
|
565
|
-
return true;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
protected handleClosedFolder(
|
|
569
|
-
node: Node,
|
|
570
|
-
nextNode: Node,
|
|
571
|
-
$element: JQuery,
|
|
572
|
-
): void {
|
|
573
|
-
const top = this.getTop($element);
|
|
574
|
-
|
|
575
|
-
if (node === this.currentNode) {
|
|
576
|
-
// Cannot move after current item
|
|
577
|
-
this.addPosition(node, Position.None, top);
|
|
578
|
-
} else {
|
|
579
|
-
this.addPosition(node, Position.Inside, top);
|
|
580
|
-
|
|
581
|
-
// Cannot move before current item
|
|
582
|
-
if (nextNode !== this.currentNode) {
|
|
583
|
-
this.addPosition(node, Position.After, top);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
protected handleFirstNode(node: Node): void {
|
|
589
|
-
if (node !== this.currentNode) {
|
|
590
|
-
this.addPosition(
|
|
591
|
-
node,
|
|
592
|
-
Position.Before,
|
|
593
|
-
this.getTop(jQuery(node.element)),
|
|
594
|
-
);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
protected handleAfterOpenFolder(node: Node, nextNode: Node): void {
|
|
599
|
-
if (node === this.currentNode || nextNode === this.currentNode) {
|
|
600
|
-
// Cannot move before or after current item
|
|
601
|
-
this.addPosition(node, Position.None, this.lastTop);
|
|
602
|
-
} else {
|
|
603
|
-
this.addPosition(node, Position.After, this.lastTop);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
protected handleNode(node: Node, nextNode: Node, $element: JQuery): void {
|
|
608
|
-
const top = this.getTop($element);
|
|
609
|
-
|
|
610
|
-
if (node === this.currentNode) {
|
|
611
|
-
// Cannot move inside current item
|
|
612
|
-
this.addPosition(node, Position.None, top);
|
|
613
|
-
} else {
|
|
614
|
-
this.addPosition(node, Position.Inside, top);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if (nextNode === this.currentNode || node === this.currentNode) {
|
|
618
|
-
// Cannot move before or after current item
|
|
619
|
-
this.addPosition(node, Position.None, top);
|
|
620
|
-
} else {
|
|
621
|
-
this.addPosition(node, Position.After, top);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
private getTop($element: JQuery<HTMLElement>): number {
|
|
626
|
-
const offset = $element.offset();
|
|
627
|
-
|
|
628
|
-
return offset ? offset.top : 0;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
private addPosition(node: Node, position: number, top: number): void {
|
|
632
|
-
const area = {
|
|
633
|
-
top,
|
|
634
|
-
bottom: 0,
|
|
635
|
-
node,
|
|
636
|
-
position,
|
|
637
|
-
};
|
|
638
|
-
|
|
639
|
-
this.positions.push(area);
|
|
640
|
-
this.lastTop = top;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
private generateHitAreasForGroup(
|
|
644
|
-
hitAreas: HitArea[],
|
|
645
|
-
positionsInGroup: HitArea[],
|
|
646
|
-
top: number,
|
|
647
|
-
bottom: number,
|
|
648
|
-
): void {
|
|
649
|
-
// limit positions in group
|
|
650
|
-
const positionCount = Math.min(positionsInGroup.length, 4);
|
|
651
|
-
|
|
652
|
-
const areaHeight = Math.round((bottom - top) / positionCount);
|
|
653
|
-
let areaTop = top;
|
|
654
|
-
|
|
655
|
-
let i = 0;
|
|
656
|
-
while (i < positionCount) {
|
|
657
|
-
const position = positionsInGroup[i];
|
|
658
|
-
|
|
659
|
-
if (position && position.position !== Position.None) {
|
|
660
|
-
hitAreas.push({
|
|
661
|
-
top: areaTop,
|
|
662
|
-
bottom: areaTop + areaHeight,
|
|
663
|
-
node: position.node,
|
|
664
|
-
position: position.position,
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
areaTop += areaHeight;
|
|
669
|
-
i += 1;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
class DragElement {
|
|
675
|
-
private offsetX: number;
|
|
676
|
-
private offsetY: number;
|
|
677
|
-
private $element: JQuery;
|
|
678
|
-
|
|
679
|
-
constructor(
|
|
680
|
-
nodeName: string,
|
|
681
|
-
offsetX: number,
|
|
682
|
-
offsetY: number,
|
|
683
|
-
$tree: JQuery,
|
|
684
|
-
autoEscape: boolean,
|
|
685
|
-
) {
|
|
686
|
-
this.offsetX = offsetX;
|
|
687
|
-
this.offsetY = offsetY;
|
|
688
|
-
|
|
689
|
-
this.$element = jQuery("<span>").addClass(
|
|
690
|
-
"jqtree-title jqtree-dragging",
|
|
691
|
-
);
|
|
692
|
-
|
|
693
|
-
if (autoEscape) {
|
|
694
|
-
this.$element.text(nodeName);
|
|
695
|
-
} else {
|
|
696
|
-
this.$element.html(nodeName);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
this.$element.css("position", "absolute");
|
|
700
|
-
$tree.append(this.$element);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
public move(pageX: number, pageY: number): void {
|
|
704
|
-
this.$element.offset({
|
|
705
|
-
left: pageX - this.offsetX,
|
|
706
|
-
top: pageY - this.offsetY,
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
public remove(): void {
|
|
711
|
-
this.$element.remove();
|
|
712
|
-
}
|
|
713
|
-
}
|