jqtree 1.8.0 → 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 (60) 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/jest.config.js +4 -0
  8. package/config/jest.polyfills.js +14 -0
  9. package/devserver/test_index.html +9 -0
  10. package/docs/.ruby-version +1 -1
  11. package/docs/_config.yml +1 -1
  12. package/docs/_entries/general/changelog.md +4 -0
  13. package/docs/_entries/multiple_selection/get-selected-nodes.md +1 -1
  14. package/docs/_entries/node/getnextnode.md +3 -6
  15. package/docs/_entries/node/getnextsibling.md +1 -1
  16. package/docs/_entries/node/getnextvisiblenode.md +8 -5
  17. package/docs/_entries/node/getpreviousnode.md +12 -0
  18. package/docs/_entries/node/getprevioussibling.md +1 -1
  19. package/docs/_entries/node/getpreviousvisiblenode.md +6 -5
  20. package/package.json +32 -30
  21. package/src/dataLoader.ts +19 -21
  22. package/src/dragAndDropHandler/dragElement.ts +37 -25
  23. package/src/dragAndDropHandler/generateHitAreas.ts +176 -0
  24. package/src/dragAndDropHandler/index.ts +32 -48
  25. package/src/dragAndDropHandler/iterateVisibleNodes.ts +91 -0
  26. package/src/dragAndDropHandler/types.ts +2 -1
  27. package/src/mouseHandler.ts +385 -0
  28. package/src/mouseUtils.ts +23 -0
  29. package/src/node.ts +1 -29
  30. package/src/nodeElement/folderElement.ts +1 -1
  31. package/src/nodeElement/ghostDropHint.ts +2 -1
  32. package/src/nodeElement/index.ts +2 -1
  33. package/src/playwright/coverage.ts +3 -3
  34. package/src/playwright/playwright.test.ts +150 -49
  35. package/src/playwright/testUtils.ts +28 -5
  36. package/src/position.ts +28 -0
  37. package/src/scrollHandler/containerScrollParent.ts +13 -23
  38. package/src/scrollHandler/createScrollParent.ts +22 -22
  39. package/src/scrollHandler/documentScrollParent.ts +16 -13
  40. package/src/scrollHandler.ts +6 -14
  41. package/src/test/jqTree/events.test.ts +97 -30
  42. package/src/test/jqTree/loadOnDemand.test.ts +22 -15
  43. package/src/test/jqTree/methods.test.ts +8 -11
  44. package/src/test/jqTree/mouse.test.ts +82 -0
  45. package/src/test/jqTree/options.test.ts +9 -8
  46. package/src/test/node.test.ts +2 -1
  47. package/src/test/{nodeUtil.test.ts → position.test.ts} +1 -1
  48. package/src/tree.jquery.ts +108 -184
  49. package/src/util.ts +10 -1
  50. package/src/version.ts +1 -1
  51. package/tree.jquery.debug.js +2158 -2135
  52. package/tree.jquery.debug.js.map +1 -1
  53. package/tree.jquery.js +3 -3
  54. package/tree.jquery.js.map +1 -1
  55. package/tsconfig.json +5 -3
  56. package/docs/_entries/functions/get-selected-nodes.md +0 -10
  57. package/src/dragAndDropHandler/hitAreasGenerator.ts +0 -175
  58. package/src/dragAndDropHandler/visibleNodeIterator.ts +0 -97
  59. package/src/mouse.widget.ts +0 -266
  60. package/src/mouseWidgetTypes.ts +0 -6
@@ -0,0 +1,176 @@
1
+ import { HitArea } from "./types";
2
+ import { Node } from "../node";
3
+ import { Position } from "../position";
4
+ import { getOffsetTop } from "../util";
5
+ import iterateVisibleNodes from "./iterateVisibleNodes";
6
+
7
+ const generatePositions = (tree: Node, currentNode: Node): HitArea[] => {
8
+ const positions: HitArea[] = [];
9
+ let lastTop = 0;
10
+
11
+ const addPosition = (node: Node, position: number, top: number) => {
12
+ const area = {
13
+ top,
14
+ bottom: 0,
15
+ node,
16
+ position,
17
+ };
18
+
19
+ positions.push(area);
20
+ lastTop = top;
21
+ };
22
+
23
+ const handleAfterOpenFolder = (node: Node, nextNode: Node | null) => {
24
+ if (node === currentNode || nextNode === currentNode) {
25
+ // Cannot move before or after current item
26
+ addPosition(node, Position.None, lastTop);
27
+ } else {
28
+ addPosition(node, Position.After, lastTop);
29
+ }
30
+ };
31
+
32
+ const handleClosedFolder = (
33
+ node: Node,
34
+ nextNode: Node | null,
35
+ element: HTMLElement,
36
+ ) => {
37
+ const top = getOffsetTop(element);
38
+
39
+ if (node === currentNode) {
40
+ // Cannot move after current item
41
+ addPosition(node, Position.None, top);
42
+ } else {
43
+ addPosition(node, Position.Inside, top);
44
+
45
+ // Cannot move before current item
46
+ if (nextNode !== currentNode) {
47
+ addPosition(node, Position.After, top);
48
+ }
49
+ }
50
+ };
51
+
52
+ const handleFirstNode = (node: Node) => {
53
+ if (node !== currentNode) {
54
+ addPosition(node, Position.Before, getOffsetTop(node.element));
55
+ }
56
+ };
57
+
58
+ const handleNode = (
59
+ node: Node,
60
+ nextNode: Node | null,
61
+ element: HTMLElement,
62
+ ) => {
63
+ const top = getOffsetTop(element);
64
+
65
+ if (node === currentNode) {
66
+ // Cannot move inside current item
67
+ addPosition(node, Position.None, top);
68
+ } else {
69
+ addPosition(node, Position.Inside, top);
70
+ }
71
+
72
+ if (nextNode === currentNode || node === currentNode) {
73
+ // Cannot move before or after current item
74
+ addPosition(node, Position.None, top);
75
+ } else {
76
+ addPosition(node, Position.After, top);
77
+ }
78
+ };
79
+
80
+ const handleOpenFolder = (node: Node, element: HTMLElement) => {
81
+ if (node === currentNode) {
82
+ // Cannot move inside current item
83
+ // Stop iterating
84
+ return false;
85
+ }
86
+
87
+ // Cannot move before current item
88
+ if (node.children[0] !== currentNode) {
89
+ addPosition(node, Position.Inside, getOffsetTop(element));
90
+ }
91
+
92
+ // Continue iterating
93
+ return true;
94
+ };
95
+
96
+ iterateVisibleNodes(tree, {
97
+ handleAfterOpenFolder,
98
+ handleClosedFolder,
99
+ handleFirstNode,
100
+ handleNode,
101
+ handleOpenFolder,
102
+ });
103
+
104
+ return positions;
105
+ };
106
+
107
+ const generateHitAreasForGroup = (
108
+ hitAreas: HitArea[],
109
+ positionsInGroup: HitArea[],
110
+ top: number,
111
+ bottom: number,
112
+ ) => {
113
+ // limit positions in group
114
+ const positionCount = Math.min(positionsInGroup.length, 4);
115
+
116
+ const areaHeight = Math.round((bottom - top) / positionCount);
117
+ let areaTop = top;
118
+
119
+ let i = 0;
120
+ while (i < positionCount) {
121
+ const position = positionsInGroup[i];
122
+
123
+ if (position) {
124
+ hitAreas.push({
125
+ top: areaTop,
126
+ bottom: areaTop + areaHeight,
127
+ node: position.node,
128
+ position: position.position,
129
+ });
130
+ }
131
+
132
+ areaTop += areaHeight;
133
+ i += 1;
134
+ }
135
+ };
136
+
137
+ const generateHitAreasFromPositions = (
138
+ positions: HitArea[],
139
+ treeBottom: number,
140
+ ): HitArea[] => {
141
+ let previousTop = positions[0]?.top ?? 0;
142
+ let group = [];
143
+ const hitAreas: HitArea[] = [];
144
+
145
+ for (const position of positions) {
146
+ if (position.top !== previousTop && group.length) {
147
+ generateHitAreasForGroup(
148
+ hitAreas,
149
+ group,
150
+ previousTop,
151
+ position.top,
152
+ );
153
+
154
+ previousTop = position.top;
155
+ group = [];
156
+ }
157
+
158
+ group.push(position);
159
+ }
160
+
161
+ generateHitAreasForGroup(hitAreas, group, previousTop, treeBottom);
162
+
163
+ return hitAreas;
164
+ };
165
+
166
+ const generateHitAreas = (
167
+ tree: Node,
168
+ currentNode: Node,
169
+ treeBottom: number,
170
+ ) => {
171
+ const positions = generatePositions(tree, currentNode);
172
+
173
+ return generateHitAreasFromPositions(positions, treeBottom);
174
+ };
175
+
176
+ export default generateHitAreas;
@@ -1,9 +1,11 @@
1
- import { getPositionName, Node, Position } from "../node";
1
+ import { Node } from "../node";
2
+ import { getPositionName, Position } from "../position";
2
3
  import { DropHint, HitArea } from "./types";
3
- import { PositionInfo } from "../mouseWidgetTypes";
4
+ import { PositionInfo } from "../mouseUtils";
4
5
  import NodeElement from "../nodeElement";
5
6
  import DragElement from "./dragElement";
6
- import HitAreasGenerator from "./hitAreasGenerator";
7
+ import generateHitAreas from "./generateHitAreas";
8
+ import { getElementPosition } from "../util";
7
9
  import {
8
10
  OnCanMove,
9
11
  OnCanMoveTo,
@@ -43,7 +45,7 @@ interface DragAndDropHandlerParams {
43
45
  openNode: OpenNode;
44
46
  refreshElements: RefreshElements;
45
47
  slide: boolean;
46
- $treeElement: JQuery<HTMLElement>;
48
+ treeElement: HTMLElement;
47
49
  triggerEvent: TriggerEvent;
48
50
  }
49
51
 
@@ -70,7 +72,7 @@ export class DragAndDropHandler {
70
72
  private previousGhost: DropHint | null;
71
73
  private refreshElements: RefreshElements;
72
74
  private slide: boolean;
73
- private $treeElement: JQuery<HTMLElement>;
75
+ private treeElement: HTMLElement;
74
76
  private triggerEvent: TriggerEvent;
75
77
 
76
78
  constructor({
@@ -87,7 +89,7 @@ export class DragAndDropHandler {
87
89
  openNode,
88
90
  refreshElements,
89
91
  slide,
90
- $treeElement,
92
+ treeElement,
91
93
  triggerEvent,
92
94
  }: DragAndDropHandlerParams) {
93
95
  this.autoEscape = autoEscape;
@@ -103,7 +105,7 @@ export class DragAndDropHandler {
103
105
  this.openNode = openNode;
104
106
  this.refreshElements = refreshElements;
105
107
  this.slide = slide;
106
- this.$treeElement = $treeElement;
108
+ this.treeElement = treeElement;
107
109
  this.triggerEvent = triggerEvent;
108
110
 
109
111
  this.hoveredArea = null;
@@ -136,29 +138,23 @@ export class DragAndDropHandler {
136
138
  }
137
139
 
138
140
  public mouseStart(positionInfo: PositionInfo): boolean {
139
- if (
140
- !this.currentItem ||
141
- positionInfo.pageX === undefined ||
142
- positionInfo.pageY === undefined
143
- ) {
141
+ if (!this.currentItem) {
144
142
  return false;
145
143
  }
146
144
 
147
145
  this.refresh();
148
146
 
149
- const offset = jQuery(positionInfo.target).offset();
150
- const left = offset ? offset.left : 0;
151
- const top = offset ? offset.top : 0;
147
+ const { left, top } = getElementPosition(positionInfo.target);
152
148
 
153
149
  const node = this.currentItem.node;
154
150
 
155
- this.dragElement = new DragElement(
156
- node.name,
157
- positionInfo.pageX - left,
158
- positionInfo.pageY - top,
159
- this.$treeElement,
160
- this.autoEscape ?? true,
161
- );
151
+ this.dragElement = new DragElement({
152
+ autoEscape: this.autoEscape ?? true,
153
+ nodeName: node.name,
154
+ offsetX: positionInfo.pageX - left,
155
+ offsetY: positionInfo.pageY - top,
156
+ treeElement: this.treeElement,
157
+ });
162
158
 
163
159
  this.isDragging = true;
164
160
  this.currentItem.element.classList.add("jqtree-moving");
@@ -167,12 +163,7 @@ export class DragAndDropHandler {
167
163
  }
168
164
 
169
165
  public mouseDrag(positionInfo: PositionInfo): boolean {
170
- if (
171
- !this.currentItem ||
172
- !this.dragElement ||
173
- positionInfo.pageX === undefined ||
174
- positionInfo.pageY === undefined
175
- ) {
166
+ if (!this.currentItem || !this.dragElement) {
176
167
  return false;
177
168
  }
178
169
 
@@ -265,12 +256,11 @@ export class DragAndDropHandler {
265
256
  if (!this.currentItem || !tree) {
266
257
  this.hitAreas = [];
267
258
  } else {
268
- const hitAreasGenerator = new HitAreasGenerator(
259
+ this.hitAreas = generateHitAreas(
269
260
  tree,
270
261
  this.currentItem.node,
271
262
  this.getTreeDimensions().bottom,
272
263
  );
273
- this.hitAreas = hitAreasGenerator.generate();
274
264
  }
275
265
  }
276
266
 
@@ -424,7 +414,8 @@ export class DragAndDropHandler {
424
414
 
425
415
  if (tree) {
426
416
  tree.moveNode(movedNode, targetNode, position);
427
- this.$treeElement.empty();
417
+
418
+ this.treeElement.textContent = "";
428
419
  this.refreshElements(null);
429
420
  }
430
421
  };
@@ -449,22 +440,15 @@ export class DragAndDropHandler {
449
440
  private getTreeDimensions(): Dimensions {
450
441
  // Return the dimensions of the tree. Add a margin to the bottom to allow
451
442
  // to drag-and-drop after the last element.
452
- const offset = this.$treeElement.offset();
453
-
454
- if (!offset) {
455
- return { left: 0, top: 0, right: 0, bottom: 0 };
456
- } else {
457
- const el = this.$treeElement;
458
- const width = el.width() || 0;
459
- const height = el.height() || 0;
460
- const left = offset.left + this.getScrollLeft();
461
-
462
- return {
463
- left,
464
- top: offset.top,
465
- right: left + width,
466
- bottom: offset.top + height + 16,
467
- };
468
- }
443
+ const treePosition = getElementPosition(this.treeElement);
444
+ const left = treePosition.left + this.getScrollLeft();
445
+ const top = treePosition.top;
446
+
447
+ return {
448
+ left,
449
+ top,
450
+ right: left + this.treeElement.clientWidth,
451
+ bottom: top + this.treeElement.clientHeight + 16,
452
+ };
469
453
  }
470
454
  }
@@ -0,0 +1,91 @@
1
+ import { Node } from "../node";
2
+
3
+ interface Options {
4
+ handleAfterOpenFolder: (node: Node, nextNode: Node | null) => void;
5
+ handleClosedFolder: (
6
+ node: Node,
7
+ nextNode: Node | null,
8
+ element: HTMLElement,
9
+ ) => void;
10
+ handleFirstNode: (node: Node) => void;
11
+ handleNode: (
12
+ node: Node,
13
+ nextNode: Node | null,
14
+ element: HTMLElement,
15
+ ) => void;
16
+
17
+ /*
18
+ override
19
+ return
20
+ - true: continue iterating
21
+ - false: stop iterating
22
+ */
23
+ handleOpenFolder: (node: Node, element: HTMLElement) => boolean;
24
+ }
25
+
26
+ const iterateVisibleNodes = (
27
+ tree: Node,
28
+ {
29
+ handleAfterOpenFolder,
30
+ handleClosedFolder,
31
+ handleFirstNode,
32
+ handleNode,
33
+ handleOpenFolder,
34
+ }: Options,
35
+ ) => {
36
+ let isFirstNode = true;
37
+
38
+ const iterate = (node: Node, nextNode: Node | null): void => {
39
+ let mustIterateInside =
40
+ (node.is_open || !node.element) && node.hasChildren();
41
+
42
+ let element: HTMLElement | null = null;
43
+
44
+ // Is the element visible?
45
+ if (node.element?.offsetParent) {
46
+ element = node.element;
47
+
48
+ if (isFirstNode) {
49
+ handleFirstNode(node);
50
+ isFirstNode = false;
51
+ }
52
+
53
+ if (!node.hasChildren()) {
54
+ handleNode(node, nextNode, node.element);
55
+ } else if (node.is_open) {
56
+ if (!handleOpenFolder(node, node.element)) {
57
+ mustIterateInside = false;
58
+ }
59
+ } else {
60
+ handleClosedFolder(node, nextNode, element);
61
+ }
62
+ }
63
+
64
+ if (mustIterateInside) {
65
+ const childrenLength = node.children.length;
66
+ node.children.forEach((_, i) => {
67
+ const child = node.children[i];
68
+
69
+ if (child) {
70
+ if (i === childrenLength - 1) {
71
+ iterate(child, null);
72
+ } else {
73
+ const nextChild = node.children[i + 1];
74
+
75
+ if (nextChild) {
76
+ iterate(child, nextChild);
77
+ }
78
+ }
79
+ }
80
+ });
81
+
82
+ if (node.is_open && element) {
83
+ handleAfterOpenFolder(node, nextNode);
84
+ }
85
+ }
86
+ };
87
+
88
+ iterate(tree, null);
89
+ };
90
+
91
+ export default iterateVisibleNodes;
@@ -1,4 +1,5 @@
1
- import { Node, Position } from "../node";
1
+ import { Node } from "../node";
2
+ import { Position } from "../position";
2
3
 
3
4
  export interface DropHint {
4
5
  remove: () => void;