tree-element 0.1.1 → 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.
- package/README.md +33 -29
- package/lib/animation.js +5 -5
- package/lib/animation.js.map +1 -1
- package/lib/classNames.d.ts.map +1 -1
- package/lib/classNames.js +2 -2
- package/lib/classNames.js.map +1 -1
- package/lib/dataLoader.d.ts.map +1 -1
- package/lib/dataLoader.js +14 -20
- package/lib/dataLoader.js.map +1 -1
- package/lib/dragAndDropHandler/binarySearch.js +3 -3
- package/lib/dragAndDropHandler/binarySearch.js.map +1 -1
- package/lib/dragAndDropHandler/dragElement.d.ts.map +1 -1
- package/lib/dragAndDropHandler/dragElement.js +9 -12
- package/lib/dragAndDropHandler/dragElement.js.map +1 -1
- package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -1
- package/lib/dragAndDropHandler/generateHitAreas.js +25 -25
- package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -1
- package/lib/dragAndDropHandler/index.d.ts.map +1 -1
- package/lib/dragAndDropHandler/index.js +90 -114
- package/lib/dragAndDropHandler/index.js.map +1 -1
- package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts.map +1 -1
- package/lib/dragAndDropHandler/iterateVisibleNodes.js +10 -10
- package/lib/dragAndDropHandler/iterateVisibleNodes.js.map +1 -1
- package/lib/dragAndDropHandler/types.d.ts.map +1 -1
- package/lib/elementsRenderer.d.ts +15 -6
- package/lib/elementsRenderer.d.ts.map +1 -1
- package/lib/elementsRenderer.js +157 -121
- package/lib/elementsRenderer.js.map +1 -1
- package/lib/events.d.ts.map +1 -1
- package/lib/index.d.ts +5 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +249 -252
- package/lib/index.js.map +1 -1
- package/lib/keyHandler.d.ts +2 -3
- package/lib/keyHandler.d.ts.map +1 -1
- package/lib/keyHandler.js +26 -25
- package/lib/keyHandler.js.map +1 -1
- package/lib/methodTypes.d.ts +1 -1
- package/lib/methodTypes.d.ts.map +1 -1
- package/lib/mouseHandler.d.ts.map +1 -1
- package/lib/mouseHandler.js +26 -42
- package/lib/mouseHandler.js.map +1 -1
- package/lib/mouseUtils.d.ts.map +1 -1
- package/lib/mouseUtils.js +2 -2
- package/lib/mouseUtils.js.map +1 -1
- package/lib/node.d.ts +2 -1
- package/lib/node.d.ts.map +1 -1
- package/lib/node.js +36 -35
- package/lib/node.js.map +1 -1
- package/lib/nodeElement/borderDropHint.d.ts.map +1 -1
- package/lib/nodeElement/borderDropHint.js +4 -5
- package/lib/nodeElement/borderDropHint.js.map +1 -1
- package/lib/nodeElement/folderElement.d.ts +7 -4
- package/lib/nodeElement/folderElement.d.ts.map +1 -1
- package/lib/nodeElement/folderElement.js +61 -42
- package/lib/nodeElement/folderElement.js.map +1 -1
- package/lib/nodeElement/ghostDropHint.d.ts.map +1 -1
- package/lib/nodeElement/ghostDropHint.js +5 -9
- package/lib/nodeElement/ghostDropHint.js.map +1 -1
- package/lib/nodeElement/index.d.ts +2 -1
- package/lib/nodeElement/index.d.ts.map +1 -1
- package/lib/nodeElement/index.js +34 -26
- package/lib/nodeElement/index.js.map +1 -1
- package/lib/nodeUtils.d.ts.map +1 -1
- package/lib/nodeUtils.js +1 -1
- package/lib/nodeUtils.js.map +1 -1
- package/lib/options.d.ts.map +1 -1
- package/lib/positionUtils.js +3 -3
- package/lib/positionUtils.js.map +1 -1
- package/lib/requestUrl.d.ts +1 -1
- package/lib/requestUrl.d.ts.map +1 -1
- package/lib/requestUrl.js +4 -6
- package/lib/requestUrl.js.map +1 -1
- package/lib/saveStateHandler.d.ts.map +1 -1
- package/lib/saveStateHandler.js +38 -48
- package/lib/saveStateHandler.js.map +1 -1
- package/lib/scrollHandler/containerScrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/containerScrollParent.js +9 -11
- package/lib/scrollHandler/containerScrollParent.js.map +1 -1
- package/lib/scrollHandler/createScrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/createScrollParent.js +10 -10
- package/lib/scrollHandler/createScrollParent.js.map +1 -1
- package/lib/scrollHandler/documentScrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/documentScrollParent.js +16 -19
- package/lib/scrollHandler/documentScrollParent.js.map +1 -1
- package/lib/scrollHandler/scrollParent.d.ts.map +1 -1
- package/lib/scrollHandler/scrollParent.js +11 -17
- package/lib/scrollHandler/scrollParent.js.map +1 -1
- package/lib/scrollHandler.d.ts.map +1 -1
- package/lib/scrollHandler.js +11 -14
- package/lib/scrollHandler.js.map +1 -1
- package/lib/selectNodeHandler.d.ts +2 -2
- package/lib/selectNodeHandler.d.ts.map +1 -1
- package/lib/selectNodeHandler.js +45 -54
- package/lib/selectNodeHandler.js.map +1 -1
- package/lib/setDefaultOptions.d.ts.map +1 -1
- package/lib/setDefaultOptions.js +23 -28
- package/lib/setDefaultOptions.js.map +1 -1
- package/lib/triggerCustomEvent.d.ts.map +1 -1
- package/lib/triggerCustomEvent.js +2 -2
- package/lib/triggerCustomEvent.js.map +1 -1
- package/lib/util.d.ts.map +1 -1
- package/lib/util.js +2 -2
- package/lib/util.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/package.json +5 -3
- package/src/animation.ts +1 -1
- package/src/classNames.ts +52 -52
- package/src/dataLoader.ts +68 -71
- package/src/dragAndDropHandler/binarySearch.ts +17 -17
- package/src/dragAndDropHandler/dragElement.ts +50 -50
- package/src/dragAndDropHandler/generateHitAreas.ts +156 -161
- package/src/dragAndDropHandler/index.ts +342 -351
- package/src/dragAndDropHandler/iterateVisibleNodes.ts +59 -63
- package/src/dragAndDropHandler/types.ts +5 -5
- package/src/elementsRenderer.ts +351 -298
- package/src/events.ts +216 -216
- package/src/header.txt +1 -1
- package/src/index.ts +52 -82
- package/src/keyHandler.ts +125 -115
- package/src/methodTypes.ts +4 -7
- package/src/mouseHandler.ts +314 -317
- package/src/mouseUtils.ts +14 -14
- package/src/node.ts +761 -766
- package/src/nodeElement/borderDropHint.ts +25 -25
- package/src/nodeElement/folderElement.ts +135 -112
- package/src/nodeElement/ghostDropHint.ts +62 -62
- package/src/nodeElement/index.ts +88 -74
- package/src/nodeUtils.ts +5 -5
- package/src/options.ts +6 -2
- package/src/requestUrl.ts +3 -3
- package/src/saveStateHandler.ts +204 -206
- package/src/scrollHandler/containerScrollParent.ts +49 -52
- package/src/scrollHandler/createScrollParent.ts +27 -27
- package/src/scrollHandler/documentScrollParent.ts +70 -70
- package/src/scrollHandler/scrollParent.ts +98 -98
- package/src/scrollHandler.ts +45 -45
- package/src/selectNodeHandler.ts +176 -166
- package/src/setDefaultOptions.ts +25 -22
- package/src/triggerCustomEvent.ts +7 -3
- package/src/util.ts +2 -2
- package/src/version.ts +1 -1
- package/tree_element.debug.js +3752 -3784
- package/tree_element.debug.js.map +1 -1
- package/tree_element.js +2 -2
- package/tree_element.js.map +1 -1
package/src/elementsRenderer.ts
CHANGED
|
@@ -6,362 +6,415 @@ import type { IconElement, OnCreateLi } from "./options";
|
|
|
6
6
|
import { getBoolString } from "./util";
|
|
7
7
|
|
|
8
8
|
interface ElementsRendererParams {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
autoEscape: boolean;
|
|
10
|
+
buttonLeft: boolean;
|
|
11
|
+
classNames: ClassNames;
|
|
12
|
+
closedIcon?: IconElement;
|
|
13
|
+
dragAndDrop: boolean;
|
|
14
|
+
getTree: GetTree;
|
|
15
|
+
htmlElement: HTMLElement;
|
|
16
|
+
isNodeSelected: IsNodeSelected;
|
|
17
|
+
onCreateLi?: OnCreateLi;
|
|
18
|
+
openedIcon?: IconElement;
|
|
19
|
+
rtl?: boolean;
|
|
20
|
+
setNodeElement: (element: HTMLElement, node: Node) => void;
|
|
21
|
+
showEmptyFolder: boolean;
|
|
22
|
+
tabIndex?: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/* Renders the tree to html.
|
|
26
|
+
*
|
|
27
|
+
* The children of a closed folder are not rendered. They are rendered when
|
|
28
|
+
* the folder is opened, see renderChildren.
|
|
29
|
+
*
|
|
30
|
+
* The elements are created by cloning templates. A template is a fully
|
|
31
|
+
* built <li> or <ul> with everything that is the same for every node
|
|
32
|
+
* (structure, roles, static classes, the toggler icon). Per node only the
|
|
33
|
+
* parts that differ are set: the title, the aria attributes and the
|
|
34
|
+
* selected and loading classes.
|
|
35
|
+
*/
|
|
25
36
|
export default class ElementsRenderer {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
37
|
+
public closedIconElement?: globalThis.Node;
|
|
38
|
+
public openedIconElement?: globalThis.Node;
|
|
39
|
+
private autoEscape: boolean;
|
|
40
|
+
private buttonLeft: boolean;
|
|
41
|
+
private classNames: ClassNames;
|
|
42
|
+
private closedFolderTemplate: HTMLLIElement;
|
|
43
|
+
private dragAndDrop: boolean;
|
|
44
|
+
private getTree: GetTree;
|
|
45
|
+
private groupUlTemplate: HTMLUListElement;
|
|
46
|
+
private htmlElement: HTMLElement;
|
|
47
|
+
private isNodeSelected: IsNodeSelected;
|
|
48
|
+
private nodeTemplate: HTMLLIElement;
|
|
49
|
+
private onCreateLi?: OnCreateLi;
|
|
50
|
+
private openedFolderTemplate: HTMLLIElement;
|
|
51
|
+
private rootUlTemplate: HTMLUListElement;
|
|
52
|
+
private rtl?: boolean;
|
|
53
|
+
private setNodeElement: (element: HTMLElement, node: Node) => void;
|
|
54
|
+
private showEmptyFolder: boolean;
|
|
55
|
+
private tabIndex?: number;
|
|
56
|
+
|
|
57
|
+
constructor({
|
|
58
|
+
autoEscape,
|
|
59
|
+
buttonLeft,
|
|
60
|
+
classNames,
|
|
61
|
+
closedIcon,
|
|
62
|
+
dragAndDrop,
|
|
63
|
+
getTree,
|
|
64
|
+
htmlElement,
|
|
65
|
+
isNodeSelected,
|
|
66
|
+
onCreateLi,
|
|
67
|
+
openedIcon,
|
|
68
|
+
rtl,
|
|
69
|
+
setNodeElement,
|
|
70
|
+
showEmptyFolder,
|
|
71
|
+
tabIndex,
|
|
72
|
+
}: ElementsRendererParams) {
|
|
73
|
+
this.autoEscape = autoEscape;
|
|
74
|
+
this.buttonLeft = buttonLeft;
|
|
75
|
+
this.classNames = classNames;
|
|
76
|
+
this.dragAndDrop = dragAndDrop;
|
|
77
|
+
this.getTree = getTree;
|
|
78
|
+
this.htmlElement = htmlElement;
|
|
79
|
+
this.isNodeSelected = isNodeSelected;
|
|
80
|
+
this.onCreateLi = onCreateLi;
|
|
81
|
+
this.rtl = rtl;
|
|
82
|
+
this.setNodeElement = setNodeElement;
|
|
83
|
+
this.showEmptyFolder = showEmptyFolder;
|
|
84
|
+
this.tabIndex = tabIndex;
|
|
85
|
+
this.openedIconElement = this.createButtonElement(openedIcon ?? "+");
|
|
86
|
+
this.closedIconElement = this.createButtonElement(closedIcon ?? "-");
|
|
87
|
+
|
|
88
|
+
this.rootUlTemplate = this.createUlTemplate(true);
|
|
89
|
+
this.groupUlTemplate = this.createUlTemplate(false);
|
|
90
|
+
this.nodeTemplate = this.createNodeTemplate();
|
|
91
|
+
this.openedFolderTemplate = this.createFolderTemplate(true);
|
|
92
|
+
this.closedFolderTemplate = this.createFolderTemplate(false);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public render(fromNode: Node | null): void {
|
|
96
|
+
if (fromNode?.parent) {
|
|
97
|
+
this.renderFromNode(fromNode);
|
|
98
|
+
} else {
|
|
99
|
+
this.renderFromRoot();
|
|
71
100
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Render the children of a rendered folder. Returns the new <ul>, or
|
|
104
|
+
* null when the folder is not rendered or has no children.
|
|
105
|
+
*/
|
|
106
|
+
public renderChildren(node: Node): HTMLUListElement | null {
|
|
107
|
+
if (!node.element || !node.hasChildren()) {
|
|
108
|
+
return null;
|
|
79
109
|
}
|
|
80
110
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
return this.createDomElements(
|
|
112
|
+
node.element,
|
|
113
|
+
node.children,
|
|
114
|
+
false,
|
|
115
|
+
node.getLevel() + 1,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public renderFromNode(node: Node): void {
|
|
120
|
+
if (!node.element) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
85
123
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
124
|
+
const currentLi = node.element;
|
|
125
|
+
const newLi = this.createLi(node, node.getLevel());
|
|
126
|
+
currentLi.replaceWith(newLi);
|
|
89
127
|
|
|
90
|
-
|
|
91
|
-
|
|
128
|
+
if (this.mustRenderChildren(node)) {
|
|
129
|
+
this.createDomElements(newLi, node.children, false, node.getLevel() + 1);
|
|
92
130
|
}
|
|
131
|
+
}
|
|
93
132
|
|
|
94
|
-
|
|
95
|
-
|
|
133
|
+
public renderFromRoot(): void {
|
|
134
|
+
this.htmlElement.textContent = "";
|
|
96
135
|
|
|
97
|
-
|
|
136
|
+
const tree = this.getTree();
|
|
98
137
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
138
|
+
if (tree) {
|
|
139
|
+
this.createDomElements(this.htmlElement, tree.children, true, 1);
|
|
102
140
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private attachNodeData(node: Node, li: HTMLElement): void {
|
|
144
|
+
node.element = li;
|
|
145
|
+
this.setNodeElement(li, node);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Returns the template of a toggler icon; it is cloned for every folder.
|
|
149
|
+
* A string is parsed as html and becomes a fragment, so it can hold
|
|
150
|
+
* entities, text and elements.
|
|
151
|
+
*/
|
|
152
|
+
private createButtonElement(value: IconElement): globalThis.Node | undefined {
|
|
153
|
+
if (typeof value === "string") {
|
|
154
|
+
const template = document.createElement("template");
|
|
155
|
+
template.innerHTML = value;
|
|
156
|
+
|
|
157
|
+
return template.content;
|
|
158
|
+
} else if (value.nodeType) {
|
|
159
|
+
return value;
|
|
160
|
+
} else {
|
|
161
|
+
return undefined;
|
|
107
162
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private createDomElements(
|
|
166
|
+
element: Element,
|
|
167
|
+
children: Node[],
|
|
168
|
+
isRootNode: boolean,
|
|
169
|
+
level: number,
|
|
170
|
+
): HTMLUListElement {
|
|
171
|
+
const template = isRootNode ? this.rootUlTemplate : this.groupUlTemplate;
|
|
172
|
+
const ul = template.cloneNode() as HTMLUListElement;
|
|
173
|
+
element.appendChild(ul);
|
|
174
|
+
|
|
175
|
+
for (const child of children) {
|
|
176
|
+
const li = this.createLi(child, level);
|
|
177
|
+
ul.appendChild(li);
|
|
178
|
+
|
|
179
|
+
if (this.mustRenderChildren(child)) {
|
|
180
|
+
this.createDomElements(li, child.children, false, level + 1);
|
|
181
|
+
}
|
|
123
182
|
}
|
|
124
183
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
}
|
|
184
|
+
return ul;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private createFolderLi(
|
|
188
|
+
node: Node,
|
|
189
|
+
level: number,
|
|
190
|
+
isSelected: boolean,
|
|
191
|
+
): HTMLLIElement {
|
|
192
|
+
const template = node.is_open
|
|
193
|
+
? this.openedFolderTemplate
|
|
194
|
+
: this.closedFolderTemplate;
|
|
195
|
+
const li = template.cloneNode(true) as HTMLLIElement;
|
|
196
|
+
|
|
197
|
+
if (isSelected) {
|
|
198
|
+
li.classList.add(this.classNames.selected);
|
|
142
199
|
}
|
|
143
200
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
level: number,
|
|
147
|
-
isSelected: boolean,
|
|
148
|
-
): HTMLLIElement {
|
|
149
|
-
const buttonClasses = this.getButtonClasses(node);
|
|
150
|
-
const folderClasses = this.getFolderClasses(node, isSelected);
|
|
151
|
-
|
|
152
|
-
const iconElement = node.is_open
|
|
153
|
-
? this.openedIconElement
|
|
154
|
-
: this.closedIconElement;
|
|
155
|
-
|
|
156
|
-
// li
|
|
157
|
-
const li = document.createElement("li");
|
|
158
|
-
li.className = `${this.classNames.common} ${folderClasses}`;
|
|
159
|
-
li.setAttribute("role", "none");
|
|
160
|
-
|
|
161
|
-
// div
|
|
162
|
-
const div = document.createElement("div");
|
|
163
|
-
div.className = `${this.classNames.element} ${this.classNames.common}`;
|
|
164
|
-
div.setAttribute("role", "none");
|
|
165
|
-
|
|
166
|
-
li.appendChild(div);
|
|
167
|
-
|
|
168
|
-
// button link
|
|
169
|
-
const buttonLink = document.createElement("a");
|
|
170
|
-
buttonLink.className = buttonClasses;
|
|
171
|
-
|
|
172
|
-
if (iconElement) {
|
|
173
|
-
buttonLink.appendChild(iconElement.cloneNode(true));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (this.buttonLeft) {
|
|
177
|
-
div.appendChild(buttonLink);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// title span
|
|
181
|
-
const titleSpan = this.createTitleSpan(
|
|
182
|
-
node.name,
|
|
183
|
-
isSelected,
|
|
184
|
-
true,
|
|
185
|
-
level,
|
|
186
|
-
);
|
|
187
|
-
titleSpan.setAttribute("aria-expanded", getBoolString(node.is_open));
|
|
188
|
-
div.appendChild(titleSpan);
|
|
189
|
-
|
|
190
|
-
if (!this.buttonLeft) {
|
|
191
|
-
div.appendChild(buttonLink);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return li;
|
|
201
|
+
if (node.is_loading) {
|
|
202
|
+
li.classList.add(this.classNames.loading);
|
|
195
203
|
}
|
|
196
204
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const isSelected = this.isNodeSelected(node);
|
|
205
|
+
// li > div > [button link], title span, [button link]
|
|
206
|
+
const div = li.firstChild as HTMLDivElement;
|
|
207
|
+
const titleSpan = div.childNodes[
|
|
208
|
+
this.buttonLeft ? 1 : 0
|
|
209
|
+
] as HTMLSpanElement;
|
|
203
210
|
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
this.fillTitleSpan(titleSpan, node.name, isSelected, level);
|
|
212
|
+
|
|
213
|
+
return li;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Template for a folder <li>:
|
|
217
|
+
* li > div > [button link], title span, [button link]
|
|
218
|
+
* The toggler icon and the aria-expanded attribute depend on the open
|
|
219
|
+
* state, so there is a template for each.
|
|
220
|
+
*/
|
|
221
|
+
private createFolderTemplate(isOpen: boolean): HTMLLIElement {
|
|
222
|
+
const liClasses = [this.classNames.common, this.classNames.folder];
|
|
223
|
+
|
|
224
|
+
if (!isOpen) {
|
|
225
|
+
liClasses.push(this.classNames.closed);
|
|
226
|
+
}
|
|
206
227
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
: this.createNodeLi(node, level, isSelected);
|
|
228
|
+
const li = this.createLiTemplate(liClasses.join(" "));
|
|
229
|
+
const div = li.firstChild as HTMLDivElement;
|
|
210
230
|
|
|
211
|
-
|
|
231
|
+
// button link
|
|
232
|
+
const buttonLink = document.createElement("a");
|
|
233
|
+
buttonLink.className = this.getButtonClasses(isOpen);
|
|
212
234
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
235
|
+
const iconElement = isOpen
|
|
236
|
+
? this.openedIconElement
|
|
237
|
+
: this.closedIconElement;
|
|
216
238
|
|
|
217
|
-
|
|
239
|
+
if (iconElement) {
|
|
240
|
+
buttonLink.appendChild(iconElement.cloneNode(true));
|
|
218
241
|
}
|
|
219
242
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
level: number,
|
|
223
|
-
isSelected: boolean,
|
|
224
|
-
): HTMLLIElement {
|
|
225
|
-
const liClasses = [this.classNames.common];
|
|
226
|
-
|
|
227
|
-
if (isSelected) {
|
|
228
|
-
liClasses.push(this.classNames.selected);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const classString = liClasses.join(" ");
|
|
232
|
-
|
|
233
|
-
// li
|
|
234
|
-
const li = document.createElement("li");
|
|
235
|
-
li.className = classString;
|
|
236
|
-
li.setAttribute("role", "none");
|
|
237
|
-
|
|
238
|
-
// div
|
|
239
|
-
const div = document.createElement("div");
|
|
240
|
-
div.className = `${this.classNames.element} ${this.classNames.common}`;
|
|
241
|
-
div.setAttribute("role", "none");
|
|
242
|
-
|
|
243
|
-
li.appendChild(div);
|
|
244
|
-
|
|
245
|
-
// title span
|
|
246
|
-
const titleSpan = this.createTitleSpan(
|
|
247
|
-
node.name,
|
|
248
|
-
isSelected,
|
|
249
|
-
false,
|
|
250
|
-
level,
|
|
251
|
-
);
|
|
252
|
-
div.appendChild(titleSpan);
|
|
253
|
-
|
|
254
|
-
return li;
|
|
243
|
+
if (this.buttonLeft) {
|
|
244
|
+
div.appendChild(buttonLink);
|
|
255
245
|
}
|
|
256
246
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
level: number,
|
|
262
|
-
): HTMLSpanElement {
|
|
263
|
-
const titleSpan = document.createElement("span");
|
|
247
|
+
// title span
|
|
248
|
+
const titleSpan = this.createTitleSpanTemplate(true);
|
|
249
|
+
titleSpan.setAttribute("aria-expanded", getBoolString(isOpen));
|
|
250
|
+
div.appendChild(titleSpan);
|
|
264
251
|
|
|
265
|
-
|
|
252
|
+
if (!this.buttonLeft) {
|
|
253
|
+
div.appendChild(buttonLink);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return li;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* Create the <li> element
|
|
260
|
+
* Attach it to node.element.
|
|
261
|
+
* Call onCreateLi
|
|
262
|
+
*/
|
|
263
|
+
private createLi(node: Node, level: number): HTMLLIElement {
|
|
264
|
+
const isSelected = this.isNodeSelected(node);
|
|
265
|
+
|
|
266
|
+
const mustShowFolder =
|
|
267
|
+
node.isFolder() || (node.isEmptyFolder && this.showEmptyFolder);
|
|
266
268
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
const li = mustShowFolder
|
|
270
|
+
? this.createFolderLi(node, level, isSelected)
|
|
271
|
+
: this.createNodeLi(node, level, isSelected);
|
|
272
|
+
|
|
273
|
+
this.attachNodeData(node, li);
|
|
274
|
+
|
|
275
|
+
if (this.onCreateLi) {
|
|
276
|
+
this.onCreateLi(node, li, isSelected);
|
|
277
|
+
}
|
|
270
278
|
|
|
271
|
-
|
|
279
|
+
return li;
|
|
280
|
+
}
|
|
272
281
|
|
|
273
|
-
|
|
282
|
+
/* Create the outer part of a <li> template: li > div */
|
|
283
|
+
private createLiTemplate(liClasses: string): HTMLLIElement {
|
|
284
|
+
const li = document.createElement("li");
|
|
285
|
+
li.className = liClasses;
|
|
286
|
+
li.setAttribute("role", "none");
|
|
274
287
|
|
|
275
|
-
|
|
276
|
-
|
|
288
|
+
const div = document.createElement("div");
|
|
289
|
+
div.className = `${this.classNames.element} ${this.classNames.common}`;
|
|
290
|
+
div.setAttribute("role", "none");
|
|
277
291
|
|
|
278
|
-
|
|
279
|
-
titleSpan.setAttribute("tabindex", `${tabIndex}`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
292
|
+
li.appendChild(div);
|
|
282
293
|
|
|
283
|
-
|
|
294
|
+
return li;
|
|
295
|
+
}
|
|
284
296
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
297
|
+
private createNodeLi(
|
|
298
|
+
node: Node,
|
|
299
|
+
level: number,
|
|
300
|
+
isSelected: boolean,
|
|
301
|
+
): HTMLLIElement {
|
|
302
|
+
const li = this.nodeTemplate.cloneNode(true) as HTMLLIElement;
|
|
290
303
|
|
|
291
|
-
|
|
304
|
+
if (isSelected) {
|
|
305
|
+
li.classList.add(this.classNames.selected);
|
|
292
306
|
}
|
|
293
307
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
308
|
+
// li > div > title span
|
|
309
|
+
const div = li.firstChild as HTMLDivElement;
|
|
310
|
+
const titleSpan = div.firstChild as HTMLSpanElement;
|
|
297
311
|
|
|
298
|
-
|
|
299
|
-
classString = "";
|
|
300
|
-
role = "group";
|
|
301
|
-
} else {
|
|
302
|
-
classString = this.classNames.tree;
|
|
303
|
-
role = "tree";
|
|
312
|
+
this.fillTitleSpan(titleSpan, node.name, isSelected, level);
|
|
304
313
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
}
|
|
314
|
+
return li;
|
|
315
|
+
}
|
|
309
316
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
317
|
+
/* Template for a <li> without children: li > div > title span */
|
|
318
|
+
private createNodeTemplate(): HTMLLIElement {
|
|
319
|
+
const li = this.createLiTemplate(this.classNames.common);
|
|
320
|
+
const div = li.firstChild as HTMLDivElement;
|
|
313
321
|
|
|
314
|
-
|
|
315
|
-
ul.className = `${this.classNames.common} ${classString}`;
|
|
322
|
+
div.appendChild(this.createTitleSpanTemplate(false));
|
|
316
323
|
|
|
317
|
-
|
|
324
|
+
return li;
|
|
325
|
+
}
|
|
318
326
|
|
|
319
|
-
|
|
327
|
+
private createTitleSpanTemplate(isFolder: boolean): HTMLSpanElement {
|
|
328
|
+
const titleSpan = document.createElement("span");
|
|
329
|
+
|
|
330
|
+
let classes = `${this.classNames.title} ${this.classNames.common}`;
|
|
331
|
+
|
|
332
|
+
if (isFolder) {
|
|
333
|
+
classes += ` ${this.classNames.titleFolder}`;
|
|
320
334
|
}
|
|
321
335
|
|
|
322
|
-
|
|
323
|
-
|
|
336
|
+
classes += ` ${this.buttonLeft ? this.classNames.titleButtonLeft : this.classNames.titleButtonRight}`;
|
|
337
|
+
|
|
338
|
+
titleSpan.className = classes;
|
|
339
|
+
titleSpan.setAttribute("role", "treeitem");
|
|
340
|
+
titleSpan.setAttribute("aria-selected", "false");
|
|
341
|
+
|
|
342
|
+
return titleSpan;
|
|
343
|
+
}
|
|
324
344
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
345
|
+
private createUlTemplate(isRootNode: boolean): HTMLUListElement {
|
|
346
|
+
let classString;
|
|
347
|
+
let role;
|
|
328
348
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
349
|
+
if (!isRootNode) {
|
|
350
|
+
classString = "";
|
|
351
|
+
role = "group";
|
|
352
|
+
} else {
|
|
353
|
+
classString = this.classNames.tree;
|
|
354
|
+
role = "tree";
|
|
334
355
|
|
|
335
|
-
|
|
356
|
+
if (this.rtl) {
|
|
357
|
+
classString += ` ${this.classNames.rtl}`;
|
|
358
|
+
}
|
|
336
359
|
}
|
|
337
360
|
|
|
338
|
-
|
|
339
|
-
|
|
361
|
+
if (this.dragAndDrop) {
|
|
362
|
+
classString += ` ${this.classNames.dnd}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const ul = document.createElement("ul");
|
|
366
|
+
ul.className = `${this.classNames.common} ${classString}`;
|
|
367
|
+
|
|
368
|
+
ul.setAttribute("role", role);
|
|
369
|
+
|
|
370
|
+
return ul;
|
|
371
|
+
}
|
|
340
372
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
373
|
+
/* Set the parts of a cloned title span that differ per node */
|
|
374
|
+
private fillTitleSpan(
|
|
375
|
+
titleSpan: HTMLSpanElement,
|
|
376
|
+
nodeName: string,
|
|
377
|
+
isSelected: boolean,
|
|
378
|
+
level: number,
|
|
379
|
+
): void {
|
|
380
|
+
titleSpan.setAttribute("aria-label", nodeName);
|
|
381
|
+
titleSpan.setAttribute("aria-level", `${level}`);
|
|
344
382
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
383
|
+
if (isSelected) {
|
|
384
|
+
titleSpan.setAttribute("aria-selected", "true");
|
|
348
385
|
|
|
349
|
-
|
|
350
|
-
classes.push(this.classNames.loading);
|
|
351
|
-
}
|
|
386
|
+
const tabIndex = this.tabIndex;
|
|
352
387
|
|
|
353
|
-
|
|
388
|
+
if (tabIndex !== undefined) {
|
|
389
|
+
titleSpan.setAttribute("tabindex", `${tabIndex}`);
|
|
390
|
+
}
|
|
354
391
|
}
|
|
355
392
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
isSelected: boolean,
|
|
361
|
-
) {
|
|
362
|
-
element.setAttribute("aria-label", name);
|
|
363
|
-
element.setAttribute("aria-level", `${level}`);
|
|
364
|
-
element.setAttribute("aria-selected", getBoolString(isSelected));
|
|
365
|
-
element.setAttribute("role", "treeitem");
|
|
393
|
+
if (this.autoEscape) {
|
|
394
|
+
titleSpan.textContent = nodeName;
|
|
395
|
+
} else {
|
|
396
|
+
titleSpan.innerHTML = nodeName;
|
|
366
397
|
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private getButtonClasses(isOpen: boolean): string {
|
|
401
|
+
const classes = [this.classNames.toggler, this.classNames.common];
|
|
402
|
+
|
|
403
|
+
if (!isOpen) {
|
|
404
|
+
classes.push(this.classNames.closed);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (this.buttonLeft) {
|
|
408
|
+
classes.push(this.classNames.togglerLeft);
|
|
409
|
+
} else {
|
|
410
|
+
classes.push(this.classNames.togglerRight);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return classes.join(" ");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* The children of a closed folder are rendered when it is opened */
|
|
417
|
+
private mustRenderChildren(node: Node): boolean {
|
|
418
|
+
return node.hasChildren() && node.is_open === true;
|
|
419
|
+
}
|
|
367
420
|
}
|