tree-element 0.1.0 → 0.2.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 (149) hide show
  1. package/README.md +49 -29
  2. package/lib/animation.js +5 -5
  3. package/lib/animation.js.map +1 -1
  4. package/lib/classNames.d.ts.map +1 -1
  5. package/lib/classNames.js +2 -2
  6. package/lib/classNames.js.map +1 -1
  7. package/lib/dataLoader.d.ts.map +1 -1
  8. package/lib/dataLoader.js +18 -21
  9. package/lib/dataLoader.js.map +1 -1
  10. package/lib/dragAndDropHandler/binarySearch.js +3 -3
  11. package/lib/dragAndDropHandler/binarySearch.js.map +1 -1
  12. package/lib/dragAndDropHandler/dragElement.d.ts.map +1 -1
  13. package/lib/dragAndDropHandler/dragElement.js +9 -12
  14. package/lib/dragAndDropHandler/dragElement.js.map +1 -1
  15. package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -1
  16. package/lib/dragAndDropHandler/generateHitAreas.js +25 -25
  17. package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -1
  18. package/lib/dragAndDropHandler/index.d.ts.map +1 -1
  19. package/lib/dragAndDropHandler/index.js +90 -114
  20. package/lib/dragAndDropHandler/index.js.map +1 -1
  21. package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts.map +1 -1
  22. package/lib/dragAndDropHandler/iterateVisibleNodes.js +10 -10
  23. package/lib/dragAndDropHandler/iterateVisibleNodes.js.map +1 -1
  24. package/lib/dragAndDropHandler/types.d.ts.map +1 -1
  25. package/lib/elementsRenderer.d.ts +15 -6
  26. package/lib/elementsRenderer.d.ts.map +1 -1
  27. package/lib/elementsRenderer.js +157 -121
  28. package/lib/elementsRenderer.js.map +1 -1
  29. package/lib/events.d.ts +11 -4
  30. package/lib/events.d.ts.map +1 -1
  31. package/lib/index.d.ts +5 -5
  32. package/lib/index.d.ts.map +1 -1
  33. package/lib/index.js +249 -252
  34. package/lib/index.js.map +1 -1
  35. package/lib/keyHandler.d.ts +2 -3
  36. package/lib/keyHandler.d.ts.map +1 -1
  37. package/lib/keyHandler.js +26 -25
  38. package/lib/keyHandler.js.map +1 -1
  39. package/lib/methodTypes.d.ts +1 -1
  40. package/lib/methodTypes.d.ts.map +1 -1
  41. package/lib/mouseHandler.d.ts.map +1 -1
  42. package/lib/mouseHandler.js +26 -42
  43. package/lib/mouseHandler.js.map +1 -1
  44. package/lib/mouseUtils.d.ts.map +1 -1
  45. package/lib/mouseUtils.js +2 -2
  46. package/lib/mouseUtils.js.map +1 -1
  47. package/lib/node.d.ts +2 -1
  48. package/lib/node.d.ts.map +1 -1
  49. package/lib/node.js +36 -35
  50. package/lib/node.js.map +1 -1
  51. package/lib/nodeElement/borderDropHint.d.ts.map +1 -1
  52. package/lib/nodeElement/borderDropHint.js +4 -5
  53. package/lib/nodeElement/borderDropHint.js.map +1 -1
  54. package/lib/nodeElement/folderElement.d.ts +7 -4
  55. package/lib/nodeElement/folderElement.d.ts.map +1 -1
  56. package/lib/nodeElement/folderElement.js +61 -42
  57. package/lib/nodeElement/folderElement.js.map +1 -1
  58. package/lib/nodeElement/ghostDropHint.d.ts.map +1 -1
  59. package/lib/nodeElement/ghostDropHint.js +5 -9
  60. package/lib/nodeElement/ghostDropHint.js.map +1 -1
  61. package/lib/nodeElement/index.d.ts +2 -1
  62. package/lib/nodeElement/index.d.ts.map +1 -1
  63. package/lib/nodeElement/index.js +34 -26
  64. package/lib/nodeElement/index.js.map +1 -1
  65. package/lib/nodeUtils.d.ts.map +1 -1
  66. package/lib/nodeUtils.js +1 -1
  67. package/lib/nodeUtils.js.map +1 -1
  68. package/lib/options.d.ts.map +1 -1
  69. package/lib/positionUtils.js +3 -3
  70. package/lib/positionUtils.js.map +1 -1
  71. package/lib/requestUrl.d.ts +1 -1
  72. package/lib/requestUrl.d.ts.map +1 -1
  73. package/lib/requestUrl.js +4 -6
  74. package/lib/requestUrl.js.map +1 -1
  75. package/lib/saveStateHandler.d.ts.map +1 -1
  76. package/lib/saveStateHandler.js +38 -48
  77. package/lib/saveStateHandler.js.map +1 -1
  78. package/lib/scrollHandler/containerScrollParent.d.ts.map +1 -1
  79. package/lib/scrollHandler/containerScrollParent.js +9 -11
  80. package/lib/scrollHandler/containerScrollParent.js.map +1 -1
  81. package/lib/scrollHandler/createScrollParent.d.ts.map +1 -1
  82. package/lib/scrollHandler/createScrollParent.js +10 -10
  83. package/lib/scrollHandler/createScrollParent.js.map +1 -1
  84. package/lib/scrollHandler/documentScrollParent.d.ts.map +1 -1
  85. package/lib/scrollHandler/documentScrollParent.js +16 -19
  86. package/lib/scrollHandler/documentScrollParent.js.map +1 -1
  87. package/lib/scrollHandler/scrollParent.d.ts.map +1 -1
  88. package/lib/scrollHandler/scrollParent.js +11 -17
  89. package/lib/scrollHandler/scrollParent.js.map +1 -1
  90. package/lib/scrollHandler.d.ts.map +1 -1
  91. package/lib/scrollHandler.js +11 -14
  92. package/lib/scrollHandler.js.map +1 -1
  93. package/lib/selectNodeHandler.d.ts +2 -2
  94. package/lib/selectNodeHandler.d.ts.map +1 -1
  95. package/lib/selectNodeHandler.js +45 -54
  96. package/lib/selectNodeHandler.js.map +1 -1
  97. package/lib/setDefaultOptions.d.ts.map +1 -1
  98. package/lib/setDefaultOptions.js +23 -28
  99. package/lib/setDefaultOptions.js.map +1 -1
  100. package/lib/triggerCustomEvent.d.ts.map +1 -1
  101. package/lib/triggerCustomEvent.js +2 -2
  102. package/lib/triggerCustomEvent.js.map +1 -1
  103. package/lib/util.d.ts.map +1 -1
  104. package/lib/util.js +2 -2
  105. package/lib/util.js.map +1 -1
  106. package/lib/version.d.ts +1 -1
  107. package/lib/version.js +1 -1
  108. package/lib/version.js.map +1 -1
  109. package/package.json +7 -3
  110. package/src/animation.ts +1 -1
  111. package/src/classNames.ts +52 -52
  112. package/src/dataLoader.ts +68 -70
  113. package/src/dragAndDropHandler/binarySearch.ts +17 -17
  114. package/src/dragAndDropHandler/dragElement.ts +50 -50
  115. package/src/dragAndDropHandler/generateHitAreas.ts +156 -161
  116. package/src/dragAndDropHandler/index.ts +342 -351
  117. package/src/dragAndDropHandler/iterateVisibleNodes.ts +59 -63
  118. package/src/dragAndDropHandler/types.ts +5 -5
  119. package/src/elementsRenderer.ts +351 -298
  120. package/src/events.ts +216 -209
  121. package/src/header.txt +1 -1
  122. package/src/index.ts +52 -82
  123. package/src/keyHandler.ts +125 -115
  124. package/src/methodTypes.ts +4 -7
  125. package/src/mouseHandler.ts +314 -317
  126. package/src/mouseUtils.ts +14 -14
  127. package/src/node.ts +761 -766
  128. package/src/nodeElement/borderDropHint.ts +25 -25
  129. package/src/nodeElement/folderElement.ts +135 -112
  130. package/src/nodeElement/ghostDropHint.ts +62 -62
  131. package/src/nodeElement/index.ts +88 -74
  132. package/src/nodeUtils.ts +5 -5
  133. package/src/options.ts +6 -2
  134. package/src/requestUrl.ts +3 -3
  135. package/src/saveStateHandler.ts +204 -206
  136. package/src/scrollHandler/containerScrollParent.ts +49 -52
  137. package/src/scrollHandler/createScrollParent.ts +27 -27
  138. package/src/scrollHandler/documentScrollParent.ts +70 -70
  139. package/src/scrollHandler/scrollParent.ts +98 -98
  140. package/src/scrollHandler.ts +45 -45
  141. package/src/selectNodeHandler.ts +176 -166
  142. package/src/setDefaultOptions.ts +25 -22
  143. package/src/triggerCustomEvent.ts +7 -3
  144. package/src/util.ts +2 -2
  145. package/src/version.ts +1 -1
  146. package/tree_element.debug.js +3770 -3799
  147. package/tree_element.debug.js.map +1 -1
  148. package/tree_element.js +2 -2
  149. package/tree_element.js.map +1 -1
@@ -2,36 +2,36 @@ import type { ClassNames } from "../classNames";
2
2
  import type { DropHint } from "../dragAndDropHandler/types";
3
3
 
4
4
  class BorderDropHint implements DropHint {
5
- private hint?: HTMLElement;
6
-
7
- constructor(
8
- element: HTMLElement,
9
- scrollLeft: number,
10
- classNames: ClassNames,
11
- ) {
12
- const div = element.querySelector(`:scope > .${classNames.element}`);
5
+ private hint?: HTMLElement;
6
+
7
+ constructor(
8
+ element: HTMLElement,
9
+ scrollLeft: number,
10
+ classNames: ClassNames,
11
+ ) {
12
+ const div = element.querySelector(`:scope > .${classNames.element}`);
13
+
14
+ if (!div) {
15
+ this.hint = undefined;
16
+ return;
17
+ }
13
18
 
14
- if (!div) {
15
- this.hint = undefined;
16
- return;
17
- }
19
+ const width = Math.max(element.offsetWidth + scrollLeft - 4, 0);
20
+ const height = Math.max(element.clientHeight - 4, 0);
18
21
 
19
- const width = Math.max(element.offsetWidth + scrollLeft - 4, 0);
20
- const height = Math.max(element.clientHeight - 4, 0);
22
+ const hint = document.createElement("span");
23
+ hint.className = classNames.border;
24
+ hint.style.width = `${width}px`;
25
+ hint.style.height = `${height}px`;
21
26
 
22
- const hint = document.createElement("span");
23
- hint.className = classNames.border;
24
- hint.style.width = `${width}px`;
25
- hint.style.height = `${height}px`;
27
+ this.hint = hint;
26
28
 
27
- this.hint = hint;
29
+ div.append(this.hint);
30
+ }
28
31
 
29
- div.append(this.hint);
30
- }
31
-
32
- public remove(): void {
33
- this.hint?.remove();
34
- }
32
+ public remove(): void {
33
+ this.hint?.remove();
34
+ }
35
35
  }
36
36
 
37
37
  export default BorderDropHint;
@@ -1,140 +1,163 @@
1
1
  import type { AnimationSpeed } from "../animation";
2
2
  import type { TriggerEvent } from "../methodTypes";
3
- import type { Position } from "../node";
3
+ import type { Node, Position } from "../node";
4
4
  import type { NodeElementParams } from "./index";
5
5
 
6
6
  import { slideDown, slideUp } from "../animation";
7
7
  import NodeElement from "./index";
8
8
 
9
+ export type RenderChildren = (node: Node) => HTMLUListElement | null;
10
+
9
11
  interface FolderElementParams extends NodeElementParams {
10
- closedIconElement?: HTMLElement | Text;
11
- openedIconElement?: HTMLElement | Text;
12
- triggerEvent: TriggerEvent;
12
+ closedIconElement?: globalThis.Node;
13
+ openedIconElement?: globalThis.Node;
14
+ renderChildren: RenderChildren;
15
+ triggerEvent: TriggerEvent;
13
16
  }
14
17
 
15
18
  class FolderElement extends NodeElement {
16
- private closedIconElement?: HTMLElement | Text;
17
- private openedIconElement?: HTMLElement | Text;
18
- private triggerEvent: TriggerEvent;
19
-
20
- constructor({
21
- classNames,
22
- closedIconElement,
23
- getScrollLeft,
24
- node,
25
- openedIconElement,
26
- tabIndex,
27
- treeElement,
28
- triggerEvent,
29
- }: FolderElementParams) {
30
- super({
31
- classNames,
32
- getScrollLeft,
33
- node,
34
- tabIndex,
35
- treeElement,
36
- });
37
-
38
- this.closedIconElement = closedIconElement;
39
- this.openedIconElement = openedIconElement;
40
- this.triggerEvent = triggerEvent;
19
+ private closedIconElement?: globalThis.Node;
20
+ private openedIconElement?: globalThis.Node;
21
+ private renderChildren: RenderChildren;
22
+ private triggerEvent: TriggerEvent;
23
+
24
+ constructor({
25
+ classNames,
26
+ closedIconElement,
27
+ getScrollLeft,
28
+ node,
29
+ openedIconElement,
30
+ renderChildren,
31
+ tabIndex,
32
+ treeElement,
33
+ triggerEvent,
34
+ }: FolderElementParams) {
35
+ super({
36
+ classNames,
37
+ getScrollLeft,
38
+ node,
39
+ tabIndex,
40
+ treeElement,
41
+ });
42
+
43
+ this.closedIconElement = closedIconElement;
44
+ this.openedIconElement = openedIconElement;
45
+ this.renderChildren = renderChildren;
46
+ this.triggerEvent = triggerEvent;
47
+ }
48
+
49
+ public close(slide: boolean, animationSpeed: AnimationSpeed): void {
50
+ if (!this.node.is_open) {
51
+ return;
41
52
  }
42
53
 
43
- public close(slide: boolean, animationSpeed: AnimationSpeed): void {
44
- if (!this.node.is_open) {
45
- return;
46
- }
47
-
48
- this.node.is_open = false;
49
-
50
- const button = this.getButton();
51
- button.classList.add(this.classNames.closed);
52
- button.innerHTML = "";
53
-
54
- const closedIconElement = this.closedIconElement;
55
-
56
- if (closedIconElement) {
57
- const icon = closedIconElement.cloneNode(true);
58
- button.appendChild(icon);
59
- }
60
-
61
- const doClose = (): void => {
62
- this.element.classList.add(this.classNames.closed);
63
-
64
- const titleSpan = this.getTitleSpan();
65
- titleSpan.setAttribute("aria-expanded", "false");
66
-
67
- this.triggerEvent("tree.close", {
68
- node: this.node,
69
- });
70
- };
54
+ this.node.is_open = false;
71
55
 
72
- const ul = this.getUl();
73
-
74
- if (slide) {
75
- slideUp(ul, animationSpeed, doClose);
76
- } else {
77
- ul.style.display = "none";
78
- doClose();
79
- }
56
+ if (!this.isRendered()) {
57
+ this.triggerEvent("tree.close", { node: this.node });
58
+ return;
80
59
  }
81
60
 
82
- public async open(
83
- slide: boolean,
84
- animationSpeed: AnimationSpeed,
85
- ): Promise<void> {
86
- return new Promise(resolve => {
87
- if (this.node.is_open) {
88
- resolve();
89
- return;
90
- }
91
-
92
- this.node.is_open = true;
93
-
94
- const button = this.getButton();
95
- button.classList.remove(this.classNames.closed);
96
- button.innerHTML = "";
97
-
98
- const openedIconElement = this.openedIconElement;
99
-
100
- if (openedIconElement) {
101
- const icon = openedIconElement.cloneNode(true);
102
- button.appendChild(icon);
103
- }
61
+ const button = this.getButton();
62
+ button.classList.add(this.classNames.closed);
63
+ button.innerHTML = "";
104
64
 
105
- const doOpen = (): void => {
106
- this.element.classList.remove(this.classNames.closed);
65
+ const closedIconElement = this.closedIconElement;
107
66
 
108
- const titleSpan = this.getTitleSpan();
109
- titleSpan.setAttribute("aria-expanded", "true");
67
+ if (closedIconElement) {
68
+ const icon = closedIconElement.cloneNode(true);
69
+ button.appendChild(icon);
70
+ }
110
71
 
111
- this.triggerEvent("tree.open", {
112
- node: this.node,
113
- });
72
+ const doClose = (): void => {
73
+ this.element.classList.add(this.classNames.closed);
114
74
 
115
- resolve();
116
- };
75
+ const titleSpan = this.getTitleSpan();
76
+ titleSpan.setAttribute("aria-expanded", "false");
117
77
 
118
- const ul = this.getUl();
78
+ this.triggerEvent("tree.close", {
79
+ node: this.node,
80
+ });
81
+ };
119
82
 
120
- if (slide) {
121
- slideDown(ul, animationSpeed, doOpen);
122
- } else {
123
- ul.style.display = "block";
124
- doOpen();
125
- }
126
- });
127
- }
83
+ const ul = this.getUl();
128
84
 
129
- protected mustShowBorderDropHint(position: Position): boolean {
130
- return !this.node.is_open && position === "inside";
85
+ if (!ul) {
86
+ doClose();
87
+ } else if (slide) {
88
+ slideUp(ul, animationSpeed, doClose);
89
+ } else {
90
+ ul.style.display = "none";
91
+ doClose();
131
92
  }
93
+ }
94
+
95
+ public async open(
96
+ slide: boolean,
97
+ animationSpeed: AnimationSpeed,
98
+ ): Promise<void> {
99
+ return new Promise((resolve) => {
100
+ if (this.node.is_open) {
101
+ resolve();
102
+ return;
103
+ }
104
+
105
+ this.node.is_open = true;
106
+
107
+ if (!this.isRendered()) {
108
+ // The folder is rendered open when its parent is opened
109
+ this.triggerEvent("tree.open", { node: this.node });
110
+ resolve();
111
+ return;
112
+ }
113
+
114
+ const button = this.getButton();
115
+ button.classList.remove(this.classNames.closed);
116
+ button.innerHTML = "";
117
+
118
+ const openedIconElement = this.openedIconElement;
119
+
120
+ if (openedIconElement) {
121
+ const icon = openedIconElement.cloneNode(true);
122
+ button.appendChild(icon);
123
+ }
124
+
125
+ const doOpen = (): void => {
126
+ this.element.classList.remove(this.classNames.closed);
127
+
128
+ const titleSpan = this.getTitleSpan();
129
+ titleSpan.setAttribute("aria-expanded", "true");
130
+
131
+ this.triggerEvent("tree.open", {
132
+ node: this.node,
133
+ });
132
134
 
133
- private getButton(): HTMLLinkElement {
134
- return this.element.querySelector(
135
- `:scope > .${this.classNames.element} > a.${this.classNames.toggler}`,
136
- ) as HTMLLinkElement;
137
- }
135
+ resolve();
136
+ };
137
+
138
+ // The children are rendered the first time the folder is opened
139
+ const ul = this.getUl() ?? this.renderChildren(this.node);
140
+
141
+ if (!ul) {
142
+ doOpen();
143
+ } else if (slide) {
144
+ slideDown(ul, animationSpeed, doOpen);
145
+ } else {
146
+ ul.style.display = "block";
147
+ doOpen();
148
+ }
149
+ });
150
+ }
151
+
152
+ protected mustShowBorderDropHint(position: Position): boolean {
153
+ return !this.node.is_open && position === "inside";
154
+ }
155
+
156
+ private getButton(): HTMLLinkElement {
157
+ return this.element.querySelector(
158
+ `:scope > .${this.classNames.element} > a.${this.classNames.toggler}`,
159
+ ) as HTMLLinkElement;
160
+ }
138
161
  }
139
162
 
140
163
  export default FolderElement;
@@ -3,82 +3,82 @@ import type { DropHint } from "../dragAndDropHandler/types";
3
3
  import type { Node, Position } from "../node";
4
4
 
5
5
  class GhostDropHint implements DropHint {
6
- private classNames: ClassNames;
7
- private element: HTMLElement;
8
- private ghost: HTMLElement;
9
- private node: Node;
10
-
11
- constructor(
12
- node: Node,
13
- element: HTMLElement,
14
- position: Position,
15
- classNames: ClassNames,
16
- ) {
17
- this.classNames = classNames;
18
- this.element = element;
19
- this.node = node;
20
- this.ghost = this.createGhostElement();
21
-
22
- switch (position) {
23
- case "after":
24
- this.moveAfter();
25
- break;
26
-
27
- case "before":
28
- this.moveBefore();
29
- break;
30
-
31
- case "inside": {
32
- if (node.isFolder() && node.is_open) {
33
- this.moveInsideOpenFolder();
34
- } else {
35
- this.moveInside();
36
- }
37
- }
6
+ private classNames: ClassNames;
7
+ private element: HTMLElement;
8
+ private ghost: HTMLElement;
9
+ private node: Node;
10
+
11
+ constructor(
12
+ node: Node,
13
+ element: HTMLElement,
14
+ position: Position,
15
+ classNames: ClassNames,
16
+ ) {
17
+ this.classNames = classNames;
18
+ this.element = element;
19
+ this.node = node;
20
+ this.ghost = this.createGhostElement();
21
+
22
+ switch (position) {
23
+ case "after":
24
+ this.moveAfter();
25
+ break;
26
+
27
+ case "before":
28
+ this.moveBefore();
29
+ break;
30
+
31
+ case "inside": {
32
+ if (node.isFolder() && node.is_open) {
33
+ this.moveInsideOpenFolder();
34
+ } else {
35
+ this.moveInside();
38
36
  }
37
+ }
39
38
  }
39
+ }
40
40
 
41
- public remove(): void {
42
- this.ghost.remove();
43
- }
41
+ public remove(): void {
42
+ this.ghost.remove();
43
+ }
44
44
 
45
- private createGhostElement() {
46
- const { circle, common, ghost: ghostClass, line } = this.classNames;
45
+ private createGhostElement() {
46
+ const { circle, common, ghost: ghostClass, line } = this.classNames;
47
47
 
48
- const ghost = document.createElement("li");
49
- ghost.className = `${common} ${ghostClass}`;
48
+ const ghost = document.createElement("li");
49
+ ghost.className = `${common} ${ghostClass}`;
50
50
 
51
- const circleSpan = document.createElement("span");
52
- circleSpan.className = `${common} ${circle}`;
53
- ghost.append(circleSpan);
51
+ const circleSpan = document.createElement("span");
52
+ circleSpan.className = `${common} ${circle}`;
53
+ ghost.append(circleSpan);
54
54
 
55
- const lineSpan = document.createElement("span");
56
- lineSpan.className = `${common} ${line}`;
57
- ghost.append(lineSpan);
55
+ const lineSpan = document.createElement("span");
56
+ lineSpan.className = `${common} ${line}`;
57
+ ghost.append(lineSpan);
58
58
 
59
- return ghost;
60
- }
59
+ return ghost;
60
+ }
61
61
 
62
- private moveAfter(): void {
63
- this.element.after(this.ghost);
64
- }
62
+ private moveAfter(): void {
63
+ this.element.after(this.ghost);
64
+ }
65
65
 
66
- private moveBefore(): void {
67
- this.element.before(this.ghost);
68
- }
66
+ private moveBefore(): void {
67
+ this.element.before(this.ghost);
68
+ }
69
69
 
70
- private moveInside(): void {
71
- this.element.after(this.ghost);
72
- this.ghost.classList.add(this.classNames.inside);
73
- }
70
+ private moveInside(): void {
71
+ this.element.after(this.ghost);
72
+ this.ghost.classList.add(this.classNames.inside);
73
+ }
74
74
 
75
- private moveInsideOpenFolder(): void {
76
- const childElement = this.node.children[0]?.element;
75
+ private moveInsideOpenFolder(): void {
76
+ const childElement = this.node.children[0]?.element;
77
77
 
78
- if (childElement) {
79
- childElement.before(this.ghost);
80
- }
78
+ if (childElement) {
79
+ childElement.before(this.ghost);
81
80
  }
81
+ }
82
82
  }
83
83
 
84
84
  export default GhostDropHint;
@@ -7,97 +7,111 @@ import BorderDropHint from "./borderDropHint";
7
7
  import GhostDropHint from "./ghostDropHint";
8
8
 
9
9
  export interface NodeElementParams {
10
- classNames: ClassNames;
11
- getScrollLeft: GetScrollLeft;
12
- node: Node;
13
- tabIndex?: number;
14
- treeElement: HTMLElement;
10
+ classNames: ClassNames;
11
+ getScrollLeft: GetScrollLeft;
12
+ node: Node;
13
+ tabIndex?: number;
14
+ treeElement: HTMLElement;
15
15
  }
16
16
 
17
17
  class NodeElement {
18
- public element: HTMLElement;
19
- public node: Node;
20
- protected classNames: ClassNames;
21
- private getScrollLeft: GetScrollLeft;
22
- private tabIndex?: number;
23
- private treeElement: HTMLElement;
24
-
25
- constructor({
26
- classNames,
27
- getScrollLeft,
28
- node,
29
- tabIndex,
30
- treeElement,
31
- }: NodeElementParams) {
32
- this.classNames = classNames;
33
- this.getScrollLeft = getScrollLeft;
34
- this.node = node;
35
- this.tabIndex = tabIndex;
36
- this.treeElement = treeElement;
37
-
38
- node.element ??= this.treeElement;
39
- this.element = node.element;
40
-
18
+ public element: HTMLElement;
19
+ public node: Node;
20
+ protected classNames: ClassNames;
21
+ private getScrollLeft: GetScrollLeft;
22
+ private tabIndex?: number;
23
+ private treeElement: HTMLElement;
24
+
25
+ constructor({
26
+ classNames,
27
+ getScrollLeft,
28
+ node,
29
+ tabIndex,
30
+ treeElement,
31
+ }: NodeElementParams) {
32
+ this.classNames = classNames;
33
+ this.getScrollLeft = getScrollLeft;
34
+ this.node = node;
35
+ this.tabIndex = tabIndex;
36
+ this.treeElement = treeElement;
37
+
38
+ // The root node has no element of its own; it uses the tree element.
39
+ this.element = node.element ?? this.treeElement;
40
+ }
41
+
42
+ public addDropHint(position: Position): DropHint {
43
+ if (this.mustShowBorderDropHint(position)) {
44
+ return new BorderDropHint(
45
+ this.element,
46
+ this.getScrollLeft(),
47
+ this.classNames,
48
+ );
49
+ } else {
50
+ return new GhostDropHint(
51
+ this.node,
52
+ this.element,
53
+ position,
54
+ this.classNames,
55
+ );
41
56
  }
57
+ }
42
58
 
43
- public addDropHint(position: Position): DropHint {
44
- if (this.mustShowBorderDropHint(position)) {
45
- return new BorderDropHint(
46
- this.element,
47
- this.getScrollLeft(),
48
- this.classNames,
49
- );
50
- } else {
51
- return new GhostDropHint(
52
- this.node,
53
- this.element,
54
- position,
55
- this.classNames,
56
- );
57
- }
59
+ public deselect(): void {
60
+ if (!this.isRendered()) {
61
+ return;
58
62
  }
59
63
 
60
- public deselect(): void {
61
- this.element.classList.remove(this.classNames.selected);
62
-
63
- const titleSpan = this.getTitleSpan();
64
- titleSpan.removeAttribute("tabindex");
65
- titleSpan.setAttribute("aria-selected", "false");
66
-
67
- titleSpan.blur();
68
- }
64
+ this.element.classList.remove(this.classNames.selected);
69
65
 
70
- public select(mustSetFocus: boolean): void {
71
- this.element.classList.add(this.classNames.selected);
66
+ const titleSpan = this.getTitleSpan();
67
+ titleSpan.removeAttribute("tabindex");
68
+ titleSpan.setAttribute("aria-selected", "false");
72
69
 
73
- const titleSpan = this.getTitleSpan();
74
- const tabIndex = this.tabIndex;
70
+ titleSpan.blur();
71
+ }
75
72
 
76
- // Check for null or undefined
77
- if (tabIndex != null) {
78
- titleSpan.setAttribute("tabindex", tabIndex.toString());
79
- }
73
+ public select(mustSetFocus: boolean): void {
74
+ if (!this.isRendered()) {
75
+ return;
76
+ }
80
77
 
81
- titleSpan.setAttribute("aria-selected", "true");
78
+ this.element.classList.add(this.classNames.selected);
82
79
 
83
- if (mustSetFocus) {
84
- titleSpan.focus();
85
- }
86
- }
80
+ const titleSpan = this.getTitleSpan();
81
+ const tabIndex = this.tabIndex;
87
82
 
88
- protected getTitleSpan(): HTMLSpanElement {
89
- return this.element.querySelector(
90
- `:scope > .${this.classNames.element} > span.${this.classNames.title}`,
91
- ) as HTMLSpanElement;
83
+ // Check for null or undefined
84
+ if (tabIndex != null) {
85
+ titleSpan.setAttribute("tabindex", tabIndex.toString());
92
86
  }
93
87
 
94
- protected getUl(): HTMLUListElement {
95
- return this.element.querySelector(":scope > ul") as HTMLUListElement;
96
- }
88
+ titleSpan.setAttribute("aria-selected", "true");
97
89
 
98
- protected mustShowBorderDropHint(position: Position): boolean {
99
- return position === "inside";
90
+ if (mustSetFocus) {
91
+ titleSpan.focus();
100
92
  }
93
+ }
94
+
95
+ protected getTitleSpan(): HTMLSpanElement {
96
+ return this.element.querySelector(
97
+ `:scope > .${this.classNames.element} > span.${this.classNames.title}`,
98
+ ) as HTMLSpanElement;
99
+ }
100
+
101
+ protected getUl(): HTMLUListElement | null {
102
+ return this.element.querySelector(":scope > ul");
103
+ }
104
+
105
+ /* A node inside a closed folder is not rendered. Its selected and open
106
+ * state is applied when it is rendered.
107
+ */
108
+ protected isRendered(): boolean {
109
+ return Boolean(this.node.element);
110
+ }
111
+
112
+ protected mustShowBorderDropHint(position: Position): boolean {
113
+ return position === "inside";
114
+ }
101
115
  }
102
116
 
103
117
  export default NodeElement;