jqtree 1.8.8 → 1.8.10

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.
@@ -14,8 +14,8 @@ import {
14
14
  import { PositionInfo } from "../mouseUtils";
15
15
  import { Node } from "../node";
16
16
  import NodeElement from "../nodeElement";
17
- import { getPositionName, Position } from "../position";
18
17
  import { getElementPosition } from "../util";
18
+ import binarySearch from "./binarySearch";
19
19
  import DragElement from "./dragElement";
20
20
  import generateHitAreas from "./generateHitAreas";
21
21
  import { DropHint, HitArea } from "./types";
@@ -27,9 +27,6 @@ interface Dimensions {
27
27
  top: number;
28
28
  }
29
29
 
30
- type GetNodeElement = (element: HTMLElement) => NodeElement | null;
31
- type GetNodeElementForNode = (node: Node) => NodeElement;
32
-
33
30
  interface DragAndDropHandlerParams {
34
31
  autoEscape?: boolean;
35
32
  getNodeElement: GetNodeElement;
@@ -48,13 +45,20 @@ interface DragAndDropHandlerParams {
48
45
  treeElement: HTMLElement;
49
46
  triggerEvent: TriggerEvent;
50
47
  }
48
+ type GetNodeElement = (element: HTMLElement) => NodeElement | null;
49
+
50
+ type GetNodeElementForNode = (node: Node) => NodeElement;
51
51
 
52
52
  export class DragAndDropHandler {
53
+ public currentItem: NodeElement | null;
54
+ public hitAreas: HitArea[];
55
+ public hoveredArea: HitArea | null;
56
+ public isDragging: boolean;
57
+
53
58
  private autoEscape?: boolean;
54
59
  private dragElement: DragElement | null;
55
60
  private getNodeElement: GetNodeElement;
56
61
  private getNodeElementForNode: GetNodeElementForNode;
57
-
58
62
  private getScrollLeft: GetScrollLeft;
59
63
  private getTree: GetTree;
60
64
  private onCanMove?: OnCanMove;
@@ -70,10 +74,6 @@ export class DragAndDropHandler {
70
74
  private slide: boolean;
71
75
  private treeElement: HTMLElement;
72
76
  private triggerEvent: TriggerEvent;
73
- public currentItem: NodeElement | null;
74
- public hitAreas: HitArea[];
75
- public hoveredArea: HitArea | null;
76
- public isDragging: boolean;
77
77
 
78
78
  constructor({
79
79
  autoEscape,
@@ -86,6 +86,7 @@ export class DragAndDropHandler {
86
86
  onDragMove,
87
87
  onDragStop,
88
88
  onIsMoveHandle,
89
+ openFolderDelay,
89
90
  openNode,
90
91
  refreshElements,
91
92
  slide,
@@ -102,6 +103,7 @@ export class DragAndDropHandler {
102
103
  this.onDragMove = onDragMove;
103
104
  this.onDragStop = onDragStop;
104
105
  this.onIsMoveHandle = onIsMoveHandle;
106
+ this.openFolderDelay = openFolderDelay;
105
107
  this.openNode = openNode;
106
108
  this.refreshElements = refreshElements;
107
109
  this.slide = slide;
@@ -114,18 +116,146 @@ export class DragAndDropHandler {
114
116
  this.currentItem = null;
115
117
  }
116
118
 
117
- private canMoveToArea(area: HitArea): boolean {
118
- if (!this.onCanMoveTo) {
119
- return true;
119
+ public mouseCapture(positionInfo: PositionInfo): boolean | null {
120
+ const element = positionInfo.target;
121
+
122
+ if (!this.mustCaptureElement(element)) {
123
+ return null;
124
+ }
125
+
126
+ if (this.onIsMoveHandle && !this.onIsMoveHandle(jQuery(element))) {
127
+ return null;
128
+ }
129
+
130
+ let nodeElement = this.getNodeElement(element);
131
+
132
+ if (nodeElement && this.onCanMove) {
133
+ if (!this.onCanMove(nodeElement.node)) {
134
+ nodeElement = null;
135
+ }
136
+ }
137
+
138
+ this.currentItem = nodeElement;
139
+ return this.currentItem != null;
140
+ }
141
+
142
+ public mouseDrag(positionInfo: PositionInfo): boolean {
143
+ if (!this.currentItem || !this.dragElement) {
144
+ return false;
120
145
  }
121
146
 
147
+ this.dragElement.move(positionInfo.pageX, positionInfo.pageY);
148
+
149
+ const area = this.findHoveredArea(
150
+ positionInfo.pageX,
151
+ positionInfo.pageY,
152
+ );
153
+
154
+ if (area && this.canMoveToArea(area, this.currentItem)) {
155
+ if (!area.node.isFolder()) {
156
+ this.stopOpenFolderTimer();
157
+ }
158
+
159
+ if (this.hoveredArea !== area) {
160
+ this.hoveredArea = area;
161
+
162
+ // If this is a closed folder, start timer to open it
163
+ if (this.mustOpenFolderTimer(area)) {
164
+ this.startOpenFolderTimer(area.node);
165
+ } else {
166
+ this.stopOpenFolderTimer();
167
+ }
168
+
169
+ this.updateDropHint();
170
+ }
171
+ } else {
172
+ this.removeDropHint();
173
+ this.stopOpenFolderTimer();
174
+ this.hoveredArea = area;
175
+ }
176
+
177
+ if (!area) {
178
+ if (this.onDragMove) {
179
+ this.onDragMove(
180
+ this.currentItem.node,
181
+ positionInfo.originalEvent,
182
+ );
183
+ }
184
+ }
185
+
186
+ return true;
187
+ }
188
+
189
+ public mouseStart(positionInfo: PositionInfo): boolean {
122
190
  if (!this.currentItem) {
123
191
  return false;
124
192
  }
125
193
 
126
- const positionName = getPositionName(area.position);
194
+ this.refresh();
195
+
196
+ const { left, top } = getElementPosition(positionInfo.target);
197
+
198
+ const node = this.currentItem.node;
199
+
200
+ this.dragElement = new DragElement({
201
+ autoEscape: this.autoEscape ?? true,
202
+ nodeName: node.name,
203
+ offsetX: positionInfo.pageX - left,
204
+ offsetY: positionInfo.pageY - top,
205
+ treeElement: this.treeElement,
206
+ });
127
207
 
128
- return this.onCanMoveTo(this.currentItem.node, area.node, positionName);
208
+ this.isDragging = true;
209
+ this.currentItem.element.classList.add("jqtree-moving");
210
+
211
+ return true;
212
+ }
213
+
214
+ public mouseStop(positionInfo: PositionInfo): boolean {
215
+ this.moveItem(positionInfo);
216
+ this.clear();
217
+ this.removeHover();
218
+ this.removeDropHint();
219
+ this.removeHitAreas();
220
+
221
+ const currentItem = this.currentItem;
222
+
223
+ if (this.currentItem) {
224
+ this.currentItem.element.classList.remove("jqtree-moving");
225
+ this.currentItem = null;
226
+ }
227
+
228
+ this.isDragging = false;
229
+
230
+ if (!this.hoveredArea && currentItem) {
231
+ if (this.onDragStop) {
232
+ this.onDragStop(currentItem.node, positionInfo.originalEvent);
233
+ }
234
+ }
235
+
236
+ return false;
237
+ }
238
+
239
+ public refresh(): void {
240
+ this.removeHitAreas();
241
+
242
+ if (this.currentItem) {
243
+ const currentNode = this.currentItem.node;
244
+ this.generateHitAreas(currentNode);
245
+ this.currentItem = this.getNodeElementForNode(currentNode);
246
+
247
+ if (this.isDragging) {
248
+ this.currentItem.element.classList.add("jqtree-moving");
249
+ }
250
+ }
251
+ }
252
+
253
+ private canMoveToArea(area: HitArea, currentItem: NodeElement): boolean {
254
+ if (!this.onCanMoveTo) {
255
+ return true;
256
+ }
257
+
258
+ return this.onCanMoveTo(currentItem.node, area.node, area.position);
129
259
  }
130
260
 
131
261
  private clear(): void {
@@ -147,37 +277,26 @@ export class DragAndDropHandler {
147
277
  return null;
148
278
  }
149
279
 
150
- let low = 0;
151
- let high = this.hitAreas.length;
152
- while (low < high) {
153
- const mid = (low + high) >> 1;
154
- const area = this.hitAreas[mid];
155
-
156
- if (!area) {
157
- return null;
158
- }
159
-
280
+ return binarySearch<HitArea>(this.hitAreas, (area) => {
160
281
  if (y < area.top) {
161
- high = mid;
282
+ return 1;
162
283
  } else if (y > area.bottom) {
163
- low = mid + 1;
284
+ return -1;
164
285
  } else {
165
- return area;
286
+ return 0;
166
287
  }
167
- }
168
-
169
- return null;
288
+ });
170
289
  }
171
290
 
172
- private generateHitAreas(): void {
291
+ private generateHitAreas(currentNode: Node): void {
173
292
  const tree = this.getTree();
174
293
 
175
- if (!this.currentItem || !tree) {
294
+ if (!tree) {
176
295
  this.hitAreas = [];
177
296
  } else {
178
297
  this.hitAreas = generateHitAreas(
179
298
  tree,
180
- this.currentItem.node,
299
+ currentNode,
181
300
  this.getTreeDimensions().bottom,
182
301
  );
183
302
  }
@@ -198,19 +317,19 @@ export class DragAndDropHandler {
198
317
  };
199
318
  }
200
319
 
320
+ /* Move the dragged node to the selected position in the tree. */
201
321
  private moveItem(positionInfo: PositionInfo): void {
202
322
  if (
203
323
  this.currentItem &&
204
- this.hoveredArea &&
205
- this.hoveredArea.position !== Position.None &&
206
- this.canMoveToArea(this.hoveredArea)
324
+ this.hoveredArea?.position &&
325
+ this.canMoveToArea(this.hoveredArea, this.currentItem)
207
326
  ) {
208
327
  const movedNode = this.currentItem.node;
209
328
  const targetNode = this.hoveredArea.node;
210
329
  const position = this.hoveredArea.position;
211
330
  const previousParent = movedNode.parent;
212
331
 
213
- if (position === Position.Inside) {
332
+ if (position === "inside") {
214
333
  this.hoveredArea.node.is_open = true;
215
334
  }
216
335
 
@@ -230,7 +349,7 @@ export class DragAndDropHandler {
230
349
  do_move: doMove,
231
350
  moved_node: movedNode,
232
351
  original_event: positionInfo.originalEvent,
233
- position: getPositionName(position),
352
+ position,
234
353
  previous_parent: previousParent,
235
354
  target_node: targetNode,
236
355
  },
@@ -255,11 +374,7 @@ export class DragAndDropHandler {
255
374
  private mustOpenFolderTimer(area: HitArea): boolean {
256
375
  const node = area.node;
257
376
 
258
- return (
259
- node.isFolder() &&
260
- !node.is_open &&
261
- area.position === Position.Inside
262
- );
377
+ return node.isFolder() && !node.is_open && area.position === "inside";
263
378
  }
264
379
 
265
380
  private removeDropHint(): void {
@@ -315,140 +430,4 @@ export class DragAndDropHandler {
315
430
  const nodeElement = this.getNodeElementForNode(this.hoveredArea.node);
316
431
  this.previousGhost = nodeElement.addDropHint(this.hoveredArea.position);
317
432
  }
318
-
319
- public mouseCapture(positionInfo: PositionInfo): boolean | null {
320
- const element = positionInfo.target;
321
-
322
- if (!this.mustCaptureElement(element)) {
323
- return null;
324
- }
325
-
326
- if (this.onIsMoveHandle && !this.onIsMoveHandle(jQuery(element))) {
327
- return null;
328
- }
329
-
330
- let nodeElement = this.getNodeElement(element);
331
-
332
- if (nodeElement && this.onCanMove) {
333
- if (!this.onCanMove(nodeElement.node)) {
334
- nodeElement = null;
335
- }
336
- }
337
-
338
- this.currentItem = nodeElement;
339
- return this.currentItem != null;
340
- }
341
-
342
- public mouseDrag(positionInfo: PositionInfo): boolean {
343
- if (!this.currentItem || !this.dragElement) {
344
- return false;
345
- }
346
-
347
- this.dragElement.move(positionInfo.pageX, positionInfo.pageY);
348
-
349
- const area = this.findHoveredArea(
350
- positionInfo.pageX,
351
- positionInfo.pageY,
352
- );
353
-
354
- if (area && this.canMoveToArea(area)) {
355
- if (!area.node.isFolder()) {
356
- this.stopOpenFolderTimer();
357
- }
358
-
359
- if (this.hoveredArea !== area) {
360
- this.hoveredArea = area;
361
-
362
- // If this is a closed folder, start timer to open it
363
- if (this.mustOpenFolderTimer(area)) {
364
- this.startOpenFolderTimer(area.node);
365
- } else {
366
- this.stopOpenFolderTimer();
367
- }
368
-
369
- this.updateDropHint();
370
- }
371
- } else {
372
- this.removeDropHint();
373
- this.stopOpenFolderTimer();
374
- this.hoveredArea = area;
375
- }
376
-
377
- if (!area) {
378
- if (this.onDragMove) {
379
- this.onDragMove(
380
- this.currentItem.node,
381
- positionInfo.originalEvent,
382
- );
383
- }
384
- }
385
-
386
- return true;
387
- }
388
-
389
- public mouseStart(positionInfo: PositionInfo): boolean {
390
- if (!this.currentItem) {
391
- return false;
392
- }
393
-
394
- this.refresh();
395
-
396
- const { left, top } = getElementPosition(positionInfo.target);
397
-
398
- const node = this.currentItem.node;
399
-
400
- this.dragElement = new DragElement({
401
- autoEscape: this.autoEscape ?? true,
402
- nodeName: node.name,
403
- offsetX: positionInfo.pageX - left,
404
- offsetY: positionInfo.pageY - top,
405
- treeElement: this.treeElement,
406
- });
407
-
408
- this.isDragging = true;
409
- this.currentItem.element.classList.add("jqtree-moving");
410
-
411
- return true;
412
- }
413
-
414
- public mouseStop(positionInfo: PositionInfo): boolean {
415
- this.moveItem(positionInfo);
416
- this.clear();
417
- this.removeHover();
418
- this.removeDropHint();
419
- this.removeHitAreas();
420
-
421
- const currentItem = this.currentItem;
422
-
423
- if (this.currentItem) {
424
- this.currentItem.element.classList.remove("jqtree-moving");
425
- this.currentItem = null;
426
- }
427
-
428
- this.isDragging = false;
429
-
430
- if (!this.hoveredArea && currentItem) {
431
- if (this.onDragStop) {
432
- this.onDragStop(currentItem.node, positionInfo.originalEvent);
433
- }
434
- }
435
-
436
- return false;
437
- }
438
-
439
- public refresh(): void {
440
- this.removeHitAreas();
441
-
442
- if (this.currentItem) {
443
- this.generateHitAreas();
444
-
445
- this.currentItem = this.getNodeElementForNode(
446
- this.currentItem.node,
447
- );
448
-
449
- if (this.isDragging) {
450
- this.currentItem.element.classList.add("jqtree-moving");
451
- }
452
- }
453
- }
454
433
  }
@@ -1,5 +1,4 @@
1
- import { Node } from "../node";
2
- import { Position } from "../position";
1
+ import { Node, Position } from "../node";
3
2
 
4
3
  export interface DropHint {
5
4
  remove: () => void;
@@ -19,6 +19,8 @@ interface ElementsRendererParams {
19
19
  }
20
20
 
21
21
  export default class ElementsRenderer {
22
+ public closedIconElement?: HTMLElement | Text;
23
+ public openedIconElement?: HTMLElement | Text;
22
24
  private $element: JQuery;
23
25
  private autoEscape: boolean;
24
26
  private buttonLeft: boolean;
@@ -27,12 +29,10 @@ export default class ElementsRenderer {
27
29
  private isNodeSelected: IsNodeSelected;
28
30
  private onCreateLi?: OnCreateLi;
29
31
  private rtl?: boolean;
32
+
30
33
  private showEmptyFolder: boolean;
31
34
  private tabIndex?: number;
32
35
 
33
- public closedIconElement?: HTMLElement | Text;
34
- public openedIconElement?: HTMLElement | Text;
35
-
36
36
  constructor({
37
37
  $element,
38
38
  autoEscape,
@@ -61,6 +61,45 @@ export default class ElementsRenderer {
61
61
  this.closedIconElement = this.createButtonElement(closedIcon ?? "-");
62
62
  }
63
63
 
64
+ public render(fromNode: Node | null): void {
65
+ if (fromNode?.parent) {
66
+ this.renderFromNode(fromNode);
67
+ } else {
68
+ this.renderFromRoot();
69
+ }
70
+ }
71
+
72
+ public renderFromNode(node: Node): void {
73
+ if (!node.element) {
74
+ return;
75
+ }
76
+
77
+ // remember current li
78
+ const $previousLi = jQuery(node.element);
79
+
80
+ // create element
81
+ const li = this.createLi(node, node.getLevel());
82
+
83
+ // add element to dom
84
+ $previousLi.after(li);
85
+
86
+ // remove previous li
87
+ $previousLi.remove();
88
+
89
+ // create children
90
+ this.createDomElements(li, node.children, false, node.getLevel() + 1);
91
+ }
92
+
93
+ public renderFromRoot(): void {
94
+ this.$element.empty();
95
+
96
+ const tree = this.getTree();
97
+
98
+ if (this.$element[0] && tree) {
99
+ this.createDomElements(this.$element[0], tree.children, true, 1);
100
+ }
101
+ }
102
+
64
103
  private attachNodeData(node: Node, li: HTMLElement): void {
65
104
  node.element = li;
66
105
  jQuery(li).data("node", node);
@@ -324,43 +363,4 @@ export default class ElementsRenderer {
324
363
  element.setAttribute("aria-selected", getBoolString(isSelected));
325
364
  element.setAttribute("role", "treeitem");
326
365
  }
327
-
328
- public render(fromNode: Node | null): void {
329
- if (fromNode?.parent) {
330
- this.renderFromNode(fromNode);
331
- } else {
332
- this.renderFromRoot();
333
- }
334
- }
335
-
336
- public renderFromNode(node: Node): void {
337
- if (!node.element) {
338
- return;
339
- }
340
-
341
- // remember current li
342
- const $previousLi = jQuery(node.element);
343
-
344
- // create element
345
- const li = this.createLi(node, node.getLevel());
346
-
347
- // add element to dom
348
- $previousLi.after(li);
349
-
350
- // remove previous li
351
- $previousLi.remove();
352
-
353
- // create children
354
- this.createDomElements(li, node.children, false, node.getLevel() + 1);
355
- }
356
-
357
- public renderFromRoot(): void {
358
- this.$element.empty();
359
-
360
- const tree = this.getTree();
361
-
362
- if (this.$element[0] && tree) {
363
- this.createDomElements(this.$element[0], tree.children, true, 1);
364
- }
365
- }
366
366
  }
@@ -1,39 +1,13 @@
1
1
  import { Node } from "./node";
2
2
 
3
- export type OnCanMove = ((node: Node) => boolean) | undefined;
4
-
5
- type DataUrlFunction = (node: Node | null) => JQuery.AjaxSettings;
3
+ export type DataFilter = (data: unknown) => NodeData[];
6
4
 
7
5
  export type DataUrl = DataUrlFunction | JQuery.AjaxSettings | string;
8
6
 
9
7
  export type DragMethod = (node: Node, event: Event | Touch) => void;
10
8
 
11
- export type OnCanMoveTo = (
12
- node: Node,
13
- targetNode: Node,
14
- positionName: string,
15
- ) => boolean;
16
-
17
- export type OnGetStateFromStorage = (() => string) | undefined;
18
-
19
- export type OnIsMoveHandle = (el: JQuery) => boolean;
20
-
21
- export type OnLoadFailed = (response: JQuery.jqXHR) => void;
22
-
23
- export type OnSetStateFromStorage = ((data: string) => void) | undefined;
24
-
25
- export type DataFilter = (data: unknown) => NodeData[];
26
-
27
9
  export type IconElement = HTMLElement | JQuery | string;
28
10
 
29
- export type OnCreateLi = (node: Node, el: JQuery, isSelected: boolean) => void;
30
-
31
- export type OnLoading = (
32
- isLoading: boolean,
33
- node: Node | null,
34
- $el: JQuery,
35
- ) => void;
36
-
37
11
  export interface JQTreeOptions {
38
12
  animationSpeed: JQuery.Duration;
39
13
  autoEscape: boolean;
@@ -68,3 +42,29 @@ export interface JQTreeOptions {
68
42
  tabIndex?: number;
69
43
  useContextMenu: boolean;
70
44
  }
45
+
46
+ export type OnCanMove = ((node: Node) => boolean) | undefined;
47
+
48
+ export type OnCanMoveTo = (
49
+ node: Node,
50
+ targetNode: Node,
51
+ positionName: string,
52
+ ) => boolean;
53
+
54
+ export type OnCreateLi = (node: Node, el: JQuery, isSelected: boolean) => void;
55
+
56
+ export type OnGetStateFromStorage = (() => string) | undefined;
57
+
58
+ export type OnIsMoveHandle = (el: JQuery) => boolean;
59
+
60
+ export type OnLoadFailed = (response: JQuery.jqXHR) => void;
61
+
62
+ export type OnLoading = (
63
+ isLoading: boolean,
64
+ node: Node | null,
65
+ $el: JQuery,
66
+ ) => void;
67
+
68
+ export type OnSetStateFromStorage = ((data: string) => void) | undefined;
69
+
70
+ type DataUrlFunction = (node: Node | null) => JQuery.AjaxSettings;