jqtree 1.7.4 → 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.
Files changed (94) hide show
  1. package/.eslintrc +5 -1
  2. package/bower.json +1 -1
  3. package/config/babel.config.json +1 -1
  4. package/config/production +2 -0
  5. package/devserver/devserver_scroll.js +8 -0
  6. package/devserver/test_scroll.html +28 -0
  7. package/devserver/test_scroll_container.html +39 -0
  8. package/docs/_config.yml +1 -1
  9. package/docs/_entries/general/changelog.md +12 -0
  10. package/docs/package.json +1 -1
  11. package/docs/pnpm-lock.yaml +30 -30
  12. package/package.json +31 -27
  13. package/src/dataLoader.ts +44 -19
  14. package/src/dragAndDropHandler/dragElement.ts +42 -0
  15. package/src/dragAndDropHandler/hitAreasGenerator.ts +175 -0
  16. package/src/dragAndDropHandler/index.ts +470 -0
  17. package/src/dragAndDropHandler/types.ts +12 -0
  18. package/src/dragAndDropHandler/visibleNodeIterator.ts +97 -0
  19. package/src/elementsRenderer.ts +75 -40
  20. package/src/jqtreeMethodTypes.ts +40 -0
  21. package/src/jqtreeOptions.ts +43 -25
  22. package/src/keyHandler.ts +59 -38
  23. package/src/mouse.widget.ts +3 -3
  24. package/src/mouseWidgetTypes.ts +6 -0
  25. package/src/node.ts +32 -48
  26. package/src/nodeElement/borderDropHint.ts +32 -0
  27. package/src/nodeElement/folderElement.ts +133 -0
  28. package/src/nodeElement/ghostDropHint.ts +68 -0
  29. package/src/nodeElement/index.ts +101 -0
  30. package/src/nodeUtils.ts +10 -0
  31. package/src/playwright/coverage.ts +1 -4
  32. package/src/playwright/playwright.test.ts +203 -15
  33. package/src/playwright/testUtils.ts +23 -15
  34. package/src/saveStateHandler.ts +75 -26
  35. package/src/scrollHandler/containerScrollParent.ts +177 -0
  36. package/src/scrollHandler/createScrollParent.ts +50 -0
  37. package/src/scrollHandler/documentScrollParent.ts +182 -0
  38. package/src/scrollHandler/types.ts +7 -0
  39. package/src/scrollHandler.ts +36 -248
  40. package/src/selectNodeHandler.ts +10 -16
  41. package/src/test/jqTree/keyboard.test.ts +18 -23
  42. package/src/test/jqTree/loadOnDemand.test.ts +2 -3
  43. package/src/test/jqTree/methods.test.ts +33 -4
  44. package/src/test/jqTree/options.test.ts +15 -4
  45. package/src/test/node.test.ts +85 -26
  46. package/src/test/nodeUtils.test.ts +21 -0
  47. package/src/tree.jquery.ts +262 -83
  48. package/src/util.ts +3 -0
  49. package/src/version.ts +1 -1
  50. package/tree.jquery.debug.js +1922 -2608
  51. package/tree.jquery.debug.js.map +1 -1
  52. package/tree.jquery.js +2 -2
  53. package/tree.jquery.js.map +1 -1
  54. package/lib/dataLoader.js +0 -124
  55. package/lib/dragAndDropHandler.js +0 -596
  56. package/lib/elementsRenderer.js +0 -268
  57. package/lib/jqtreeOptions.js +0 -1
  58. package/lib/keyHandler.js +0 -115
  59. package/lib/mouse.widget.js +0 -256
  60. package/lib/node.js +0 -717
  61. package/lib/nodeElement.js +0 -277
  62. package/lib/playwright/coverage.js +0 -96
  63. package/lib/playwright/playwright.test.js +0 -228
  64. package/lib/playwright/testUtils.js +0 -184
  65. package/lib/saveStateHandler.js +0 -278
  66. package/lib/scrollHandler.js +0 -250
  67. package/lib/selectNodeHandler.js +0 -129
  68. package/lib/simple.widget.js +0 -159
  69. package/lib/test/global.d.js +0 -3
  70. package/lib/test/jqTree/accessibility.test.js +0 -37
  71. package/lib/test/jqTree/create.test.js +0 -48
  72. package/lib/test/jqTree/events.test.js +0 -210
  73. package/lib/test/jqTree/keyboard.test.js +0 -225
  74. package/lib/test/jqTree/loadOnDemand.test.js +0 -218
  75. package/lib/test/jqTree/methods.test.js +0 -1347
  76. package/lib/test/jqTree/options.test.js +0 -548
  77. package/lib/test/node.test.js +0 -1160
  78. package/lib/test/nodeUtil.test.js +0 -27
  79. package/lib/test/support/exampleData.js +0 -36
  80. package/lib/test/support/jqTreeMatchers.js +0 -70
  81. package/lib/test/support/matchers.d.js +0 -1
  82. package/lib/test/support/setupTests.js +0 -7
  83. package/lib/test/support/testUtil.js +0 -32
  84. package/lib/test/support/treeStructure.js +0 -39
  85. package/lib/test/util.test.js +0 -26
  86. package/lib/tree.jquery.d.js +0 -1
  87. package/lib/tree.jquery.js +0 -1106
  88. package/lib/types.js +0 -1
  89. package/lib/typings.d.js +0 -2
  90. package/lib/util.js +0 -18
  91. package/lib/version.js +0 -9
  92. package/src/dragAndDropHandler.ts +0 -719
  93. package/src/nodeElement.ts +0 -272
  94. package/src/types.ts +0 -19
package/src/node.ts CHANGED
@@ -1,6 +1,4 @@
1
- interface NodeRecordWithChildren extends NodeRecord {
2
- children: NodeData[];
3
- }
1
+ import { isNodeRecordWithChildren } from "./nodeUtils";
4
2
 
5
3
  export enum Position {
6
4
  Before = 1,
@@ -33,13 +31,6 @@ export const getPositionName = (position: Position): string => {
33
31
  export const getPosition = (name: string): Position | undefined =>
34
32
  positionNames[name];
35
33
 
36
- const isNodeRecordWithChildren = (
37
- data: NodeData
38
- ): data is NodeRecordWithChildren =>
39
- typeof data === "object" &&
40
- "children" in data &&
41
- data["children"] instanceof Array;
42
-
43
34
  export class Node implements INode {
44
35
  public id?: NodeId;
45
36
  public name: string;
@@ -56,12 +47,20 @@ export class Node implements INode {
56
47
 
57
48
  [key: string]: unknown;
58
49
 
59
- constructor(o: NodeData | null = null, isRoot = false, nodeClass = Node) {
50
+ constructor(
51
+ nodeData: NodeData | null = null,
52
+ isRoot = false,
53
+ nodeClass = Node,
54
+ ) {
60
55
  this.name = "";
61
- this.isEmptyFolder = false;
62
56
  this.load_on_demand = false;
63
57
 
64
- this.setData(o);
58
+ this.isEmptyFolder =
59
+ nodeData != null &&
60
+ isNodeRecordWithChildren(nodeData) &&
61
+ nodeData.children.length === 0;
62
+
63
+ this.setData(nodeData);
65
64
 
66
65
  this.children = [];
67
66
  this.parent = null;
@@ -77,10 +76,10 @@ export class Node implements INode {
77
76
  Set the data of this node.
78
77
 
79
78
  setData(string): set the name of the node
80
- setdata(object): set attributes of the node
79
+ setData(object): set attributes of the node
81
80
 
82
81
  Examples:
83
- setdata('node1')
82
+ setData('node1')
84
83
 
85
84
  setData({ name: 'node1', id: 1});
86
85
 
@@ -133,16 +132,12 @@ export class Node implements INode {
133
132
  public loadFromData(data: NodeData[]): Node {
134
133
  this.removeChildren();
135
134
 
136
- for (const o of data) {
137
- const node = this.createNode(o);
135
+ for (const childData of data) {
136
+ const node = this.createNode(childData);
138
137
  this.addChild(node);
139
138
 
140
- if (isNodeRecordWithChildren(o)) {
141
- if (o.children.length === 0) {
142
- node.isEmptyFolder = true;
143
- } else {
144
- node.loadFromData(o.children);
145
- }
139
+ if (isNodeRecordWithChildren(childData)) {
140
+ node.loadFromData(childData.children);
146
141
  }
147
142
  }
148
143
 
@@ -254,7 +249,7 @@ export class Node implements INode {
254
249
  public moveNode(
255
250
  movedNode: Node,
256
251
  targetNode: Node,
257
- position: Position
252
+ position: Position,
258
253
  ): boolean {
259
254
  if (!movedNode.parent || movedNode.isParentOf(targetNode)) {
260
255
  // - Node is parent of target node
@@ -268,7 +263,7 @@ export class Node implements INode {
268
263
  if (targetNode.parent) {
269
264
  targetNode.parent.addChildAtPosition(
270
265
  movedNode,
271
- targetNode.parent.getChildIndex(targetNode) + 1
266
+ targetNode.parent.getChildIndex(targetNode) + 1,
272
267
  );
273
268
  return true;
274
269
  }
@@ -279,7 +274,7 @@ export class Node implements INode {
279
274
  if (targetNode.parent) {
280
275
  targetNode.parent.addChildAtPosition(
281
276
  movedNode,
282
- targetNode.parent.getChildIndex(targetNode)
277
+ targetNode.parent.getChildIndex(targetNode),
283
278
  );
284
279
  return true;
285
280
  }
@@ -380,13 +375,7 @@ export class Node implements INode {
380
375
  const childIndex = this.parent.getChildIndex(this);
381
376
  this.parent.addChildAtPosition(node, childIndex + 1);
382
377
 
383
- if (
384
- isNodeRecordWithChildren(nodeInfo) &&
385
- nodeInfo.children.length
386
- ) {
387
- node.loadFromData(nodeInfo.children);
388
- }
389
-
378
+ node.loadChildrenFromData(nodeInfo);
390
379
  return node;
391
380
  }
392
381
  }
@@ -400,13 +389,7 @@ export class Node implements INode {
400
389
  const childIndex = this.parent.getChildIndex(this);
401
390
  this.parent.addChildAtPosition(node, childIndex);
402
391
 
403
- if (
404
- isNodeRecordWithChildren(nodeInfo) &&
405
- nodeInfo.children.length
406
- ) {
407
- node.loadFromData(nodeInfo.children);
408
- }
409
-
392
+ node.loadChildrenFromData(nodeInfo);
410
393
  return node;
411
394
  }
412
395
  }
@@ -443,10 +426,7 @@ export class Node implements INode {
443
426
  const node = this.createNode(nodeInfo);
444
427
  this.addChild(node);
445
428
 
446
- if (isNodeRecordWithChildren(nodeInfo) && nodeInfo.children.length) {
447
- node.loadFromData(nodeInfo.children);
448
- }
449
-
429
+ node.loadChildrenFromData(nodeInfo);
450
430
  return node;
451
431
  }
452
432
 
@@ -454,10 +434,7 @@ export class Node implements INode {
454
434
  const node = this.createNode(nodeInfo);
455
435
  this.addChildAtPosition(node, 0);
456
436
 
457
- if (isNodeRecordWithChildren(nodeInfo) && nodeInfo.children.length) {
458
- node.loadFromData(nodeInfo.children);
459
- }
460
-
437
+ node.loadChildrenFromData(nodeInfo);
461
438
  return node;
462
439
  }
463
440
 
@@ -702,4 +679,11 @@ export class Node implements INode {
702
679
  const nodeClass = this.getNodeClass();
703
680
  return new nodeClass(nodeData);
704
681
  }
682
+
683
+ // Load children data from nodeInfo if it has children
684
+ private loadChildrenFromData(nodeInfo: NodeData) {
685
+ if (isNodeRecordWithChildren(nodeInfo) && nodeInfo.children.length) {
686
+ this.loadFromData(nodeInfo.children);
687
+ }
688
+ }
705
689
  }
@@ -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 "../node";
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,68 @@
1
+ import { Position, Node } from "../node";
2
+ import { DropHint } from "../dragAndDropHandler/types";
3
+
4
+ class GhostDropHint implements DropHint {
5
+ private element: HTMLElement;
6
+ private node: Node;
7
+ private ghost: HTMLElement;
8
+
9
+ constructor(node: Node, element: HTMLElement, position: Position) {
10
+ this.element = element;
11
+ this.node = node;
12
+ this.ghost = this.createGhostElement();
13
+
14
+ if (position === Position.After) {
15
+ this.moveAfter();
16
+ } else if (position === Position.Before) {
17
+ this.moveBefore();
18
+ } else if (position === Position.Inside) {
19
+ if (node.isFolder() && node.is_open) {
20
+ this.moveInsideOpenFolder();
21
+ } else {
22
+ this.moveInside();
23
+ }
24
+ }
25
+ }
26
+
27
+ public remove(): void {
28
+ this.ghost.remove();
29
+ }
30
+
31
+ private moveAfter(): void {
32
+ this.element.after(this.ghost);
33
+ }
34
+
35
+ private moveBefore(): void {
36
+ this.element.before(this.ghost);
37
+ }
38
+
39
+ private moveInsideOpenFolder(): void {
40
+ const childElement = this.node.children[0]?.element;
41
+
42
+ if (childElement) {
43
+ childElement.before(this.ghost);
44
+ }
45
+ }
46
+
47
+ private moveInside(): void {
48
+ this.element.after(this.ghost);
49
+ this.ghost.classList.add("jqtree-inside");
50
+ }
51
+
52
+ private createGhostElement() {
53
+ const ghost = document.createElement("li");
54
+ ghost.className = "jqtree_common jqtree-ghost";
55
+
56
+ const circleSpan = document.createElement("span");
57
+ circleSpan.className = "jqtree_common jqtree-circle";
58
+ ghost.append(circleSpan);
59
+
60
+ const lineSpan = document.createElement("span");
61
+ lineSpan.className = "jqtree_common jqtree-line";
62
+ ghost.append(lineSpan);
63
+
64
+ return ghost;
65
+ }
66
+ }
67
+
68
+ export default GhostDropHint;
@@ -0,0 +1,101 @@
1
+ import { Position, Node } from "../node";
2
+ import { DropHint } from "../dragAndDropHandler/types";
3
+ import BorderDropHint from "./borderDropHint";
4
+ import GhostDropHint from "./ghostDropHint";
5
+ import { GetScrollLeft } from "../jqtreeMethodTypes";
6
+
7
+ export interface NodeElementParams {
8
+ getScrollLeft: GetScrollLeft;
9
+ node: Node;
10
+ tabIndex?: number;
11
+ $treeElement: JQuery<HTMLElement>;
12
+ }
13
+
14
+ class NodeElement {
15
+ public node: Node;
16
+ public element: HTMLElement;
17
+ private getScrollLeft: GetScrollLeft;
18
+ private tabIndex?: number;
19
+ private $treeElement: JQuery<HTMLElement>;
20
+
21
+ constructor({
22
+ getScrollLeft,
23
+ node,
24
+ tabIndex,
25
+ $treeElement,
26
+ }: NodeElementParams) {
27
+ this.getScrollLeft = getScrollLeft;
28
+ this.tabIndex = tabIndex;
29
+ this.$treeElement = $treeElement;
30
+
31
+ this.init(node);
32
+ }
33
+
34
+ public init(node: Node): void {
35
+ this.node = node;
36
+
37
+ if (!node.element) {
38
+ const element = this.$treeElement.get(0);
39
+
40
+ if (element) {
41
+ node.element = element;
42
+ }
43
+ }
44
+
45
+ if (node.element) {
46
+ this.element = node.element;
47
+ }
48
+ }
49
+
50
+ public addDropHint(position: number): DropHint {
51
+ if (this.mustShowBorderDropHint(position)) {
52
+ return new BorderDropHint(this.element, this.getScrollLeft());
53
+ } else {
54
+ return new GhostDropHint(this.node, this.element, position);
55
+ }
56
+ }
57
+
58
+ public select(mustSetFocus: boolean): void {
59
+ this.element.classList.add("jqtree-selected");
60
+
61
+ const titleSpan = this.getTitleSpan();
62
+ const tabIndex = this.tabIndex;
63
+
64
+ // Check for null or undefined
65
+ if (tabIndex != null) {
66
+ titleSpan.setAttribute("tabindex", tabIndex.toString());
67
+ }
68
+
69
+ titleSpan.setAttribute("aria-selected", "true");
70
+
71
+ if (mustSetFocus) {
72
+ titleSpan.focus();
73
+ }
74
+ }
75
+
76
+ public deselect(): void {
77
+ this.element.classList.remove("jqtree-selected");
78
+
79
+ const titleSpan = this.getTitleSpan();
80
+ titleSpan.removeAttribute("tabindex");
81
+ titleSpan.setAttribute("aria-selected", "false");
82
+
83
+ titleSpan.blur();
84
+ }
85
+
86
+ protected getUl(): HTMLUListElement {
87
+ return this.element.querySelector(":scope > ul") as HTMLUListElement;
88
+ }
89
+
90
+ protected getTitleSpan(): HTMLSpanElement {
91
+ return this.element.querySelector(
92
+ ":scope > .jqtree-element > span.jqtree-title",
93
+ ) as HTMLSpanElement;
94
+ }
95
+
96
+ protected mustShowBorderDropHint(position: Position): boolean {
97
+ return position === Position.Inside;
98
+ }
99
+ }
100
+
101
+ export default NodeElement;
@@ -0,0 +1,10 @@
1
+ interface NodeRecordWithChildren extends NodeRecord {
2
+ children: NodeData[];
3
+ }
4
+
5
+ export const isNodeRecordWithChildren = (
6
+ data: NodeData,
7
+ ): data is NodeRecordWithChildren =>
8
+ typeof data === "object" &&
9
+ "children" in data &&
10
+ data["children"] instanceof Array;
@@ -13,14 +13,11 @@ export const initCoverage = async (context: BrowserContext) => {
13
13
  await context.exposeFunction(
14
14
  "collectIstanbulCoverage",
15
15
  (coverageJSON: string) => {
16
- if (!coverageJSON) {
17
- console.log("No coverage");
18
- } else {
16
+ if (coverageJSON) {
19
17
  const filename = path.join(
20
18
  istanbulCLIOutput,
21
19
  `playwright_coverage_${generateUUID()}.json`
22
20
  );
23
- console.log(`Writing coverage to ${filename}`);
24
21
  fs.writeFileSync(filename, coverageJSON);
25
22
  }
26
23
  }