tree-element 0.3.2 → 0.4.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.
- package/README.md +23 -20
- package/lib/classNames.js +4 -4
- package/lib/classNames.js.map +1 -1
- package/lib/dataLoader.d.ts +0 -1
- package/lib/dataLoader.d.ts.map +1 -1
- package/lib/dataLoader.js +10 -7
- package/lib/dataLoader.js.map +1 -1
- package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -1
- package/lib/dragAndDropHandler/generateHitAreas.js +3 -2
- package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -1
- package/lib/dragAndDropHandler/index.d.ts +2 -4
- package/lib/dragAndDropHandler/index.d.ts.map +1 -1
- package/lib/dragAndDropHandler/index.js +2 -9
- package/lib/dragAndDropHandler/index.js.map +1 -1
- package/lib/elementsRenderer.d.ts +11 -4
- package/lib/elementsRenderer.d.ts.map +1 -1
- package/lib/elementsRenderer.js +104 -43
- package/lib/elementsRenderer.js.map +1 -1
- package/lib/events.d.ts +13 -7
- package/lib/events.d.ts.map +1 -1
- package/lib/folderOpenState.d.ts +8 -0
- package/lib/folderOpenState.d.ts.map +1 -0
- package/lib/folderOpenState.js +22 -0
- package/lib/folderOpenState.js.map +1 -0
- package/lib/index.d.ts +27 -20
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +166 -65
- package/lib/index.js.map +1 -1
- package/lib/keyHandler.d.ts +16 -3
- package/lib/keyHandler.d.ts.map +1 -1
- package/lib/keyHandler.js +79 -32
- package/lib/keyHandler.js.map +1 -1
- package/lib/methodTypes.d.ts +3 -0
- package/lib/methodTypes.d.ts.map +1 -1
- package/lib/mouseHandler.d.ts.map +1 -1
- package/lib/mouseHandler.js +12 -12
- package/lib/mouseHandler.js.map +1 -1
- package/lib/node.d.ts +12 -10
- package/lib/node.d.ts.map +1 -1
- package/lib/node.js +12 -10
- package/lib/node.js.map +1 -1
- package/lib/nodeElement/folderElement.d.ts +7 -5
- package/lib/nodeElement/folderElement.d.ts.map +1 -1
- package/lib/nodeElement/folderElement.js +35 -55
- package/lib/nodeElement/folderElement.js.map +1 -1
- package/lib/nodeElement/index.d.ts +3 -2
- package/lib/nodeElement/index.d.ts.map +1 -1
- package/lib/nodeElement/index.js +15 -12
- package/lib/nodeElement/index.js.map +1 -1
- package/lib/options.d.ts +29 -14
- package/lib/options.d.ts.map +1 -1
- package/lib/options.js +12 -0
- package/lib/options.js.map +1 -0
- package/lib/saveStateHandler.d.ts.map +1 -1
- package/lib/saveStateHandler.js +6 -6
- package/lib/saveStateHandler.js.map +1 -1
- package/lib/selectNodeHandler.d.ts +3 -0
- package/lib/selectNodeHandler.d.ts.map +1 -1
- package/lib/selectNodeHandler.js +66 -22
- package/lib/selectNodeHandler.js.map +1 -1
- package/lib/setDefaultOptions.d.ts.map +1 -1
- package/lib/setDefaultOptions.js +1 -0
- package/lib/setDefaultOptions.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 +38 -32
- package/src/classNames.ts +4 -4
- package/src/dataLoader.ts +9 -10
- package/src/dragAndDropHandler/generateHitAreas.ts +3 -2
- package/src/dragAndDropHandler/index.ts +3 -17
- package/src/elementsRenderer.ts +124 -55
- package/src/events.ts +13 -7
- package/src/folderOpenState.ts +38 -0
- package/src/index.ts +191 -82
- package/src/keyHandler.ts +104 -34
- package/src/methodTypes.ts +6 -0
- package/src/mouseHandler.ts +15 -13
- package/src/node.ts +12 -10
- package/src/nodeElement/folderElement.ts +47 -72
- package/src/nodeElement/index.ts +20 -17
- package/src/options.ts +46 -14
- package/src/saveStateHandler.ts +5 -8
- package/src/selectNodeHandler.ts +69 -25
- package/src/setDefaultOptions.ts +1 -0
- package/src/version.ts +1 -1
- package/tree_element.css +48 -5
- package/tree_element.debug.js +540 -278
- 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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ClassNames } from "./classNames";
|
|
2
|
+
import type { FolderIcons } from "./folderOpenState";
|
|
2
3
|
import type { GetTree, IsNodeSelected } from "./methodTypes";
|
|
3
4
|
import type { Node } from "./node";
|
|
4
5
|
import type { IconElement, OnCreateLi } from "./options";
|
|
5
6
|
|
|
6
|
-
import {
|
|
7
|
+
import { setFolderLiOpenState, setTogglerOpenState } from "./folderOpenState";
|
|
7
8
|
|
|
8
9
|
interface ElementsRendererParams {
|
|
9
10
|
ariaLabel?: string;
|
|
@@ -16,6 +17,7 @@ interface ElementsRendererParams {
|
|
|
16
17
|
getTree: GetTree;
|
|
17
18
|
htmlElement: HTMLElement;
|
|
18
19
|
isNodeSelected: IsNodeSelected;
|
|
20
|
+
multiSelect: boolean;
|
|
19
21
|
onCreateLi?: OnCreateLi;
|
|
20
22
|
openedIcon?: IconElement;
|
|
21
23
|
rtl?: boolean;
|
|
@@ -36,8 +38,7 @@ interface ElementsRendererParams {
|
|
|
36
38
|
* selected and loading classes.
|
|
37
39
|
*/
|
|
38
40
|
export default class ElementsRenderer {
|
|
39
|
-
public
|
|
40
|
-
public openedIconElement?: globalThis.Node;
|
|
41
|
+
public icons: FolderIcons;
|
|
41
42
|
private ariaLabel?: string;
|
|
42
43
|
private ariaLabelledBy?: string;
|
|
43
44
|
private autoEscape: boolean;
|
|
@@ -49,6 +50,7 @@ export default class ElementsRenderer {
|
|
|
49
50
|
private groupUlTemplate: HTMLUListElement;
|
|
50
51
|
private htmlElement: HTMLElement;
|
|
51
52
|
private isNodeSelected: IsNodeSelected;
|
|
53
|
+
private multiSelect: boolean;
|
|
52
54
|
private nodeTemplate: HTMLLIElement;
|
|
53
55
|
private onCreateLi?: OnCreateLi;
|
|
54
56
|
private openedFolderTemplate: HTMLLIElement;
|
|
@@ -69,6 +71,7 @@ export default class ElementsRenderer {
|
|
|
69
71
|
getTree,
|
|
70
72
|
htmlElement,
|
|
71
73
|
isNodeSelected,
|
|
74
|
+
multiSelect,
|
|
72
75
|
onCreateLi,
|
|
73
76
|
openedIcon = "+",
|
|
74
77
|
rtl,
|
|
@@ -85,19 +88,23 @@ export default class ElementsRenderer {
|
|
|
85
88
|
this.getTree = getTree;
|
|
86
89
|
this.htmlElement = htmlElement;
|
|
87
90
|
this.isNodeSelected = isNodeSelected;
|
|
91
|
+
this.multiSelect = multiSelect;
|
|
88
92
|
this.onCreateLi = onCreateLi;
|
|
89
93
|
this.rtl = rtl;
|
|
90
94
|
this.setNodeElement = setNodeElement;
|
|
91
95
|
this.showEmptyFolder = showEmptyFolder;
|
|
92
96
|
this.tabIndex = tabIndex;
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.
|
|
100
|
-
this.
|
|
97
|
+
this.icons = {
|
|
98
|
+
closed: this.createButtonElement(closedIcon),
|
|
99
|
+
opened: this.createButtonElement(openedIcon),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const templates = this.createTemplates();
|
|
103
|
+
this.closedFolderTemplate = templates.closedFolderTemplate;
|
|
104
|
+
this.groupUlTemplate = templates.groupUlTemplate;
|
|
105
|
+
this.nodeTemplate = templates.nodeTemplate;
|
|
106
|
+
this.openedFolderTemplate = templates.openedFolderTemplate;
|
|
107
|
+
this.rootUlTemplate = templates.rootUlTemplate;
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
public render(fromNode: Node | null): void {
|
|
@@ -112,14 +119,14 @@ export default class ElementsRenderer {
|
|
|
112
119
|
* null when the folder is not rendered or has no children.
|
|
113
120
|
*/
|
|
114
121
|
public renderChildren(node: Node): HTMLUListElement | null {
|
|
115
|
-
return
|
|
116
|
-
?
|
|
117
|
-
: this.createDomElements(
|
|
122
|
+
return node.element && node.hasChildren()
|
|
123
|
+
? this.createDomElements(
|
|
118
124
|
node.element,
|
|
119
125
|
node.children,
|
|
120
126
|
false,
|
|
121
127
|
node.getLevel() + 1,
|
|
122
|
-
)
|
|
128
|
+
)
|
|
129
|
+
: null;
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
public renderFromNode(node: Node): void {
|
|
@@ -146,6 +153,50 @@ export default class ElementsRenderer {
|
|
|
146
153
|
}
|
|
147
154
|
}
|
|
148
155
|
|
|
156
|
+
/* Change dragAndDrop after construction. The rendered lists and the
|
|
157
|
+
* templates for new lists get or lose the dnd class.
|
|
158
|
+
*/
|
|
159
|
+
public setDragAndDrop(dragAndDrop: boolean): void {
|
|
160
|
+
this.dragAndDrop = dragAndDrop;
|
|
161
|
+
this.updateTemplates();
|
|
162
|
+
|
|
163
|
+
for (const ul of this.htmlElement.querySelectorAll(
|
|
164
|
+
`ul.${this.classNames.common}`,
|
|
165
|
+
)) {
|
|
166
|
+
ul.classList.toggle(this.classNames.dnd, dragAndDrop);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Change multiSelect after construction. Only the templates are updated;
|
|
171
|
+
* the tree must be rendered again.
|
|
172
|
+
*/
|
|
173
|
+
public setMultiSelect(multiSelect: boolean): void {
|
|
174
|
+
this.multiSelect = multiSelect;
|
|
175
|
+
this.updateTemplates();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Change tabIndex after construction. The rendered nodes and the
|
|
179
|
+
* templates for new nodes get the new tabindex. The tab stop is updated by
|
|
180
|
+
* TreeElement.updateTabStop.
|
|
181
|
+
*/
|
|
182
|
+
public setTabIndex(tabIndex: number | undefined): void {
|
|
183
|
+
this.tabIndex = tabIndex;
|
|
184
|
+
this.updateTemplates();
|
|
185
|
+
|
|
186
|
+
for (const li of this.htmlElement.querySelectorAll("li[role=treeitem]")) {
|
|
187
|
+
if (tabIndex === undefined) {
|
|
188
|
+
li.removeAttribute("tabindex");
|
|
189
|
+
} else if (
|
|
190
|
+
!this.multiSelect &&
|
|
191
|
+
li.getAttribute("aria-selected") === "true"
|
|
192
|
+
) {
|
|
193
|
+
li.setAttribute("tabindex", String(tabIndex));
|
|
194
|
+
} else {
|
|
195
|
+
li.setAttribute("tabindex", "-1");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
149
200
|
private attachNodeData(node: Node, li: HTMLElement): void {
|
|
150
201
|
node.element = li;
|
|
151
202
|
this.setNodeElement(li, node);
|
|
@@ -208,11 +259,11 @@ export default class ElementsRenderer {
|
|
|
208
259
|
this.buttonLeft ? 1 : 0
|
|
209
260
|
] as HTMLSpanElement;
|
|
210
261
|
|
|
211
|
-
this.
|
|
262
|
+
this.fillNode(li, titleSpan, node.name, isSelected, level);
|
|
212
263
|
|
|
213
264
|
if (node.is_loading) {
|
|
214
265
|
li.classList.add(this.classNames.loading);
|
|
215
|
-
|
|
266
|
+
li.setAttribute("aria-busy", "true");
|
|
216
267
|
}
|
|
217
268
|
|
|
218
269
|
return li;
|
|
@@ -224,37 +275,26 @@ export default class ElementsRenderer {
|
|
|
224
275
|
* state, so there is a template for each.
|
|
225
276
|
*/
|
|
226
277
|
private createFolderTemplate(isOpen: boolean): HTMLLIElement {
|
|
227
|
-
const
|
|
278
|
+
const li = this.createLiTemplate(
|
|
279
|
+
`${this.classNames.common} ${this.classNames.folder}`,
|
|
280
|
+
);
|
|
281
|
+
setFolderLiOpenState(li, isOpen, this.classNames);
|
|
228
282
|
|
|
229
|
-
if (!isOpen) {
|
|
230
|
-
liClasses.push(this.classNames.closed);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const li = this.createLiTemplate(liClasses.join(" "));
|
|
234
283
|
const div = li.firstChild as HTMLDivElement;
|
|
235
284
|
|
|
236
285
|
// The toggler is hidden from screen readers: the icon is decoration, and
|
|
237
286
|
// aria-expanded on the treeitem has the open state.
|
|
238
287
|
const toggler = document.createElement("span");
|
|
239
|
-
toggler.className = this.getButtonClasses(
|
|
288
|
+
toggler.className = this.getButtonClasses();
|
|
240
289
|
toggler.setAttribute("aria-hidden", "true");
|
|
241
|
-
|
|
242
|
-
const iconElement = isOpen
|
|
243
|
-
? this.openedIconElement
|
|
244
|
-
: this.closedIconElement;
|
|
245
|
-
|
|
246
|
-
if (iconElement) {
|
|
247
|
-
toggler.append(iconElement.cloneNode(true));
|
|
248
|
-
}
|
|
290
|
+
setTogglerOpenState(toggler, isOpen, this.classNames, this.icons);
|
|
249
291
|
|
|
250
292
|
if (this.buttonLeft) {
|
|
251
293
|
div.append(toggler);
|
|
252
294
|
}
|
|
253
295
|
|
|
254
296
|
// title span
|
|
255
|
-
|
|
256
|
-
titleSpan.setAttribute("aria-expanded", getBoolString(isOpen));
|
|
257
|
-
div.append(titleSpan);
|
|
297
|
+
div.append(this.createTitleSpanTemplate(true));
|
|
258
298
|
|
|
259
299
|
if (!this.buttonLeft) {
|
|
260
300
|
div.append(toggler);
|
|
@@ -288,11 +328,22 @@ export default class ElementsRenderer {
|
|
|
288
328
|
|
|
289
329
|
/*
|
|
290
330
|
Create the outer part of a <li> template: li > div
|
|
331
|
+
The li is the treeitem, so the ul of its children is inside the treeitem.
|
|
291
332
|
*/
|
|
292
333
|
private createLiTemplate(liClasses: string): HTMLLIElement {
|
|
293
334
|
const li = document.createElement("li");
|
|
294
335
|
li.className = liClasses;
|
|
295
|
-
li.setAttribute("role", "
|
|
336
|
+
li.setAttribute("role", "treeitem");
|
|
337
|
+
|
|
338
|
+
// Only a multiselect tree marks the nodes that are not selected
|
|
339
|
+
if (this.multiSelect) {
|
|
340
|
+
li.setAttribute("aria-selected", "false");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Every node is focusable, but only the tab stop is in the tab order
|
|
344
|
+
if (this.tabIndex !== undefined) {
|
|
345
|
+
li.setAttribute("tabindex", "-1");
|
|
346
|
+
}
|
|
296
347
|
|
|
297
348
|
const div = document.createElement("div");
|
|
298
349
|
div.className = `${this.classNames.element} ${this.classNames.common}`;
|
|
@@ -318,7 +369,7 @@ export default class ElementsRenderer {
|
|
|
318
369
|
const div = li.firstChild as HTMLDivElement;
|
|
319
370
|
const titleSpan = div.firstChild as HTMLSpanElement;
|
|
320
371
|
|
|
321
|
-
this.
|
|
372
|
+
this.fillNode(li, titleSpan, node.name, isSelected, level);
|
|
322
373
|
|
|
323
374
|
return li;
|
|
324
375
|
}
|
|
@@ -335,6 +386,16 @@ export default class ElementsRenderer {
|
|
|
335
386
|
return li;
|
|
336
387
|
}
|
|
337
388
|
|
|
389
|
+
private createTemplates() {
|
|
390
|
+
return {
|
|
391
|
+
closedFolderTemplate: this.createFolderTemplate(false),
|
|
392
|
+
groupUlTemplate: this.createUlTemplate(false),
|
|
393
|
+
nodeTemplate: this.createNodeTemplate(),
|
|
394
|
+
openedFolderTemplate: this.createFolderTemplate(true),
|
|
395
|
+
rootUlTemplate: this.createUlTemplate(true),
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
338
399
|
private createTitleSpanTemplate(isFolder: boolean): HTMLSpanElement {
|
|
339
400
|
const titleSpan = document.createElement("span");
|
|
340
401
|
|
|
@@ -347,13 +408,6 @@ export default class ElementsRenderer {
|
|
|
347
408
|
classes += ` ${this.buttonLeft ? this.classNames.titleButtonLeft : this.classNames.titleButtonRight}`;
|
|
348
409
|
|
|
349
410
|
titleSpan.className = classes;
|
|
350
|
-
titleSpan.setAttribute("role", "treeitem");
|
|
351
|
-
titleSpan.setAttribute("aria-selected", "false");
|
|
352
|
-
|
|
353
|
-
// Every node is focusable, but only the tab stop is in the tab order
|
|
354
|
-
if (this.tabIndex !== undefined) {
|
|
355
|
-
titleSpan.setAttribute("tabindex", "-1");
|
|
356
|
-
}
|
|
357
411
|
|
|
358
412
|
return titleSpan;
|
|
359
413
|
}
|
|
@@ -391,29 +445,35 @@ export default class ElementsRenderer {
|
|
|
391
445
|
if (this.ariaLabelledBy !== undefined) {
|
|
392
446
|
ul.setAttribute("aria-labelledby", this.ariaLabelledBy);
|
|
393
447
|
}
|
|
448
|
+
|
|
449
|
+
if (this.multiSelect) {
|
|
450
|
+
ul.setAttribute("aria-multiselectable", "true");
|
|
451
|
+
}
|
|
394
452
|
}
|
|
395
453
|
|
|
396
454
|
return ul;
|
|
397
455
|
}
|
|
398
456
|
|
|
399
457
|
/*
|
|
400
|
-
Set the parts of a cloned title span that differ per node
|
|
458
|
+
Set the parts of a cloned li and title span that differ per node
|
|
401
459
|
*/
|
|
402
|
-
private
|
|
460
|
+
private fillNode(
|
|
461
|
+
li: HTMLLIElement,
|
|
403
462
|
titleSpan: HTMLSpanElement,
|
|
404
463
|
nodeName: string,
|
|
405
464
|
isSelected: boolean,
|
|
406
465
|
level: number,
|
|
407
466
|
): void {
|
|
408
|
-
|
|
467
|
+
li.setAttribute("aria-level", String(level));
|
|
409
468
|
|
|
410
469
|
if (isSelected) {
|
|
411
|
-
|
|
470
|
+
li.setAttribute("aria-selected", "true");
|
|
412
471
|
|
|
413
472
|
const tabIndex = this.tabIndex;
|
|
414
473
|
|
|
415
|
-
|
|
416
|
-
|
|
474
|
+
// With multiSelect the tab stop is the active node, see updateTabStop
|
|
475
|
+
if (tabIndex !== undefined && !this.multiSelect) {
|
|
476
|
+
li.setAttribute("tabindex", String(tabIndex));
|
|
417
477
|
}
|
|
418
478
|
}
|
|
419
479
|
|
|
@@ -423,15 +483,15 @@ export default class ElementsRenderer {
|
|
|
423
483
|
// eslint-disable-next-line no-unsanitized/property -- the user opted out of escaping with autoEscape: false
|
|
424
484
|
titleSpan.innerHTML = nodeName;
|
|
425
485
|
}
|
|
486
|
+
|
|
487
|
+
// The treeitem is named by its title, without the html when autoEscape
|
|
488
|
+
// is false. Otherwise the name would contain everything in the row.
|
|
489
|
+
li.setAttribute("aria-label", titleSpan.textContent);
|
|
426
490
|
}
|
|
427
491
|
|
|
428
|
-
private getButtonClasses(
|
|
492
|
+
private getButtonClasses(): string {
|
|
429
493
|
const classes = [this.classNames.toggler, this.classNames.common];
|
|
430
494
|
|
|
431
|
-
if (!isOpen) {
|
|
432
|
-
classes.push(this.classNames.closed);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
495
|
if (this.buttonLeft) {
|
|
436
496
|
classes.push(this.classNames.togglerLeft);
|
|
437
497
|
} else {
|
|
@@ -447,4 +507,13 @@ export default class ElementsRenderer {
|
|
|
447
507
|
private mustRenderChildren(node: Node): boolean {
|
|
448
508
|
return node.hasChildren() && node.is_open === true;
|
|
449
509
|
}
|
|
510
|
+
|
|
511
|
+
private updateTemplates(): void {
|
|
512
|
+
const templates = this.createTemplates();
|
|
513
|
+
this.closedFolderTemplate = templates.closedFolderTemplate;
|
|
514
|
+
this.groupUlTemplate = templates.groupUlTemplate;
|
|
515
|
+
this.nodeTemplate = templates.nodeTemplate;
|
|
516
|
+
this.openedFolderTemplate = templates.openedFolderTemplate;
|
|
517
|
+
this.rootUlTemplate = templates.rootUlTemplate;
|
|
518
|
+
}
|
|
450
519
|
}
|
package/src/events.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface TreeEvents {
|
|
|
25
25
|
/**
|
|
26
26
|
* Dispatched once, after the tree is rendered for the first time. With
|
|
27
27
|
* `dataUrl` this is after the data has arrived, so it is the moment the
|
|
28
|
-
*
|
|
28
|
+
* API is safe to use:
|
|
29
29
|
*
|
|
30
30
|
* ```js
|
|
31
31
|
* element.addEventListener("tree.init", () => {
|
|
@@ -85,7 +85,7 @@ export interface TreeEvents {
|
|
|
85
85
|
originalEvent: MouseEvent;
|
|
86
86
|
};
|
|
87
87
|
/**
|
|
88
|
-
* Dispatched
|
|
88
|
+
* Dispatched when a node is right-clicked, unless `useContextMenu` is
|
|
89
89
|
* `false`. The browser's own menu is suppressed.
|
|
90
90
|
*
|
|
91
91
|
* @group Mouse
|
|
@@ -96,7 +96,7 @@ export interface TreeEvents {
|
|
|
96
96
|
};
|
|
97
97
|
/**
|
|
98
98
|
* Dispatched when `Enter` is pressed on the selected node, or on the
|
|
99
|
-
* focused node when no node is selected. Use it for what
|
|
99
|
+
* focused node when no node is selected. Use it for what clicking a
|
|
100
100
|
* link would do, like opening the edit page of the node:
|
|
101
101
|
*
|
|
102
102
|
* ```js
|
|
@@ -142,7 +142,9 @@ export interface TreeEvents {
|
|
|
142
142
|
*/
|
|
143
143
|
"tree.select": {
|
|
144
144
|
/**
|
|
145
|
-
The node that was selected before, if any.
|
|
145
|
+
The node that was selected before, if any. When several nodes were
|
|
146
|
+
selected, this is the first, and the others get a `tree.deselect`,
|
|
147
|
+
dispatched before this event.
|
|
146
148
|
*/
|
|
147
149
|
deselectedNode: Node | null;
|
|
148
150
|
/**
|
|
@@ -155,13 +157,17 @@ export interface TreeEvents {
|
|
|
155
157
|
* is clicked again or passed to `selectNode` again with
|
|
156
158
|
* `mustToggle: true`. No `tree.select` is dispatched for it.
|
|
157
159
|
*
|
|
160
|
+
* When several nodes are selected and a click or `selectNode` selects just
|
|
161
|
+
* one node, it is dispatched for each node that loses the selection, except
|
|
162
|
+
* the one in the `deselectedNode` of `tree.select`.
|
|
163
|
+
*
|
|
158
164
|
* ```js
|
|
159
165
|
* element.addEventListener("tree.deselect", (e) => {
|
|
160
166
|
* console.log("deselected", e.detail.node.name);
|
|
161
167
|
* });
|
|
162
168
|
* ```
|
|
163
169
|
*
|
|
164
|
-
* Selecting another node does not dispatch it
|
|
170
|
+
* Selecting another node does not dispatch it—that is a `tree.select`
|
|
165
171
|
* with the previous node in `deselectedNode`. It is also not dispatched
|
|
166
172
|
* by `selectNode` without `mustToggle: true`, `selectNode(null)`,
|
|
167
173
|
* `removeFromSelection`, `removeNode`, `loadData` and restoring a saved
|
|
@@ -198,7 +204,7 @@ export interface TreeEvents {
|
|
|
198
204
|
};
|
|
199
205
|
/**
|
|
200
206
|
* Dispatched when data is set on the tree, both from the `data` option
|
|
201
|
-
* and from a
|
|
207
|
+
* and from a URL.
|
|
202
208
|
*
|
|
203
209
|
* @group Loading data
|
|
204
210
|
*/
|
|
@@ -223,7 +229,7 @@ export interface TreeEvents {
|
|
|
223
229
|
};
|
|
224
230
|
/**
|
|
225
231
|
* Dispatched when a request finishes. It is dispatched when the request
|
|
226
|
-
* fails too
|
|
232
|
+
* fails too, and a [`tree.load_failed`](#tree-load-failed) follows it.
|
|
227
233
|
*
|
|
228
234
|
* @group Loading data
|
|
229
235
|
*/
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ClassNames } from "./classNames";
|
|
2
|
+
|
|
3
|
+
import { getBoolString } from "./util";
|
|
4
|
+
|
|
5
|
+
export interface FolderIcons {
|
|
6
|
+
closed?: globalThis.Node;
|
|
7
|
+
opened?: globalThis.Node;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* The open state of a folder is shown on the toggler and on the <li>.
|
|
11
|
+
* These are used both when a folder is rendered and when it is opened or
|
|
12
|
+
* closed, so a rendered folder looks the same as a toggled one.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const setTogglerOpenState = (
|
|
16
|
+
toggler: HTMLElement,
|
|
17
|
+
isOpen: boolean,
|
|
18
|
+
classNames: ClassNames,
|
|
19
|
+
icons: FolderIcons,
|
|
20
|
+
): void => {
|
|
21
|
+
toggler.classList.toggle(classNames.closed, !isOpen);
|
|
22
|
+
toggler.replaceChildren();
|
|
23
|
+
|
|
24
|
+
const iconElement = isOpen ? icons.opened : icons.closed;
|
|
25
|
+
|
|
26
|
+
if (iconElement) {
|
|
27
|
+
toggler.append(iconElement.cloneNode(true));
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const setFolderLiOpenState = (
|
|
32
|
+
li: HTMLElement,
|
|
33
|
+
isOpen: boolean,
|
|
34
|
+
classNames: ClassNames,
|
|
35
|
+
): void => {
|
|
36
|
+
li.classList.toggle(classNames.closed, !isOpen);
|
|
37
|
+
li.setAttribute("aria-expanded", getBoolString(isOpen));
|
|
38
|
+
};
|