tree-element 0.1.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/LICENSE +201 -0
- package/README.md +221 -0
- package/lib/animation.d.ts +5 -0
- package/lib/animation.d.ts.map +1 -0
- package/lib/animation.js +39 -0
- package/lib/animation.js.map +1 -0
- package/lib/classNames.d.ts +34 -0
- package/lib/classNames.d.ts.map +1 -0
- package/lib/classNames.js +39 -0
- package/lib/classNames.js.map +1 -0
- package/lib/dataLoader.d.ts +25 -0
- package/lib/dataLoader.d.ts.map +1 -0
- package/lib/dataLoader.js +69 -0
- package/lib/dataLoader.js.map +1 -0
- package/lib/dragAndDropHandler/binarySearch.d.ts +3 -0
- package/lib/dragAndDropHandler/binarySearch.d.ts.map +1 -0
- package/lib/dragAndDropHandler/binarySearch.js +23 -0
- package/lib/dragAndDropHandler/binarySearch.js.map +1 -0
- package/lib/dragAndDropHandler/dragElement.d.ts +20 -0
- package/lib/dragAndDropHandler/dragElement.d.ts.map +1 -0
- package/lib/dragAndDropHandler/dragElement.js +39 -0
- package/lib/dragAndDropHandler/dragElement.js.map +1 -0
- package/lib/dragAndDropHandler/generateHitAreas.d.ts +13 -0
- package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -0
- package/lib/dragAndDropHandler/generateHitAreas.js +130 -0
- package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -0
- package/lib/dragAndDropHandler/index.d.ts +76 -0
- package/lib/dragAndDropHandler/index.d.ts.map +1 -0
- package/lib/dragAndDropHandler/index.js +311 -0
- package/lib/dragAndDropHandler/index.js.map +1 -0
- package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts +11 -0
- package/lib/dragAndDropHandler/iterateVisibleNodes.d.ts.map +1 -0
- package/lib/dragAndDropHandler/iterateVisibleNodes.js +54 -0
- package/lib/dragAndDropHandler/iterateVisibleNodes.js.map +1 -0
- package/lib/dragAndDropHandler/types.d.ts +11 -0
- package/lib/dragAndDropHandler/types.d.ts.map +1 -0
- package/lib/elementsRenderer.d.ts +53 -0
- package/lib/elementsRenderer.d.ts.map +1 -0
- package/lib/elementsRenderer.js +251 -0
- package/lib/elementsRenderer.js.map +1 -0
- package/lib/events.d.ts +224 -0
- package/lib/events.d.ts.map +1 -0
- package/lib/index.d.ts +367 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +1098 -0
- package/lib/index.js.map +1 -0
- package/lib/keyHandler.d.ts +29 -0
- package/lib/keyHandler.d.ts.map +1 -0
- package/lib/keyHandler.js +107 -0
- package/lib/keyHandler.js.map +1 -0
- package/lib/methodTypes.d.ts +21 -0
- package/lib/methodTypes.d.ts.map +1 -0
- package/lib/mouseHandler.d.ts +60 -0
- package/lib/mouseHandler.d.ts.map +1 -0
- package/lib/mouseHandler.js +283 -0
- package/lib/mouseHandler.js.map +1 -0
- package/lib/mouseUtils.d.ts +9 -0
- package/lib/mouseUtils.d.ts.map +1 -0
- package/lib/mouseUtils.js +15 -0
- package/lib/mouseUtils.js.map +1 -0
- package/lib/node.d.ts +320 -0
- package/lib/node.d.ts.map +1 -0
- package/lib/node.js +737 -0
- package/lib/node.js.map +1 -0
- package/lib/nodeElement/borderDropHint.d.ts +9 -0
- package/lib/nodeElement/borderDropHint.d.ts.map +1 -0
- package/lib/nodeElement/borderDropHint.js +24 -0
- package/lib/nodeElement/borderDropHint.js.map +1 -0
- package/lib/nodeElement/folderElement.d.ts +22 -0
- package/lib/nodeElement/folderElement.d.ts.map +1 -0
- package/lib/nodeElement/folderElement.js +100 -0
- package/lib/nodeElement/folderElement.js.map +1 -0
- package/lib/nodeElement/ghostDropHint.d.ts +18 -0
- package/lib/nodeElement/ghostDropHint.d.ts.map +1 -0
- package/lib/nodeElement/ghostDropHint.js +67 -0
- package/lib/nodeElement/ghostDropHint.js.map +1 -0
- package/lib/nodeElement/index.d.ts +28 -0
- package/lib/nodeElement/index.d.ts.map +1 -0
- package/lib/nodeElement/index.js +66 -0
- package/lib/nodeElement/index.js.map +1 -0
- package/lib/nodeUtils.d.ts +7 -0
- package/lib/nodeUtils.d.ts.map +1 -0
- package/lib/nodeUtils.js +4 -0
- package/lib/nodeUtils.js.map +1 -0
- package/lib/options.d.ts +310 -0
- package/lib/options.d.ts.map +1 -0
- package/lib/package.json +3 -0
- package/lib/positionUtils.d.ts +8 -0
- package/lib/positionUtils.d.ts.map +1 -0
- package/lib/positionUtils.js +14 -0
- package/lib/positionUtils.js.map +1 -0
- package/lib/requestUrl.d.ts +9 -0
- package/lib/requestUrl.d.ts.map +1 -0
- package/lib/requestUrl.js +37 -0
- package/lib/requestUrl.js.map +1 -0
- package/lib/saveStateHandler.d.ts +46 -0
- package/lib/saveStateHandler.d.ts.map +1 -0
- package/lib/saveStateHandler.js +204 -0
- package/lib/saveStateHandler.js.map +1 -0
- package/lib/scrollHandler/containerScrollParent.d.ts +12 -0
- package/lib/scrollHandler/containerScrollParent.d.ts.map +1 -0
- package/lib/scrollHandler/containerScrollParent.js +49 -0
- package/lib/scrollHandler/containerScrollParent.js.map +1 -0
- package/lib/scrollHandler/createScrollParent.d.ts +4 -0
- package/lib/scrollHandler/createScrollParent.d.ts.map +1 -0
- package/lib/scrollHandler/createScrollParent.js +38 -0
- package/lib/scrollHandler/createScrollParent.js.map +1 -0
- package/lib/scrollHandler/documentScrollParent.d.ts +22 -0
- package/lib/scrollHandler/documentScrollParent.d.ts.map +1 -0
- package/lib/scrollHandler/documentScrollParent.js +71 -0
- package/lib/scrollHandler/documentScrollParent.js.map +1 -0
- package/lib/scrollHandler/scrollParent.d.ts +26 -0
- package/lib/scrollHandler/scrollParent.d.ts.map +1 -0
- package/lib/scrollHandler/scrollParent.js +79 -0
- package/lib/scrollHandler/scrollParent.js.map +1 -0
- package/lib/scrollHandler.d.ts +20 -0
- package/lib/scrollHandler.d.ts.map +1 -0
- package/lib/scrollHandler.js +41 -0
- package/lib/scrollHandler.js.map +1 -0
- package/lib/selectNodeHandler.d.ts +38 -0
- package/lib/selectNodeHandler.d.ts.map +1 -0
- package/lib/selectNodeHandler.js +166 -0
- package/lib/selectNodeHandler.js.map +1 -0
- package/lib/setDefaultOptions.d.ts +4 -0
- package/lib/setDefaultOptions.d.ts.map +1 -0
- package/lib/setDefaultOptions.js +86 -0
- package/lib/setDefaultOptions.js.map +1 -0
- package/lib/triggerCustomEvent.d.ts +3 -0
- package/lib/triggerCustomEvent.d.ts.map +1 -0
- package/lib/triggerCustomEvent.js +13 -0
- package/lib/triggerCustomEvent.js.map +1 -0
- package/lib/util.d.ts +3 -0
- package/lib/util.d.ts.map +1 -0
- package/lib/util.js +5 -0
- package/lib/util.js.map +1 -0
- package/lib/version.d.ts +3 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +5 -0
- package/lib/version.js.map +1 -0
- package/package.json +110 -0
- package/src/animation.ts +48 -0
- package/src/classNames.ts +70 -0
- package/src/dataLoader.ts +92 -0
- package/src/dragAndDropHandler/binarySearch.ts +27 -0
- package/src/dragAndDropHandler/dragElement.ts +62 -0
- package/src/dragAndDropHandler/generateHitAreas.ts +195 -0
- package/src/dragAndDropHandler/index.ts +442 -0
- package/src/dragAndDropHandler/iterateVisibleNodes.ts +91 -0
- package/src/dragAndDropHandler/types.ts +12 -0
- package/src/elementsRenderer.ts +367 -0
- package/src/events.ts +237 -0
- package/src/header.txt +16 -0
- package/src/index.ts +1290 -0
- package/src/keyHandler.ts +139 -0
- package/src/methodTypes.ts +44 -0
- package/src/mouseHandler.ts +391 -0
- package/src/mouseUtils.ts +23 -0
- package/src/node.ts +831 -0
- package/src/nodeElement/borderDropHint.ts +37 -0
- package/src/nodeElement/folderElement.ts +140 -0
- package/src/nodeElement/ghostDropHint.ts +84 -0
- package/src/nodeElement/index.ts +103 -0
- package/src/nodeUtils.ts +12 -0
- package/src/options.ts +325 -0
- package/src/positionUtils.ts +18 -0
- package/src/requestUrl.ts +41 -0
- package/src/saveStateHandler.ts +279 -0
- package/src/scrollHandler/containerScrollParent.ts +71 -0
- package/src/scrollHandler/createScrollParent.ts +51 -0
- package/src/scrollHandler/documentScrollParent.ts +104 -0
- package/src/scrollHandler/scrollParent.ts +117 -0
- package/src/scrollHandler.ts +55 -0
- package/src/selectNodeHandler.ts +192 -0
- package/src/setDefaultOptions.ts +79 -0
- package/src/triggerCustomEvent.ts +14 -0
- package/src/util.ts +5 -0
- package/src/version.ts +4 -0
- package/tree_element.css +195 -0
- package/tree_element.debug.js +4153 -0
- package/tree_element.debug.js.map +1 -0
- package/tree_element.js +21 -0
- package/tree_element.js.map +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ClassNames } from "../classNames";
|
|
2
|
+
import type { DropHint } from "../dragAndDropHandler/types";
|
|
3
|
+
|
|
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}`);
|
|
13
|
+
|
|
14
|
+
if (!div) {
|
|
15
|
+
this.hint = undefined;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const width = Math.max(element.offsetWidth + scrollLeft - 4, 0);
|
|
20
|
+
const height = Math.max(element.clientHeight - 4, 0);
|
|
21
|
+
|
|
22
|
+
const hint = document.createElement("span");
|
|
23
|
+
hint.className = classNames.border;
|
|
24
|
+
hint.style.width = `${width}px`;
|
|
25
|
+
hint.style.height = `${height}px`;
|
|
26
|
+
|
|
27
|
+
this.hint = hint;
|
|
28
|
+
|
|
29
|
+
div.append(this.hint);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public remove(): void {
|
|
33
|
+
this.hint?.remove();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default BorderDropHint;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { AnimationSpeed } from "../animation";
|
|
2
|
+
import type { TriggerEvent } from "../methodTypes";
|
|
3
|
+
import type { Position } from "../node";
|
|
4
|
+
import type { NodeElementParams } from "./index";
|
|
5
|
+
|
|
6
|
+
import { slideDown, slideUp } from "../animation";
|
|
7
|
+
import NodeElement from "./index";
|
|
8
|
+
|
|
9
|
+
interface FolderElementParams extends NodeElementParams {
|
|
10
|
+
closedIconElement?: HTMLElement | Text;
|
|
11
|
+
openedIconElement?: HTMLElement | Text;
|
|
12
|
+
triggerEvent: TriggerEvent;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
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;
|
|
41
|
+
}
|
|
42
|
+
|
|
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
|
+
};
|
|
71
|
+
|
|
72
|
+
const ul = this.getUl();
|
|
73
|
+
|
|
74
|
+
if (slide) {
|
|
75
|
+
slideUp(ul, animationSpeed, doClose);
|
|
76
|
+
} else {
|
|
77
|
+
ul.style.display = "none";
|
|
78
|
+
doClose();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
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
|
+
}
|
|
104
|
+
|
|
105
|
+
const doOpen = (): void => {
|
|
106
|
+
this.element.classList.remove(this.classNames.closed);
|
|
107
|
+
|
|
108
|
+
const titleSpan = this.getTitleSpan();
|
|
109
|
+
titleSpan.setAttribute("aria-expanded", "true");
|
|
110
|
+
|
|
111
|
+
this.triggerEvent("tree.open", {
|
|
112
|
+
node: this.node,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
resolve();
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const ul = this.getUl();
|
|
119
|
+
|
|
120
|
+
if (slide) {
|
|
121
|
+
slideDown(ul, animationSpeed, doOpen);
|
|
122
|
+
} else {
|
|
123
|
+
ul.style.display = "block";
|
|
124
|
+
doOpen();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected mustShowBorderDropHint(position: Position): boolean {
|
|
130
|
+
return !this.node.is_open && position === "inside";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private getButton(): HTMLLinkElement {
|
|
134
|
+
return this.element.querySelector(
|
|
135
|
+
`:scope > .${this.classNames.element} > a.${this.classNames.toggler}`,
|
|
136
|
+
) as HTMLLinkElement;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default FolderElement;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ClassNames } from "../classNames";
|
|
2
|
+
import type { DropHint } from "../dragAndDropHandler/types";
|
|
3
|
+
import type { Node, Position } from "../node";
|
|
4
|
+
|
|
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
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public remove(): void {
|
|
42
|
+
this.ghost.remove();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private createGhostElement() {
|
|
46
|
+
const { circle, common, ghost: ghostClass, line } = this.classNames;
|
|
47
|
+
|
|
48
|
+
const ghost = document.createElement("li");
|
|
49
|
+
ghost.className = `${common} ${ghostClass}`;
|
|
50
|
+
|
|
51
|
+
const circleSpan = document.createElement("span");
|
|
52
|
+
circleSpan.className = `${common} ${circle}`;
|
|
53
|
+
ghost.append(circleSpan);
|
|
54
|
+
|
|
55
|
+
const lineSpan = document.createElement("span");
|
|
56
|
+
lineSpan.className = `${common} ${line}`;
|
|
57
|
+
ghost.append(lineSpan);
|
|
58
|
+
|
|
59
|
+
return ghost;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private moveAfter(): void {
|
|
63
|
+
this.element.after(this.ghost);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private moveBefore(): void {
|
|
67
|
+
this.element.before(this.ghost);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private moveInside(): void {
|
|
71
|
+
this.element.after(this.ghost);
|
|
72
|
+
this.ghost.classList.add(this.classNames.inside);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private moveInsideOpenFolder(): void {
|
|
76
|
+
const childElement = this.node.children[0]?.element;
|
|
77
|
+
|
|
78
|
+
if (childElement) {
|
|
79
|
+
childElement.before(this.ghost);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default GhostDropHint;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ClassNames } from "../classNames";
|
|
2
|
+
import type { DropHint } from "../dragAndDropHandler/types";
|
|
3
|
+
import type { GetScrollLeft } from "../methodTypes";
|
|
4
|
+
import type { Node, Position } from "../node";
|
|
5
|
+
|
|
6
|
+
import BorderDropHint from "./borderDropHint";
|
|
7
|
+
import GhostDropHint from "./ghostDropHint";
|
|
8
|
+
|
|
9
|
+
export interface NodeElementParams {
|
|
10
|
+
classNames: ClassNames;
|
|
11
|
+
getScrollLeft: GetScrollLeft;
|
|
12
|
+
node: Node;
|
|
13
|
+
tabIndex?: number;
|
|
14
|
+
treeElement: HTMLElement;
|
|
15
|
+
}
|
|
16
|
+
|
|
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
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
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
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
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
|
+
}
|
|
69
|
+
|
|
70
|
+
public select(mustSetFocus: boolean): void {
|
|
71
|
+
this.element.classList.add(this.classNames.selected);
|
|
72
|
+
|
|
73
|
+
const titleSpan = this.getTitleSpan();
|
|
74
|
+
const tabIndex = this.tabIndex;
|
|
75
|
+
|
|
76
|
+
// Check for null or undefined
|
|
77
|
+
if (tabIndex != null) {
|
|
78
|
+
titleSpan.setAttribute("tabindex", tabIndex.toString());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
titleSpan.setAttribute("aria-selected", "true");
|
|
82
|
+
|
|
83
|
+
if (mustSetFocus) {
|
|
84
|
+
titleSpan.focus();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected getTitleSpan(): HTMLSpanElement {
|
|
89
|
+
return this.element.querySelector(
|
|
90
|
+
`:scope > .${this.classNames.element} > span.${this.classNames.title}`,
|
|
91
|
+
) as HTMLSpanElement;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected getUl(): HTMLUListElement {
|
|
95
|
+
return this.element.querySelector(":scope > ul") as HTMLUListElement;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected mustShowBorderDropHint(position: Position): boolean {
|
|
99
|
+
return position === "inside";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default NodeElement;
|
package/src/nodeUtils.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NodeData, NodeRecord } from "./node";
|
|
2
|
+
|
|
3
|
+
interface NodeRecordWithChildren extends NodeRecord {
|
|
4
|
+
children: NodeData[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const isNodeRecordWithChildren = (
|
|
8
|
+
data: NodeData,
|
|
9
|
+
): data is NodeRecordWithChildren =>
|
|
10
|
+
typeof data === "object" &&
|
|
11
|
+
"children" in data &&
|
|
12
|
+
data.children instanceof Array;
|
package/src/options.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import type { AnimationSpeed } from "./animation";
|
|
2
|
+
import type { Node, NodeData } from "./node";
|
|
3
|
+
|
|
4
|
+
export type DataFilter = (data: unknown) => NodeData[];
|
|
5
|
+
|
|
6
|
+
export type DataUrl = DataUrlFunction | string;
|
|
7
|
+
|
|
8
|
+
export type DragMethod = (node: Node, event: Event | Touch) => void;
|
|
9
|
+
|
|
10
|
+
export type IconElement = HTMLElement | string;
|
|
11
|
+
|
|
12
|
+
export type OnCanMove = ((node: Node) => boolean) | undefined;
|
|
13
|
+
|
|
14
|
+
export type OnCanMoveTo = (
|
|
15
|
+
node: Node,
|
|
16
|
+
targetNode: Node,
|
|
17
|
+
positionName: string,
|
|
18
|
+
) => boolean;
|
|
19
|
+
|
|
20
|
+
export type OnCanSelectNode = (node: Node) => boolean
|
|
21
|
+
|
|
22
|
+
export type OnCreateLi = (node: Node, el: HTMLElement, isSelected: boolean) => void;
|
|
23
|
+
|
|
24
|
+
export type OnGetStateFromStorage = (() => string) | undefined;
|
|
25
|
+
|
|
26
|
+
export type OnIsMoveHandle = (el: HTMLElement) => boolean;
|
|
27
|
+
|
|
28
|
+
export type OnSetStateFromStorage = ((data: string) => void) | undefined;
|
|
29
|
+
|
|
30
|
+
export interface TreeElementOptions {
|
|
31
|
+
/**
|
|
32
|
+
* The duration of the slide animation: `"fast"` is 200ms, `"slow"` is
|
|
33
|
+
* 600ms, a number is milliseconds.
|
|
34
|
+
*
|
|
35
|
+
* @defaultValue `"fast"`
|
|
36
|
+
* @group Appearance
|
|
37
|
+
*/
|
|
38
|
+
animationSpeed: AnimationSpeed;
|
|
39
|
+
/**
|
|
40
|
+
* Escape node names. With `false`, names are inserted as html — only do
|
|
41
|
+
* that for data you control.
|
|
42
|
+
*
|
|
43
|
+
* @defaultValue `true`
|
|
44
|
+
* @group Data
|
|
45
|
+
*/
|
|
46
|
+
autoEscape: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Which levels are open initially: `false` none, `true` all, a number that
|
|
49
|
+
* many levels starting at `0` (so `0` opens the top level, `1` the first
|
|
50
|
+
* two levels).
|
|
51
|
+
*
|
|
52
|
+
* @defaultValue `false`
|
|
53
|
+
* @group Appearance
|
|
54
|
+
*/
|
|
55
|
+
autoOpen: boolean | number;
|
|
56
|
+
/**
|
|
57
|
+
* Put the open/close button before the node title. With `false` it goes
|
|
58
|
+
* after it.
|
|
59
|
+
*
|
|
60
|
+
* @defaultValue `true`
|
|
61
|
+
* @group Appearance
|
|
62
|
+
*/
|
|
63
|
+
buttonLeft: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The prefix of the css classes the widget puts on the elements it
|
|
66
|
+
* creates: with `"my-tree"` a title gets `my-tree-title` instead of
|
|
67
|
+
* `tree-element-title`. The bundled stylesheet uses the default prefix, so
|
|
68
|
+
* change it only if you bring your own css. See
|
|
69
|
+
* [Styling](/guide/styling#changing-the-class-names).
|
|
70
|
+
*
|
|
71
|
+
* @defaultValue `"tree-element"`
|
|
72
|
+
* @group Appearance
|
|
73
|
+
*/
|
|
74
|
+
classPrefix: string;
|
|
75
|
+
/**
|
|
76
|
+
* The icon of a closed folder. A string is inserted as html; an element is
|
|
77
|
+
* cloned for every node.
|
|
78
|
+
*
|
|
79
|
+
* Type: `HTMLElement | string`
|
|
80
|
+
*
|
|
81
|
+
* @defaultValue `"►"` (`►`), or `"◀"` (`◀`) when `rtl` is
|
|
82
|
+
* true.
|
|
83
|
+
* @group Appearance
|
|
84
|
+
*/
|
|
85
|
+
closedIcon?: IconElement;
|
|
86
|
+
/**
|
|
87
|
+
* The class that every element of the widget gets.
|
|
88
|
+
*
|
|
89
|
+
* @defaultValue `"<classPrefix>-common"`
|
|
90
|
+
* @group Appearance
|
|
91
|
+
*/
|
|
92
|
+
commonClassName?: string;
|
|
93
|
+
/**
|
|
94
|
+
* The nodes of the tree. See [Data](/guide/data) for the format.
|
|
95
|
+
*
|
|
96
|
+
* @group Data
|
|
97
|
+
*/
|
|
98
|
+
data?: NodeData[];
|
|
99
|
+
/**
|
|
100
|
+
* Transforms a response from `dataUrl` into node data. Use it when the
|
|
101
|
+
* server wraps the nodes in another object.
|
|
102
|
+
*
|
|
103
|
+
* Type: `(data: unknown) => NodeData[]`
|
|
104
|
+
*
|
|
105
|
+
* @group Data
|
|
106
|
+
*/
|
|
107
|
+
dataFilter?: DataFilter;
|
|
108
|
+
/**
|
|
109
|
+
* A url to fetch the data from, or a function that returns one. The
|
|
110
|
+
* function is called with the node that is being loaded, and without an
|
|
111
|
+
* argument for the initial load. See
|
|
112
|
+
* [Loading on demand](/guide/loading-on-demand).
|
|
113
|
+
*
|
|
114
|
+
* Type: `string | ((node?: Node) => string)`
|
|
115
|
+
*
|
|
116
|
+
* @defaultValue The element's `data-url` attribute.
|
|
117
|
+
* @group Data
|
|
118
|
+
*/
|
|
119
|
+
dataUrl?: DataUrl;
|
|
120
|
+
/**
|
|
121
|
+
* Let the user move nodes by dragging them.
|
|
122
|
+
*
|
|
123
|
+
* @defaultValue `false`
|
|
124
|
+
* @group Drag and drop
|
|
125
|
+
*/
|
|
126
|
+
dragAndDrop: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Handle the arrow keys when the focus is on the tree. See
|
|
129
|
+
* [Selection](/guide/selection#keyboard-navigation).
|
|
130
|
+
*
|
|
131
|
+
* @defaultValue `true`
|
|
132
|
+
* @group Selection
|
|
133
|
+
*/
|
|
134
|
+
keyboardSupport: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* The class used to create nodes. Subclass [`Node`](/reference/node) to
|
|
137
|
+
* add your own methods. The `Node` class is not exposed on the global
|
|
138
|
+
* `TreeElement`, so this option needs the `lib` build.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```js
|
|
142
|
+
* import TreeElement from "tree-element/lib/index.js";
|
|
143
|
+
* import { Node } from "tree-element/lib/node.js";
|
|
144
|
+
*
|
|
145
|
+
* class MyNode extends Node {
|
|
146
|
+
* fullName() {
|
|
147
|
+
* return this.getParent()
|
|
148
|
+
* ? `${this.getParent().name}/${this.name}`
|
|
149
|
+
* : this.name;
|
|
150
|
+
* }
|
|
151
|
+
* }
|
|
152
|
+
*
|
|
153
|
+
* new TreeElement({ data, htmlElement, nodeClass: MyNode });
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @defaultValue `Node`
|
|
157
|
+
* @group Advanced
|
|
158
|
+
*/
|
|
159
|
+
nodeClass: typeof Node;
|
|
160
|
+
/**
|
|
161
|
+
* Called before a drag starts. Return `false` to keep the node in place.
|
|
162
|
+
*
|
|
163
|
+
* Type: `(node: Node) => boolean`
|
|
164
|
+
*
|
|
165
|
+
* @group Drag and drop
|
|
166
|
+
*/
|
|
167
|
+
onCanMove?: OnCanMove;
|
|
168
|
+
/**
|
|
169
|
+
* Called for a possible drop. `position` is `"before"`, `"after"` or
|
|
170
|
+
* `"inside"`. Return `false` to refuse the drop.
|
|
171
|
+
*
|
|
172
|
+
* Type: `(movedNode: Node, targetNode: Node, position: string) => boolean`
|
|
173
|
+
*
|
|
174
|
+
* @group Drag and drop
|
|
175
|
+
*/
|
|
176
|
+
onCanMoveTo?: OnCanMoveTo;
|
|
177
|
+
/**
|
|
178
|
+
* Called before a node is selected. Return `false` to refuse.
|
|
179
|
+
*
|
|
180
|
+
* Type: `(node: Node) => boolean`
|
|
181
|
+
*
|
|
182
|
+
* @group Selection
|
|
183
|
+
*/
|
|
184
|
+
onCanSelectNode?: OnCanSelectNode;
|
|
185
|
+
/**
|
|
186
|
+
* Called after the `li` of a node is created, so you can add to it. See
|
|
187
|
+
* [Styling](/guide/styling#customizing-the-markup).
|
|
188
|
+
*
|
|
189
|
+
* Type: `(node: Node, li: HTMLElement, isSelected: boolean) => void`
|
|
190
|
+
*
|
|
191
|
+
* @group Appearance
|
|
192
|
+
*/
|
|
193
|
+
onCreateLi?: OnCreateLi;
|
|
194
|
+
/**
|
|
195
|
+
* Called while a node is being dragged.
|
|
196
|
+
*
|
|
197
|
+
* Type: `(node: Node, event: Event | Touch) => void`
|
|
198
|
+
*
|
|
199
|
+
* @group Drag and drop
|
|
200
|
+
*/
|
|
201
|
+
onDragMove?: DragMethod;
|
|
202
|
+
/**
|
|
203
|
+
* Called when a drag ends.
|
|
204
|
+
*
|
|
205
|
+
* Type: `(node: Node, event: Event | Touch) => void`
|
|
206
|
+
*
|
|
207
|
+
* @group Drag and drop
|
|
208
|
+
*/
|
|
209
|
+
onDragStop?: DragMethod;
|
|
210
|
+
/**
|
|
211
|
+
* Read the saved state, as a json string, from somewhere other than
|
|
212
|
+
* `localStorage`.
|
|
213
|
+
*
|
|
214
|
+
* Type: `() => string`
|
|
215
|
+
*
|
|
216
|
+
* @group State
|
|
217
|
+
*/
|
|
218
|
+
onGetStateFromStorage?: OnGetStateFromStorage;
|
|
219
|
+
/**
|
|
220
|
+
* Decides whether an element inside a node starts a drag. Without it, the
|
|
221
|
+
* whole node is a handle.
|
|
222
|
+
*
|
|
223
|
+
* Type: `(element: HTMLElement) => boolean`
|
|
224
|
+
*
|
|
225
|
+
* @group Drag and drop
|
|
226
|
+
*/
|
|
227
|
+
onIsMoveHandle?: OnIsMoveHandle;
|
|
228
|
+
/**
|
|
229
|
+
* Write the state, as a json string, to somewhere other than
|
|
230
|
+
* `localStorage`.
|
|
231
|
+
*
|
|
232
|
+
* Type: `(state: string) => void`
|
|
233
|
+
*
|
|
234
|
+
* @group State
|
|
235
|
+
*/
|
|
236
|
+
onSetStateFromStorage?: OnSetStateFromStorage;
|
|
237
|
+
/**
|
|
238
|
+
* The icon of an open folder.
|
|
239
|
+
*
|
|
240
|
+
* Type: `HTMLElement | string`
|
|
241
|
+
*
|
|
242
|
+
* @defaultValue `"▼"` (`▼`)
|
|
243
|
+
* @group Appearance
|
|
244
|
+
*/
|
|
245
|
+
openedIcon?: IconElement;
|
|
246
|
+
/**
|
|
247
|
+
* How long, in milliseconds, a closed folder has to be hovered during a
|
|
248
|
+
* drag before it opens. `false` never opens folders while dragging.
|
|
249
|
+
*
|
|
250
|
+
* @defaultValue `500`
|
|
251
|
+
* @group Drag and drop
|
|
252
|
+
*/
|
|
253
|
+
openFolderDelay: false | number;
|
|
254
|
+
/**
|
|
255
|
+
* Mirror the tree for right-to-left languages.
|
|
256
|
+
*
|
|
257
|
+
* @defaultValue The element's `data-rtl` attribute, otherwise `false`.
|
|
258
|
+
* @group Appearance
|
|
259
|
+
*/
|
|
260
|
+
rtl?: boolean;
|
|
261
|
+
/**
|
|
262
|
+
* Remember the open and selected nodes in `localStorage`. A string is used
|
|
263
|
+
* as the storage key; `true` uses `tree`. See
|
|
264
|
+
* [Saving state](/guide/saving-state).
|
|
265
|
+
*
|
|
266
|
+
* @defaultValue `false`
|
|
267
|
+
* @group State
|
|
268
|
+
*/
|
|
269
|
+
saveState: boolean | string;
|
|
270
|
+
/**
|
|
271
|
+
* Allow the user to select nodes.
|
|
272
|
+
*
|
|
273
|
+
* @defaultValue `true`
|
|
274
|
+
* @group Selection
|
|
275
|
+
*/
|
|
276
|
+
selectable: boolean;
|
|
277
|
+
/**
|
|
278
|
+
* Render a node with `children: []` as a folder instead of as a leaf.
|
|
279
|
+
*
|
|
280
|
+
* @defaultValue `false`
|
|
281
|
+
* @group Appearance
|
|
282
|
+
*/
|
|
283
|
+
showEmptyFolder: boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Animate opening and closing folders.
|
|
286
|
+
*
|
|
287
|
+
* @defaultValue `true`
|
|
288
|
+
* @group Appearance
|
|
289
|
+
*/
|
|
290
|
+
slide: boolean;
|
|
291
|
+
/**
|
|
292
|
+
* How long, in milliseconds, the mouse has to be held down on a node
|
|
293
|
+
* before a drag starts.
|
|
294
|
+
*
|
|
295
|
+
* @defaultValue `300`
|
|
296
|
+
* @group Drag and drop
|
|
297
|
+
*/
|
|
298
|
+
startDndDelay?: number;
|
|
299
|
+
/**
|
|
300
|
+
* The `tabindex` of the selected node's title, which is what makes the
|
|
301
|
+
* tree focusable.
|
|
302
|
+
*
|
|
303
|
+
* @defaultValue `0`
|
|
304
|
+
* @group Selection
|
|
305
|
+
*/
|
|
306
|
+
tabIndex?: number;
|
|
307
|
+
/**
|
|
308
|
+
* The class of the root `ul`.
|
|
309
|
+
*
|
|
310
|
+
* @defaultValue The value of `classPrefix`.
|
|
311
|
+
* @group Appearance
|
|
312
|
+
*/
|
|
313
|
+
treeClassName?: string;
|
|
314
|
+
/**
|
|
315
|
+
* Handle right clicks on a node: the browser menu is suppressed and
|
|
316
|
+
* [`tree.contextmenu`](/reference/events#tree-contextmenu) is dispatched
|
|
317
|
+
* instead.
|
|
318
|
+
*
|
|
319
|
+
* @defaultValue `true`
|
|
320
|
+
* @group Selection
|
|
321
|
+
*/
|
|
322
|
+
useContextMenu: boolean;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
type DataUrlFunction = (node?: Node) => string;
|