tree-element 0.3.2 → 0.4.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 +1 -0
- 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 +6 -6
- package/lib/dataLoader.js.map +1 -1
- package/lib/elementsRenderer.d.ts +4 -2
- package/lib/elementsRenderer.d.ts.map +1 -1
- package/lib/elementsRenderer.js +35 -21
- package/lib/elementsRenderer.js.map +1 -1
- package/lib/index.d.ts +10 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +75 -18
- package/lib/index.js.map +1 -1
- package/lib/keyHandler.d.ts +14 -3
- package/lib/keyHandler.d.ts.map +1 -1
- package/lib/keyHandler.js +61 -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/nodeElement/folderElement.d.ts +1 -1
- package/lib/nodeElement/folderElement.d.ts.map +1 -1
- package/lib/nodeElement/folderElement.js +4 -4
- 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 +13 -0
- package/lib/options.d.ts.map +1 -1
- package/lib/selectNodeHandler.d.ts +3 -0
- package/lib/selectNodeHandler.d.ts.map +1 -1
- package/lib/selectNodeHandler.js +54 -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 +19 -19
- package/src/classNames.ts +4 -4
- package/src/dataLoader.ts +6 -9
- package/src/elementsRenderer.ts +42 -24
- package/src/index.ts +83 -24
- package/src/keyHandler.ts +84 -34
- package/src/methodTypes.ts +6 -0
- package/src/nodeElement/folderElement.ts +4 -4
- package/src/nodeElement/index.ts +20 -17
- package/src/options.ts +13 -0
- package/src/selectNodeHandler.ts +58 -25
- package/src/setDefaultOptions.ts +1 -0
- package/src/version.ts +1 -1
- package/tree_element.css +16 -0
- package/tree_element.debug.js +256 -120
- package/tree_element.debug.js.map +1 -1
- package/tree_element.js +2 -2
- package/tree_element.js.map +1 -1
package/lib/selectNodeHandler.js
CHANGED
|
@@ -75,9 +75,9 @@ class SelectNodeHandler {
|
|
|
75
75
|
} else {
|
|
76
76
|
this._selectedNodes.delete(node.id);
|
|
77
77
|
if (includeChildren) {
|
|
78
|
-
node.iterate(
|
|
79
|
-
if (
|
|
80
|
-
this._selectedNodes.delete(
|
|
78
|
+
node.iterate(child => {
|
|
79
|
+
if (child.id != null) {
|
|
80
|
+
this._selectedNodes.delete(child.id);
|
|
81
81
|
}
|
|
82
82
|
return true;
|
|
83
83
|
});
|
|
@@ -96,6 +96,9 @@ class SelectNodeHandler {
|
|
|
96
96
|
* mustToggle: support deselecting the selected node
|
|
97
97
|
*/
|
|
98
98
|
_selectSingleNode(node, optionsParam) {
|
|
99
|
+
if (!this._canSelect(node)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
99
102
|
let defaultOptions = {
|
|
100
103
|
mustSetFocus: true,
|
|
101
104
|
mustToggle: true
|
|
@@ -104,31 +107,23 @@ class SelectNodeHandler {
|
|
|
104
107
|
...defaultOptions,
|
|
105
108
|
...optionsParam
|
|
106
109
|
};
|
|
107
|
-
let
|
|
108
|
-
if (!this._getSelectable()) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
let onCanSelectNode = this._getOnCanSelectNode();
|
|
112
|
-
return onCanSelectNode ? onCanSelectNode(node) : true;
|
|
113
|
-
};
|
|
114
|
-
if (!canSelect()) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
let deselectCurrentNode = deselectedNode => {
|
|
118
|
-
this._removeFromSelection(deselectedNode);
|
|
119
|
-
this._getNodeElementForNode(deselectedNode)._deselect();
|
|
120
|
-
};
|
|
110
|
+
let otherSelectedNodes = this._getSelectedNodes().filter(selectedNode => selectedNode !== node);
|
|
121
111
|
if (this._isNodeSelected(node)) {
|
|
122
|
-
if (
|
|
123
|
-
|
|
112
|
+
if (otherSelectedNodes.length) {
|
|
113
|
+
// Part of a multiple selection: keep only this node selected
|
|
114
|
+
for (let otherNode of otherSelectedNodes) {
|
|
115
|
+
this._deselectNode(otherNode);
|
|
116
|
+
}
|
|
117
|
+
} else if (selectOptions.mustToggle) {
|
|
118
|
+
this._deselectNode(node);
|
|
124
119
|
this._triggerEvent("tree.deselect", {
|
|
125
120
|
node
|
|
126
121
|
});
|
|
127
122
|
}
|
|
128
123
|
} else {
|
|
129
|
-
let deselectedNode =
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
let deselectedNode = otherSelectedNodes[0] ?? null;
|
|
125
|
+
for (let otherNode of otherSelectedNodes) {
|
|
126
|
+
this._deselectNode(otherNode);
|
|
132
127
|
}
|
|
133
128
|
this._addToSelection(node);
|
|
134
129
|
this._openParents(node);
|
|
@@ -141,6 +136,43 @@ class SelectNodeHandler {
|
|
|
141
136
|
this._updateTabStop();
|
|
142
137
|
this._saveState();
|
|
143
138
|
}
|
|
139
|
+
|
|
140
|
+
/* Add a node to the selection, or remove it when it is selected, without
|
|
141
|
+
* changing the other selected nodes.
|
|
142
|
+
* Checks if the node is selectable and saves the state.
|
|
143
|
+
*/
|
|
144
|
+
_toggleNodeInSelection(node, mustSetFocus = true) {
|
|
145
|
+
if (!this._canSelect(node)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (this._isNodeSelected(node)) {
|
|
149
|
+
this._deselectNode(node);
|
|
150
|
+
this._triggerEvent("tree.deselect", {
|
|
151
|
+
node
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
this._addToSelection(node);
|
|
155
|
+
this._openParents(node);
|
|
156
|
+
this._getNodeElementForNode(node)._select(mustSetFocus);
|
|
157
|
+
this._triggerEvent("tree.select", {
|
|
158
|
+
deselectedNode: null,
|
|
159
|
+
node
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
this._updateTabStop();
|
|
163
|
+
this._saveState();
|
|
164
|
+
}
|
|
165
|
+
_canSelect(node) {
|
|
166
|
+
if (!this._getSelectable()) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
let onCanSelectNode = this._getOnCanSelectNode();
|
|
170
|
+
return onCanSelectNode ? onCanSelectNode(node) : true;
|
|
171
|
+
}
|
|
172
|
+
_deselectNode(node) {
|
|
173
|
+
this._removeFromSelection(node);
|
|
174
|
+
this._getNodeElementForNode(node)._deselect();
|
|
175
|
+
}
|
|
144
176
|
}
|
|
145
177
|
|
|
146
178
|
export { SelectNodeHandler as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectNodeHandler.js","sources":["../src/selectNodeHandler.ts"],"sourcesContent":["import type {\n GetNodeById,\n GetNodeElementForNode,\n OpenParents,\n TriggerEvent,\n} from \"./methodTypes\";\nimport type { Node, NodeId } from \"./node\";\nimport type { OnCanSelectNode } from \"./options\";\n\nexport interface SelectNodeOptions {\n mustSetFocus?: boolean;\n mustToggle?: boolean;\n}\n\ninterface SelectNodeHandlerParameters {\n getNodeById: GetNodeById;\n getNodeElementForNode: GetNodeElementForNode;\n getOnCanSelectNode: () => OnCanSelectNode | undefined;\n getSelectable: () => boolean;\n openParents: OpenParents;\n saveState: () => void;\n triggerEvent: TriggerEvent;\n updateTabStop: () => void;\n}\n\nexport default class SelectNodeHandler {\n private getNodeById: GetNodeById;\n private getNodeElementForNode: GetNodeElementForNode;\n private getOnCanSelectNode: () => OnCanSelectNode | undefined;\n private getSelectable: () => boolean;\n private openParents: OpenParents;\n private saveState: () => void;\n private selectedNodes: Set<NodeId>;\n private selectedSingleNode: Node | null;\n private triggerEvent: TriggerEvent;\n private updateTabStop: () => void;\n\n constructor({\n getNodeById,\n getNodeElementForNode,\n getOnCanSelectNode,\n getSelectable,\n openParents,\n saveState,\n triggerEvent,\n updateTabStop,\n }: SelectNodeHandlerParameters) {\n this.getNodeById = getNodeById;\n this.getNodeElementForNode = getNodeElementForNode;\n this.getOnCanSelectNode = getOnCanSelectNode;\n this.getSelectable = getSelectable;\n this.openParents = openParents;\n this.saveState = saveState;\n this.selectedNodes = new Set<NodeId>();\n this.selectedSingleNode = null;\n this.triggerEvent = triggerEvent;\n this.updateTabStop = updateTabStop;\n }\n\n public addToSelection(node: Node): void {\n if (node.id == null) {\n this.selectedSingleNode = node;\n } else {\n this.selectedNodes.add(node.id);\n }\n }\n\n public clear(): void {\n this.selectedNodes.clear();\n this.selectedSingleNode = null;\n }\n\n public getSelectedNode(): Node | null {\n const selectedNodes = this.getSelectedNodes();\n\n return selectedNodes.length ? (selectedNodes[0] ?? null) : null;\n }\n\n public getSelectedNodes(): Node[] {\n if (this.selectedSingleNode) {\n return [this.selectedSingleNode];\n }\n\n const selectedNodes: Node[] = [];\n\n this.selectedNodes.forEach((id) => {\n const node = this.getNodeById(id);\n if (node) {\n selectedNodes.push(node);\n }\n });\n\n return selectedNodes;\n }\n\n public getSelectedNodesUnder(parent: Node): Node[] {\n if (this.selectedSingleNode) {\n return parent.isParentOf(this.selectedSingleNode)\n ? [this.selectedSingleNode]\n : [];\n }\n\n const selectedNodes: Node[] = [];\n\n this.selectedNodes.forEach((id) => {\n const node = this.getNodeById(id);\n if (node && parent.isParentOf(node)) {\n selectedNodes.push(node);\n }\n });\n\n return selectedNodes;\n }\n\n public isNodeSelected(node: Node): boolean {\n if (node.id != null) {\n return this.selectedNodes.has(node.id);\n }\n return this.selectedSingleNode ? this.selectedSingleNode === node : false;\n }\n\n public removeFromSelection(node: Node, includeChildren = false): void {\n if (node.id == null) {\n if (this.selectedSingleNode === node) {\n this.selectedSingleNode = null;\n }\n } else {\n this.selectedNodes.delete(node.id);\n\n if (includeChildren) {\n node.iterate(() => {\n if (node.id != null) {\n this.selectedNodes.delete(node.id);\n }\n return true;\n });\n }\n }\n }\n\n /* Select a single node.\n * Renders the changed elements.\n * Deselects if the node is currently selected (if the mustToggle is on).\n * Deselects the previously selected node.\n * Check if the node is selectable.\n * Saves the state.\n * Options:\n * mustSetFocus: set the focus to the selected node\n * mustToggle: support deselecting the selected node\n */\n public selectSingleNode(node: Node, optionsParam?: SelectNodeOptions) {\n const defaultOptions = { mustSetFocus: true, mustToggle: true };\n const selectOptions = { ...defaultOptions, ...optionsParam };\n\n const canSelect = (): boolean => {\n if (!this.getSelectable()) {\n return false;\n }\n\n const onCanSelectNode = this.getOnCanSelectNode();\n return onCanSelectNode ? onCanSelectNode(node) : true;\n };\n\n if (!canSelect()) {\n return;\n }\n\n const deselectCurrentNode = (deselectedNode: Node) => {\n this.removeFromSelection(deselectedNode);\n this.getNodeElementForNode(deselectedNode).deselect();\n };\n\n if (this.isNodeSelected(node)) {\n if (selectOptions.mustToggle) {\n deselectCurrentNode(node);\n this.triggerEvent(\"tree.deselect\", { node });\n }\n } else {\n const deselectedNode = this.getSelectedNode();\n\n if (deselectedNode) {\n deselectCurrentNode(deselectedNode);\n }\n\n this.addToSelection(node);\n this.openParents(node);\n this.getNodeElementForNode(node).select(selectOptions.mustSetFocus);\n\n this.triggerEvent(\"tree.select\", {\n deselectedNode,\n node,\n });\n }\n\n this.updateTabStop();\n this.saveState();\n }\n}\n"],"names":["getNodeById","getNodeElementForNode","getOnCanSelectNode","getSelectable","openParents","saveState","selectedNodes","selectedSingleNode","triggerEvent","updateTabStop","addToSelection","clear","getSelectedNode","getSelectedNodes","getSelectedNodesUnder","isNodeSelected","removeFromSelection","selectSingleNode","deselect","select"],"mappings":"AAyBe,MAAM,iBAAiB,CAAC;AAYrC,EAAA,WAAW,CAAC;IACV,YAAA,EAAA,WAAW;IACX,sBAAA,EAAA,qBAAqB;IACrB,mBAAA,EAAA,kBAAkB;IAClB,cAAA,EAAA,aAAa;IACb,YAAA,EAAA,WAAW;IACX,UAAA,EAAA,SAAS;IACT,aAAA,EAAA,YAAY;AACZ,IAAA,cAAA,EAAA;AAC2B,GAAC,EAAE;IAC9B,IAAI,CAACA,YAAW,GAAG,WAAW;IAC9B,IAAI,CAACC,sBAAqB,GAAG,qBAAqB;IAClD,IAAI,CAACC,mBAAkB,GAAG,kBAAkB;IAC5C,IAAI,CAACC,cAAa,GAAG,aAAa;IAClC,IAAI,CAACC,YAAW,GAAG,WAAW;IAC9B,IAAI,CAACC,UAAS,GAAG,SAAS;AAC1B,IAAA,IAAI,CAACC,cAAa,GAAG,IAAI,GAAG,EAAU;IACtC,IAAI,CAACC,mBAAkB,GAAG,IAAI;IAC9B,IAAI,CAACC,aAAY,GAAG,YAAY;IAChC,IAAI,CAACC,cAAa,GAAG,aAAa;AACpC,EAAA;EAEOC,eAAcA,CAAC,IAAU,EAAQ;AACtC,IAAA,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE;MACnB,IAAI,CAACH,mBAAkB,GAAG,IAAI;AAChC,IAAA,CAAC,MAAM;MACL,IAAI,CAACD,cAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AACjC,IAAA;AACF,EAAA;AAEOK,EAAAA,MAAKA,GAAS;AACnB,IAAA,IAAI,CAACL,cAAa,CAAC,KAAK,EAAE;IAC1B,IAAI,CAACC,mBAAkB,GAAG,IAAI;AAChC,EAAA;AAEOK,EAAAA,gBAAeA,GAAgB;AACpC,IAAA,IAAM,aAAa,GAAG,IAAI,CAACC,iBAAgB,EAAE;IAE7C,OAAO,aAAa,CAAC,MAAM,GAAI,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,GAAI,IAAI;AACjE,EAAA;AAEOA,EAAAA,iBAAgBA,GAAW;IAChC,IAAI,IAAI,CAACN,mBAAkB,EAAE;AAC3B,MAAA,OAAO,CAAC,IAAI,CAACA,mBAAkB,CAAC;AAClC,IAAA;IAEA,IAAM,aAAqB,GAAG,EAAE;AAEhC,IAAA,IAAI,CAACD,cAAa,CAAC,OAAO,CAAE,EAAE,IAAK;AACjC,MAAA,IAAM,IAAI,GAAG,IAAI,CAACN,YAAW,CAAC,EAAE,CAAC;AACjC,MAAA,IAAI,IAAI,EAAE;AACR,QAAA,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;AAC1B,MAAA;AACF,IAAA,CAAC,CAAC;AAEF,IAAA,OAAO,aAAa;AACtB,EAAA;EAEOc,sBAAqBA,CAAC,MAAY,EAAU;IACjD,IAAI,IAAI,CAACP,mBAAkB,EAAE;AAC3B,MAAA,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAACA,mBAAkB,CAAC,GAC7C,CAAC,IAAI,CAACA,mBAAkB,CAAC,GACzB,EAAE;AACR,IAAA;IAEA,IAAM,aAAqB,GAAG,EAAE;AAEhC,IAAA,IAAI,CAACD,cAAa,CAAC,OAAO,CAAE,EAAE,IAAK;AACjC,MAAA,IAAM,IAAI,GAAG,IAAI,CAACN,YAAW,CAAC,EAAE,CAAC;MACjC,IAAI,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;AACnC,QAAA,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;AAC1B,MAAA;AACF,IAAA,CAAC,CAAC;AAEF,IAAA,OAAO,aAAa;AACtB,EAAA;EAEOe,eAAcA,CAAC,IAAU,EAAW;AACzC,IAAA,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE;MACnB,OAAO,IAAI,CAACT,cAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AACxC,IAAA;IACA,OAAO,IAAI,CAACC,mBAAkB,GAAG,IAAI,CAACA,mBAAkB,KAAK,IAAI,GAAG,KAAK;AAC3E,EAAA;AAEOS,EAAAA,oBAAmBA,CAAC,IAAU,EAAE,eAAe,GAAG,KAAK,EAAQ;AACpE,IAAA,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE;AACnB,MAAA,IAAI,IAAI,CAACT,mBAAkB,KAAK,IAAI,EAAE;QACpC,IAAI,CAACA,mBAAkB,GAAG,IAAI;AAChC,MAAA;AACF,IAAA,CAAC,MAAM;MACL,IAAI,CAACD,cAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;AAElC,MAAA,IAAI,eAAe,EAAE;QACnB,IAAI,CAAC,OAAO,CAAC,MAAM;AACjB,UAAA,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE;YACnB,IAAI,CAACA,cAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;AACpC,UAAA;AACA,UAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;AACJ,MAAA;AACF,IAAA;AACF,EAAA;;AAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACSW,EAAAA,iBAAgBA,CAAC,IAAU,EAAE,YAAgC,EAAE;AACpE,IAAA,IAAM,cAAc,GAAG;AAAE,MAAA,YAAY,EAAE,IAAI;AAAE,MAAA,UAAU,EAAE;KAAM;AAC/D,IAAA,IAAM,aAAa,GAAG;AAAE,MAAA,GAAG,cAAc;MAAE,GAAG;KAAc;IAE5D,IAAM,SAAS,GAAG,MAAe;AAC/B,MAAA,IAAI,CAAC,IAAI,CAACd,cAAa,EAAE,EAAE;AACzB,QAAA,OAAO,KAAK;AACd,MAAA;AAEA,MAAA,IAAM,eAAe,GAAG,IAAI,CAACD,mBAAkB,EAAE;AACjD,MAAA,OAAO,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,IAAI;IACvD,CAAC;AAED,IAAA,IAAI,CAAC,SAAS,EAAE,EAAE;AAChB,MAAA;AACF,IAAA;IAEA,IAAM,mBAAmB,GAAI,cAAoB,IAAK;AACpD,MAAA,IAAI,CAACc,oBAAmB,CAAC,cAAc,CAAC;MACxC,IAAI,CAACf,sBAAqB,CAAC,cAAc,CAAC,CAACiB,SAAQ,EAAE;IACvD,CAAC;AAED,IAAA,IAAI,IAAI,CAACH,eAAc,CAAC,IAAI,CAAC,EAAE;MAC7B,IAAI,aAAa,CAAC,UAAU,EAAE;QAC5B,mBAAmB,CAAC,IAAI,CAAC;AACzB,QAAA,IAAI,CAACP,aAAY,CAAC,eAAe,EAAE;AAAE,UAAA;AAAK,SAAC,CAAC;AAC9C,MAAA;AACF,IAAA,CAAC,MAAM;AACL,MAAA,IAAM,cAAc,GAAG,IAAI,CAACI,gBAAe,EAAE;AAE7C,MAAA,IAAI,cAAc,EAAE;QAClB,mBAAmB,CAAC,cAAc,CAAC;AACrC,MAAA;AAEA,MAAA,IAAI,CAACF,eAAc,CAAC,IAAI,CAAC;AACzB,MAAA,IAAI,CAACN,YAAW,CAAC,IAAI,CAAC;MACtB,IAAI,CAACH,sBAAqB,CAAC,IAAI,CAAC,CAACkB,OAAM,CAAC,aAAa,CAAC,YAAY,CAAC;AAEnE,MAAA,IAAI,CAACX,aAAY,CAAC,aAAa,EAAE;QAC/B,cAAc;AACd,QAAA;AACF,OAAC,CAAC;AACJ,IAAA;IAEA,IAAI,CAACC,cAAa,EAAE;IACpB,IAAI,CAACJ,UAAS,EAAE;AAClB,EAAA;AACF;;"}
|
|
1
|
+
{"version":3,"file":"selectNodeHandler.js","sources":["../src/selectNodeHandler.ts"],"sourcesContent":["import type {\n GetNodeById,\n GetNodeElementForNode,\n OpenParents,\n TriggerEvent,\n} from \"./methodTypes\";\nimport type { Node, NodeId } from \"./node\";\nimport type { OnCanSelectNode } from \"./options\";\n\nexport interface SelectNodeOptions {\n mustSetFocus?: boolean;\n mustToggle?: boolean;\n}\n\ninterface SelectNodeHandlerParameters {\n getNodeById: GetNodeById;\n getNodeElementForNode: GetNodeElementForNode;\n getOnCanSelectNode: () => OnCanSelectNode | undefined;\n getSelectable: () => boolean;\n openParents: OpenParents;\n saveState: () => void;\n triggerEvent: TriggerEvent;\n updateTabStop: () => void;\n}\n\nexport default class SelectNodeHandler {\n private getNodeById: GetNodeById;\n private getNodeElementForNode: GetNodeElementForNode;\n private getOnCanSelectNode: () => OnCanSelectNode | undefined;\n private getSelectable: () => boolean;\n private openParents: OpenParents;\n private saveState: () => void;\n private selectedNodes: Set<NodeId>;\n private selectedSingleNode: Node | null;\n private triggerEvent: TriggerEvent;\n private updateTabStop: () => void;\n\n constructor({\n getNodeById,\n getNodeElementForNode,\n getOnCanSelectNode,\n getSelectable,\n openParents,\n saveState,\n triggerEvent,\n updateTabStop,\n }: SelectNodeHandlerParameters) {\n this.getNodeById = getNodeById;\n this.getNodeElementForNode = getNodeElementForNode;\n this.getOnCanSelectNode = getOnCanSelectNode;\n this.getSelectable = getSelectable;\n this.openParents = openParents;\n this.saveState = saveState;\n this.selectedNodes = new Set<NodeId>();\n this.selectedSingleNode = null;\n this.triggerEvent = triggerEvent;\n this.updateTabStop = updateTabStop;\n }\n\n public addToSelection(node: Node): void {\n if (node.id == null) {\n this.selectedSingleNode = node;\n } else {\n this.selectedNodes.add(node.id);\n }\n }\n\n public clear(): void {\n this.selectedNodes.clear();\n this.selectedSingleNode = null;\n }\n\n public getSelectedNode(): Node | null {\n const selectedNodes = this.getSelectedNodes();\n\n return selectedNodes.length ? (selectedNodes[0] ?? null) : null;\n }\n\n public getSelectedNodes(): Node[] {\n if (this.selectedSingleNode) {\n return [this.selectedSingleNode];\n }\n\n const selectedNodes: Node[] = [];\n\n this.selectedNodes.forEach((id) => {\n const node = this.getNodeById(id);\n if (node) {\n selectedNodes.push(node);\n }\n });\n\n return selectedNodes;\n }\n\n public getSelectedNodesUnder(parent: Node): Node[] {\n if (this.selectedSingleNode) {\n return parent.isParentOf(this.selectedSingleNode)\n ? [this.selectedSingleNode]\n : [];\n }\n\n const selectedNodes: Node[] = [];\n\n this.selectedNodes.forEach((id) => {\n const node = this.getNodeById(id);\n if (node && parent.isParentOf(node)) {\n selectedNodes.push(node);\n }\n });\n\n return selectedNodes;\n }\n\n public isNodeSelected(node: Node): boolean {\n if (node.id != null) {\n return this.selectedNodes.has(node.id);\n }\n return this.selectedSingleNode ? this.selectedSingleNode === node : false;\n }\n\n public removeFromSelection(node: Node, includeChildren = false): void {\n if (node.id == null) {\n if (this.selectedSingleNode === node) {\n this.selectedSingleNode = null;\n }\n } else {\n this.selectedNodes.delete(node.id);\n\n if (includeChildren) {\n node.iterate((child: Node) => {\n if (child.id != null) {\n this.selectedNodes.delete(child.id);\n }\n return true;\n });\n }\n }\n }\n\n /* Select a single node.\n * Renders the changed elements.\n * Deselects if the node is currently selected (if the mustToggle is on).\n * Deselects the previously selected node.\n * Check if the node is selectable.\n * Saves the state.\n * Options:\n * mustSetFocus: set the focus to the selected node\n * mustToggle: support deselecting the selected node\n */\n public selectSingleNode(node: Node, optionsParam?: SelectNodeOptions) {\n if (!this.canSelect(node)) {\n return;\n }\n\n const defaultOptions = { mustSetFocus: true, mustToggle: true };\n const selectOptions = { ...defaultOptions, ...optionsParam };\n\n const otherSelectedNodes = this.getSelectedNodes().filter(\n (selectedNode) => selectedNode !== node,\n );\n\n if (this.isNodeSelected(node)) {\n if (otherSelectedNodes.length) {\n // Part of a multiple selection: keep only this node selected\n for (const otherNode of otherSelectedNodes) {\n this.deselectNode(otherNode);\n }\n } else if (selectOptions.mustToggle) {\n this.deselectNode(node);\n this.triggerEvent(\"tree.deselect\", { node });\n }\n } else {\n const deselectedNode = otherSelectedNodes[0] ?? null;\n\n for (const otherNode of otherSelectedNodes) {\n this.deselectNode(otherNode);\n }\n\n this.addToSelection(node);\n this.openParents(node);\n this.getNodeElementForNode(node).select(selectOptions.mustSetFocus);\n\n this.triggerEvent(\"tree.select\", {\n deselectedNode,\n node,\n });\n }\n\n this.updateTabStop();\n this.saveState();\n }\n\n /* Add a node to the selection, or remove it when it is selected, without\n * changing the other selected nodes.\n * Checks if the node is selectable and saves the state.\n */\n public toggleNodeInSelection(node: Node, mustSetFocus = true): void {\n if (!this.canSelect(node)) {\n return;\n }\n\n if (this.isNodeSelected(node)) {\n this.deselectNode(node);\n this.triggerEvent(\"tree.deselect\", { node });\n } else {\n this.addToSelection(node);\n this.openParents(node);\n this.getNodeElementForNode(node).select(mustSetFocus);\n\n this.triggerEvent(\"tree.select\", { deselectedNode: null, node });\n }\n\n this.updateTabStop();\n this.saveState();\n }\n\n private canSelect(node: Node): boolean {\n if (!this.getSelectable()) {\n return false;\n }\n\n const onCanSelectNode = this.getOnCanSelectNode();\n return onCanSelectNode ? onCanSelectNode(node) : true;\n }\n\n private deselectNode(node: Node): void {\n this.removeFromSelection(node);\n this.getNodeElementForNode(node).deselect();\n }\n}\n"],"names":["getNodeById","getNodeElementForNode","getOnCanSelectNode","getSelectable","openParents","saveState","selectedNodes","selectedSingleNode","triggerEvent","updateTabStop","addToSelection","clear","getSelectedNode","getSelectedNodes","getSelectedNodesUnder","isNodeSelected","removeFromSelection","selectSingleNode","canSelect","deselectNode","select","toggleNodeInSelection","deselect"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setDefaultOptions.d.ts","sourceRoot":"","sources":["../src/setDefaultOptions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAapD,eAAO,MAAM,YAAY,QAAoC,CAAC;AAC9D,eAAO,MAAM,YAAY,QAAoC,CAAC;AAC9D,eAAO,MAAM,aAAa,QAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"setDefaultOptions.d.ts","sourceRoot":"","sources":["../src/setDefaultOptions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAapD,eAAO,MAAM,YAAY,QAAoC,CAAC;AAC9D,eAAO,MAAM,YAAY,QAAoC,CAAC;AAC9D,eAAO,MAAM,aAAa,QAAoC,CAAC;AAuC/D,QAAA,MAAM,iBAAiB,GACrB,aAAa,WAAW,EACxB,cAAc,OAAO,CAAC,kBAAkB,CAAC,KACxC,kBAaF,CAAC;AAuBF,eAAe,iBAAiB,CAAC"}
|
package/lib/setDefaultOptions.js
CHANGED
|
@@ -25,6 +25,7 @@ let defaults = {
|
|
|
25
25
|
// dataUrl: undefined,
|
|
26
26
|
dragAndDrop: false,
|
|
27
27
|
keyboardSupport: true,
|
|
28
|
+
multiSelect: false,
|
|
28
29
|
nodeClass: Node,
|
|
29
30
|
// onCanMove: undefined, // Can this node be moved?
|
|
30
31
|
// onCanMoveTo: undefined, // Can this node be moved to this position? function(moved_node, target_node, position)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setDefaultOptions.js","sources":["../src/setDefaultOptions.ts"],"sourcesContent":["import type { AnimationSpeed } from \"./animation\";\nimport type { TreeElementOptions } from \"./options\";\n\nimport { DEFAULT_CLASS_PREFIX } from \"./classNames\";\nimport { Node } from \"./node\";\n\n/* The default icons are svg instead of text: the triangle characters come\n * from different fonts on each platform, so they differ in shape and size.\n * They are sized in em to follow the font size, and use currentColor to follow\n * --tree-element-toggler-color.\n */\nconst triangle = (points: string): string =>\n `<svg width=\"0.8em\" height=\"0.8em\" viewBox=\"0 0 10 10\" focusable=\"false\"><polygon points=\"${points}\" fill=\"currentColor\"/></svg>`;\n\nexport const triangleDown = triangle(\"0.5,0.5 9.5,0.5 5,9.5\");\nexport const triangleLeft = triangle(\"9.5,0.5 9.5,9.5 0.5,5\");\nexport const triangleRight = triangle(\"0.5,0.5 0.5,9.5 9.5,5\");\n\nconst defaults: TreeElementOptions = {\n animationSpeed: \"fast\",\n autoEscape: true,\n autoOpen: false, // true / false / int (open n levels starting at 0)\n buttonLeft: true,\n classPrefix: DEFAULT_CLASS_PREFIX, // the prefix of all css classes\n // closedIcon: undefined, // the default is a triangle; see getDefaultClosedIcon\n // commonClassName: undefined, // the class of every element; the default is \"<classPrefix>-common\"\n // data: undefined,\n // dataFilter: undefined,\n // dataUrl: undefined,\n dragAndDrop: false,\n keyboardSupport: true,\n nodeClass: Node,\n // onCanMove: undefined, // Can this node be moved?\n // onCanMoveTo: undefined, // Can this node be moved to this position? function(moved_node, target_node, position)\n // onCanSelectNode: undefined,\n // onCreateLi: undefined,\n // onDragMove: undefined,\n // onDragStop: undefined,\n // onGetStateFromStorage: undefined,\n // onIsMoveHandle: undefined,\n // onSetStateFromStorage: undefined,\n // openedIcon: undefined, // the default is a triangle pointing down\n openFolderDelay: 500, // The delay for opening a folder during drag and drop; the value is in milliseconds\n // rtl: undefined, // right-to-left support; true / false (default)\n saveState: false, // true / false / string (local storage key; the default key is \"tree\")\n selectable: true,\n showEmptyFolder: false,\n slide: true, // must display slide animation?\n startDndDelay: 300, // The delay for starting dnd (in milliseconds)\n tabIndex: 0,\n // treeClassName: undefined, // the class of the root element; the default is classPrefix\n useContextMenu: true,\n};\n\nconst setDefaultOptions = (\n htmlElement: HTMLElement,\n inputOptions: Partial<TreeElementOptions>,\n): TreeElementOptions => {\n const options = {\n ...defaults,\n ...inputOptions,\n animationSpeed: inputOptions.animationSpeed ?? getDefaultAnimationSpeed(),\n };\n\n options.dataUrl ??= htmlElement.dataset.url;\n options.rtl ??= getRtlOptionFromHTMLElement(htmlElement);\n options.closedIcon ??= getDefaultClosedIcon(options);\n options.openedIcon ??= triangleDown;\n\n return options;\n};\n\n// No animation when the user asked the system for reduced motion\nconst getDefaultAnimationSpeed = (): AnimationSpeed =>\n prefersReducedMotion() ? 0 : defaults.animationSpeed;\n\nconst prefersReducedMotion = (): boolean =>\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\nconst getDefaultClosedIcon = (options: TreeElementOptions): string => {\n return options.rtl ? triangleLeft : triangleRight;\n};\n\nconst getRtlOptionFromHTMLElement = (htmlElement: HTMLElement): boolean => {\n const dataRtl = htmlElement.dataset.rtl;\n\n if (dataRtl == \"\") {\n return true;\n }\n return dataRtl === \"false\" ? false : Boolean(dataRtl);\n};\n\nexport default setDefaultOptions;\n"],"names":[],"mappings":";;;AAMA;AACA;AACA;AACA;AACA;AACA,IAAM,QAAQ,GAAI,MAAc,IAC9B,CAAA,yFAAA,EAA4F,MAAM,CAAA,6BAAA,CAA+B;IAEtH,YAAY,GAAG,QAAQ,CAAC,uBAAuB;IAC/C,YAAY,GAAG,QAAQ,CAAC,uBAAuB;IAC/C,aAAa,GAAG,QAAQ,CAAC,uBAAuB;AAE7D,IAAM,QAA4B,GAAG;AACnC,EAAA,cAAc,EAAE,MAAM;AACtB,EAAA,UAAU,EAAE,IAAI;AAChB,EAAA,QAAQ,EAAE,KAAK;AAAE;AACjB,EAAA,UAAU,EAAE,IAAI;AAChB,EAAA,WAAW,EAAE,oBAAoB;AAAE;AACnC;AACA;AACA;AACA;AACA;AACA,EAAA,WAAW,EAAE,KAAK;AAClB,EAAA,eAAe,EAAE,IAAI;AACrB,EAAA,SAAS,EAAE,IAAI;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA,eAAe,EAAE,GAAG;AAAE;AACtB;AACA,EAAA,SAAS,EAAE,KAAK;AAAE;AAClB,EAAA,UAAU,EAAE,IAAI;AAChB,EAAA,eAAe,EAAE,KAAK;AACtB,EAAA,KAAK,EAAE,IAAI;AAAE;AACb,EAAA,aAAa,EAAE,GAAG;AAAE;AACpB,EAAA,QAAQ,EAAE,CAAC;AACX;AACA,EAAA,cAAc,EAAE;AAClB,CAAC;AAED,IAAM,iBAAiB,GAAG,CACxB,WAAwB,EACxB,YAAyC,KAClB;AACvB,EAAA,IAAM,OAAO,GAAG;AACd,IAAA,GAAG,QAAQ;AACX,IAAA,GAAG,YAAY;AACf,IAAA,cAAc,EAAE,YAAY,CAAC,cAAc,IAAI,wBAAwB;GACxE;AAED,EAAA,OAAO,CAAC,OAAO,KAAK,WAAW,CAAC,OAAO,CAAC,GAAG;AAC3C,EAAA,OAAO,CAAC,GAAG,KAAK,2BAA2B,CAAC,WAAW,CAAC;AACxD,EAAA,OAAO,CAAC,UAAU,KAAK,oBAAoB,CAAC,OAAO,CAAC;EACpD,OAAO,CAAC,UAAU,KAAK,YAAY;AAEnC,EAAA,OAAO,OAAO;AAChB;;AAEA;AACA,IAAM,wBAAwB,GAAG,MAC/B,oBAAoB,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,cAAc;AAEtD,IAAM,oBAAoB,GAAG,MAC3B,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,IACvC,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO;AAE/D,IAAM,oBAAoB,GAAI,OAA2B,IAAa;AACpE,EAAA,OAAO,OAAO,CAAC,GAAG,GAAG,YAAY,GAAG,aAAa;AACnD,CAAC;AAED,IAAM,2BAA2B,GAAI,WAAwB,IAAc;AACzE,EAAA,IAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG;EAEvC,IAAI,OAAO,IAAI,EAAE,EAAE;AACjB,IAAA,OAAO,IAAI;AACb,EAAA;EACA,OAAO,OAAO,KAAK,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;AACvD,CAAC;;"}
|
|
1
|
+
{"version":3,"file":"setDefaultOptions.js","sources":["../src/setDefaultOptions.ts"],"sourcesContent":["import type { AnimationSpeed } from \"./animation\";\nimport type { TreeElementOptions } from \"./options\";\n\nimport { DEFAULT_CLASS_PREFIX } from \"./classNames\";\nimport { Node } from \"./node\";\n\n/* The default icons are svg instead of text: the triangle characters come\n * from different fonts on each platform, so they differ in shape and size.\n * They are sized in em to follow the font size, and use currentColor to follow\n * --tree-element-toggler-color.\n */\nconst triangle = (points: string): string =>\n `<svg width=\"0.8em\" height=\"0.8em\" viewBox=\"0 0 10 10\" focusable=\"false\"><polygon points=\"${points}\" fill=\"currentColor\"/></svg>`;\n\nexport const triangleDown = triangle(\"0.5,0.5 9.5,0.5 5,9.5\");\nexport const triangleLeft = triangle(\"9.5,0.5 9.5,9.5 0.5,5\");\nexport const triangleRight = triangle(\"0.5,0.5 0.5,9.5 9.5,5\");\n\nconst defaults: TreeElementOptions = {\n animationSpeed: \"fast\",\n autoEscape: true,\n autoOpen: false, // true / false / int (open n levels starting at 0)\n buttonLeft: true,\n classPrefix: DEFAULT_CLASS_PREFIX, // the prefix of all css classes\n // closedIcon: undefined, // the default is a triangle; see getDefaultClosedIcon\n // commonClassName: undefined, // the class of every element; the default is \"<classPrefix>-common\"\n // data: undefined,\n // dataFilter: undefined,\n // dataUrl: undefined,\n dragAndDrop: false,\n keyboardSupport: true,\n multiSelect: false,\n nodeClass: Node,\n // onCanMove: undefined, // Can this node be moved?\n // onCanMoveTo: undefined, // Can this node be moved to this position? function(moved_node, target_node, position)\n // onCanSelectNode: undefined,\n // onCreateLi: undefined,\n // onDragMove: undefined,\n // onDragStop: undefined,\n // onGetStateFromStorage: undefined,\n // onIsMoveHandle: undefined,\n // onSetStateFromStorage: undefined,\n // openedIcon: undefined, // the default is a triangle pointing down\n openFolderDelay: 500, // The delay for opening a folder during drag and drop; the value is in milliseconds\n // rtl: undefined, // right-to-left support; true / false (default)\n saveState: false, // true / false / string (local storage key; the default key is \"tree\")\n selectable: true,\n showEmptyFolder: false,\n slide: true, // must display slide animation?\n startDndDelay: 300, // The delay for starting dnd (in milliseconds)\n tabIndex: 0,\n // treeClassName: undefined, // the class of the root element; the default is classPrefix\n useContextMenu: true,\n};\n\nconst setDefaultOptions = (\n htmlElement: HTMLElement,\n inputOptions: Partial<TreeElementOptions>,\n): TreeElementOptions => {\n const options = {\n ...defaults,\n ...inputOptions,\n animationSpeed: inputOptions.animationSpeed ?? getDefaultAnimationSpeed(),\n };\n\n options.dataUrl ??= htmlElement.dataset.url;\n options.rtl ??= getRtlOptionFromHTMLElement(htmlElement);\n options.closedIcon ??= getDefaultClosedIcon(options);\n options.openedIcon ??= triangleDown;\n\n return options;\n};\n\n// No animation when the user asked the system for reduced motion\nconst getDefaultAnimationSpeed = (): AnimationSpeed =>\n prefersReducedMotion() ? 0 : defaults.animationSpeed;\n\nconst prefersReducedMotion = (): boolean =>\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\nconst getDefaultClosedIcon = (options: TreeElementOptions): string => {\n return options.rtl ? triangleLeft : triangleRight;\n};\n\nconst getRtlOptionFromHTMLElement = (htmlElement: HTMLElement): boolean => {\n const dataRtl = htmlElement.dataset.rtl;\n\n if (dataRtl == \"\") {\n return true;\n }\n return dataRtl === \"false\" ? false : Boolean(dataRtl);\n};\n\nexport default setDefaultOptions;\n"],"names":[],"mappings":";;;AAMA;AACA;AACA;AACA;AACA;AACA,IAAM,QAAQ,GAAI,MAAc,IAC9B,CAAA,yFAAA,EAA4F,MAAM,CAAA,6BAAA,CAA+B;IAEtH,YAAY,GAAG,QAAQ,CAAC,uBAAuB;IAC/C,YAAY,GAAG,QAAQ,CAAC,uBAAuB;IAC/C,aAAa,GAAG,QAAQ,CAAC,uBAAuB;AAE7D,IAAM,QAA4B,GAAG;AACnC,EAAA,cAAc,EAAE,MAAM;AACtB,EAAA,UAAU,EAAE,IAAI;AAChB,EAAA,QAAQ,EAAE,KAAK;AAAE;AACjB,EAAA,UAAU,EAAE,IAAI;AAChB,EAAA,WAAW,EAAE,oBAAoB;AAAE;AACnC;AACA;AACA;AACA;AACA;AACA,EAAA,WAAW,EAAE,KAAK;AAClB,EAAA,eAAe,EAAE,IAAI;AACrB,EAAA,WAAW,EAAE,KAAK;AAClB,EAAA,SAAS,EAAE,IAAI;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA,eAAe,EAAE,GAAG;AAAE;AACtB;AACA,EAAA,SAAS,EAAE,KAAK;AAAE;AAClB,EAAA,UAAU,EAAE,IAAI;AAChB,EAAA,eAAe,EAAE,KAAK;AACtB,EAAA,KAAK,EAAE,IAAI;AAAE;AACb,EAAA,aAAa,EAAE,GAAG;AAAE;AACpB,EAAA,QAAQ,EAAE,CAAC;AACX;AACA,EAAA,cAAc,EAAE;AAClB,CAAC;AAED,IAAM,iBAAiB,GAAG,CACxB,WAAwB,EACxB,YAAyC,KAClB;AACvB,EAAA,IAAM,OAAO,GAAG;AACd,IAAA,GAAG,QAAQ;AACX,IAAA,GAAG,YAAY;AACf,IAAA,cAAc,EAAE,YAAY,CAAC,cAAc,IAAI,wBAAwB;GACxE;AAED,EAAA,OAAO,CAAC,OAAO,KAAK,WAAW,CAAC,OAAO,CAAC,GAAG;AAC3C,EAAA,OAAO,CAAC,GAAG,KAAK,2BAA2B,CAAC,WAAW,CAAC;AACxD,EAAA,OAAO,CAAC,UAAU,KAAK,oBAAoB,CAAC,OAAO,CAAC;EACpD,OAAO,CAAC,UAAU,KAAK,YAAY;AAEnC,EAAA,OAAO,OAAO;AAChB;;AAEA;AACA,IAAM,wBAAwB,GAAG,MAC/B,oBAAoB,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,cAAc;AAEtD,IAAM,oBAAoB,GAAG,MAC3B,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,IACvC,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO;AAE/D,IAAM,oBAAoB,GAAI,OAA2B,IAAa;AACpE,EAAA,OAAO,OAAO,CAAC,GAAG,GAAG,YAAY,GAAG,aAAa;AACnD,CAAC;AAED,IAAM,2BAA2B,GAAI,WAAwB,IAAc;AACzE,EAAA,IAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG;EAEvC,IAAI,OAAO,IAAI,EAAE,EAAE;AACjB,IAAA,OAAO,IAAI;AACb,EAAA;EACA,OAAO,OAAO,KAAK,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;AACvD,CAAC;;"}
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
package/lib/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["// Generated by config/generateVersion.mjs. Do not edit.\nconst version = \"0.
|
|
1
|
+
{"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["// Generated by config/generateVersion.mjs. Do not edit.\nconst version = \"0.4.0\";\n\nexport default version;\n"],"names":[],"mappings":"AAAA;AACA,IAAM,OAAO,GAAG;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tree-element",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Tree widget in plain javascript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tree"
|
|
@@ -61,29 +61,29 @@
|
|
|
61
61
|
"docs-check": "kiira check"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@axe-core/playwright": "^4.
|
|
64
|
+
"@axe-core/playwright": "^4.14.0",
|
|
65
65
|
"@babel/cli": "^8.0.6",
|
|
66
66
|
"@babel/core": "^8.0.7",
|
|
67
67
|
"@babel/preset-env": "^8.0.7",
|
|
68
68
|
"@babel/preset-typescript": "^8.0.7",
|
|
69
|
-
"@cspell/eslint-plugin": "^10.
|
|
70
|
-
"@eslint/css": "^2.0.
|
|
69
|
+
"@cspell/eslint-plugin": "^10.4.0",
|
|
70
|
+
"@eslint/css": "^2.0.1",
|
|
71
71
|
"@eslint/js": "^10.0.1",
|
|
72
|
-
"@playwright/test": "^1.64.
|
|
72
|
+
"@playwright/test": "^1.64.1",
|
|
73
73
|
"@rollup/plugin-babel": "^7.1.0",
|
|
74
74
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
75
75
|
"@rollup/plugin-terser": "^1.0.0",
|
|
76
76
|
"@testing-library/dom": "^10.4.2",
|
|
77
77
|
"@testing-library/jest-dom": "^7.0.1",
|
|
78
|
-
"@testing-library/user-event": "^14.6.
|
|
78
|
+
"@testing-library/user-event": "^14.6.8",
|
|
79
79
|
"@types/debug": "^4.1.13",
|
|
80
80
|
"@types/node": "^26.6.5",
|
|
81
|
-
"@vitest/coverage-istanbul": "^5.0.
|
|
81
|
+
"@vitest/coverage-istanbul": "^5.0.5",
|
|
82
82
|
"@vitest/eslint-plugin": "^1.6.27",
|
|
83
83
|
"axe-core": "^4.14.0",
|
|
84
84
|
"babel-plugin-istanbul": "^8.0.0",
|
|
85
85
|
"babel-plugin-prefix-private-members": "^0.3.1",
|
|
86
|
-
"eslint": "^10.
|
|
86
|
+
"eslint": "^10.13.0",
|
|
87
87
|
"eslint-import-resolver-typescript": "^4.4.5",
|
|
88
88
|
"eslint-plugin-compat": "^7.0.2",
|
|
89
89
|
"eslint-plugin-import-x": "^4.17.1",
|
|
@@ -94,30 +94,30 @@
|
|
|
94
94
|
"eslint-plugin-playwright": "^2.12.1",
|
|
95
95
|
"eslint-plugin-testing-library": "^7.16.2",
|
|
96
96
|
"eslint-plugin-tsdoc": "^0.5.4",
|
|
97
|
-
"eslint-plugin-unicorn": "^
|
|
97
|
+
"eslint-plugin-unicorn": "^78.0.0",
|
|
98
98
|
"jest-extended": "^7.0.0",
|
|
99
|
-
"jsdom": "^30.1.
|
|
99
|
+
"jsdom": "^30.1.3",
|
|
100
100
|
"jsdom-testing-mocks": "^1.16.0",
|
|
101
101
|
"jsonfile": "^6.2.1",
|
|
102
|
-
"kiira": "^0.
|
|
103
|
-
"kiira-core": "^0.
|
|
102
|
+
"kiira": "^0.7.0",
|
|
103
|
+
"kiira-core": "^0.7.0",
|
|
104
104
|
"lodash": "^4.18.1",
|
|
105
105
|
"msw": "^3.0.3",
|
|
106
|
-
"postcss": "^8.5.
|
|
106
|
+
"postcss": "^8.5.30",
|
|
107
107
|
"postcss-cli": "^12.0.0",
|
|
108
|
-
"postcss-import": "^17.0.
|
|
108
|
+
"postcss-import": "^17.0.1",
|
|
109
109
|
"postcss-load-config": "^6.0.1",
|
|
110
|
-
"postcss-nested": "^
|
|
110
|
+
"postcss-nested": "^9.0.0",
|
|
111
111
|
"prettier": "^3.9.10",
|
|
112
|
-
"rollup": "^4.64.
|
|
112
|
+
"rollup": "^4.64.7",
|
|
113
113
|
"rollup-plugin-serve": "^3.0.0",
|
|
114
114
|
"rollup-plugin-tsconfig-paths": "^1.5.2",
|
|
115
115
|
"tslib": "^2.8.1",
|
|
116
116
|
"typedoc": "^0.28.20",
|
|
117
|
-
"typedoc-plugin-markdown": "^4.
|
|
117
|
+
"typedoc-plugin-markdown": "^4.14.0",
|
|
118
118
|
"typescript": "^6.0.3",
|
|
119
|
-
"typescript-eslint": "^8.71.
|
|
119
|
+
"typescript-eslint": "^8.71.2",
|
|
120
120
|
"vitepress": "^1.6.4",
|
|
121
|
-
"vitest": "^5.0.
|
|
121
|
+
"vitest": "^5.0.5"
|
|
122
122
|
}
|
|
123
123
|
}
|
package/src/classNames.ts
CHANGED
|
@@ -39,13 +39,13 @@ export const DEFAULT_CLASS_PREFIX = "tree-element";
|
|
|
39
39
|
*/
|
|
40
40
|
const createClassNames = ({
|
|
41
41
|
classPrefix,
|
|
42
|
-
commonClassName
|
|
43
|
-
treeClassName,
|
|
42
|
+
commonClassName = `${classPrefix}-common`,
|
|
43
|
+
treeClassName = classPrefix,
|
|
44
44
|
}: ClassNamesOptions): ClassNames => ({
|
|
45
45
|
border: `${classPrefix}-border`,
|
|
46
46
|
circle: `${classPrefix}-circle`,
|
|
47
47
|
closed: `${classPrefix}-closed`,
|
|
48
|
-
common: commonClassName
|
|
48
|
+
common: commonClassName,
|
|
49
49
|
dnd: `${classPrefix}-dnd`,
|
|
50
50
|
dragging: `${classPrefix}-dragging`,
|
|
51
51
|
element: `${classPrefix}-element`,
|
|
@@ -64,7 +64,7 @@ const createClassNames = ({
|
|
|
64
64
|
toggler: `${classPrefix}-toggler`,
|
|
65
65
|
togglerLeft: `${classPrefix}-toggler-left`,
|
|
66
66
|
togglerRight: `${classPrefix}-toggler-right`,
|
|
67
|
-
tree: treeClassName
|
|
67
|
+
tree: treeClassName,
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
export default createClassNames;
|
package/src/dataLoader.ts
CHANGED
|
@@ -44,8 +44,9 @@ export default class DataLoader {
|
|
|
44
44
|
element.classList.add(this.classNames.loading);
|
|
45
45
|
|
|
46
46
|
// Tell screen readers that the children of the folder are loading
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if (node) {
|
|
48
|
+
element.setAttribute("aria-busy", "true");
|
|
49
|
+
}
|
|
49
50
|
|
|
50
51
|
this.triggerEvent("tree.loading_data", {
|
|
51
52
|
element,
|
|
@@ -54,7 +55,9 @@ export default class DataLoader {
|
|
|
54
55
|
|
|
55
56
|
const stopLoading = (): void => {
|
|
56
57
|
element.classList.remove(this.classNames.loading);
|
|
57
|
-
|
|
58
|
+
if (node) {
|
|
59
|
+
element.removeAttribute("aria-busy");
|
|
60
|
+
}
|
|
58
61
|
this.triggerEvent("tree.loaded_data", {
|
|
59
62
|
element,
|
|
60
63
|
node,
|
|
@@ -92,10 +95,4 @@ export default class DataLoader {
|
|
|
92
95
|
this.triggerEvent("tree.load_failed", { error });
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
|
-
|
|
96
|
-
private getTitleSpan(element: HTMLElement): HTMLElement | null {
|
|
97
|
-
return element.querySelector<HTMLElement>(
|
|
98
|
-
`:scope > .${this.classNames.element} > .${this.classNames.title}`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
98
|
}
|
package/src/elementsRenderer.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface ElementsRendererParams {
|
|
|
16
16
|
getTree: GetTree;
|
|
17
17
|
htmlElement: HTMLElement;
|
|
18
18
|
isNodeSelected: IsNodeSelected;
|
|
19
|
+
multiSelect: boolean;
|
|
19
20
|
onCreateLi?: OnCreateLi;
|
|
20
21
|
openedIcon?: IconElement;
|
|
21
22
|
rtl?: 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,6 +88,7 @@ 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;
|
|
@@ -112,14 +116,14 @@ export default class ElementsRenderer {
|
|
|
112
116
|
* null when the folder is not rendered or has no children.
|
|
113
117
|
*/
|
|
114
118
|
public renderChildren(node: Node): HTMLUListElement | null {
|
|
115
|
-
return
|
|
116
|
-
?
|
|
117
|
-
: this.createDomElements(
|
|
119
|
+
return node.element && node.hasChildren()
|
|
120
|
+
? this.createDomElements(
|
|
118
121
|
node.element,
|
|
119
122
|
node.children,
|
|
120
123
|
false,
|
|
121
124
|
node.getLevel() + 1,
|
|
122
|
-
)
|
|
125
|
+
)
|
|
126
|
+
: null;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
public renderFromNode(node: Node): void {
|
|
@@ -208,11 +212,11 @@ export default class ElementsRenderer {
|
|
|
208
212
|
this.buttonLeft ? 1 : 0
|
|
209
213
|
] as HTMLSpanElement;
|
|
210
214
|
|
|
211
|
-
this.
|
|
215
|
+
this.fillNode(li, titleSpan, node.name, isSelected, level);
|
|
212
216
|
|
|
213
217
|
if (node.is_loading) {
|
|
214
218
|
li.classList.add(this.classNames.loading);
|
|
215
|
-
|
|
219
|
+
li.setAttribute("aria-busy", "true");
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
return li;
|
|
@@ -251,10 +255,10 @@ export default class ElementsRenderer {
|
|
|
251
255
|
div.append(toggler);
|
|
252
256
|
}
|
|
253
257
|
|
|
258
|
+
li.setAttribute("aria-expanded", getBoolString(isOpen));
|
|
259
|
+
|
|
254
260
|
// title span
|
|
255
|
-
|
|
256
|
-
titleSpan.setAttribute("aria-expanded", getBoolString(isOpen));
|
|
257
|
-
div.append(titleSpan);
|
|
261
|
+
div.append(this.createTitleSpanTemplate(true));
|
|
258
262
|
|
|
259
263
|
if (!this.buttonLeft) {
|
|
260
264
|
div.append(toggler);
|
|
@@ -288,11 +292,22 @@ export default class ElementsRenderer {
|
|
|
288
292
|
|
|
289
293
|
/*
|
|
290
294
|
Create the outer part of a <li> template: li > div
|
|
295
|
+
The li is the treeitem, so the ul of its children is inside the treeitem.
|
|
291
296
|
*/
|
|
292
297
|
private createLiTemplate(liClasses: string): HTMLLIElement {
|
|
293
298
|
const li = document.createElement("li");
|
|
294
299
|
li.className = liClasses;
|
|
295
|
-
li.setAttribute("role", "
|
|
300
|
+
li.setAttribute("role", "treeitem");
|
|
301
|
+
|
|
302
|
+
// Only a multiselect tree marks the nodes that are not selected
|
|
303
|
+
if (this.multiSelect) {
|
|
304
|
+
li.setAttribute("aria-selected", "false");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Every node is focusable, but only the tab stop is in the tab order
|
|
308
|
+
if (this.tabIndex !== undefined) {
|
|
309
|
+
li.setAttribute("tabindex", "-1");
|
|
310
|
+
}
|
|
296
311
|
|
|
297
312
|
const div = document.createElement("div");
|
|
298
313
|
div.className = `${this.classNames.element} ${this.classNames.common}`;
|
|
@@ -318,7 +333,7 @@ export default class ElementsRenderer {
|
|
|
318
333
|
const div = li.firstChild as HTMLDivElement;
|
|
319
334
|
const titleSpan = div.firstChild as HTMLSpanElement;
|
|
320
335
|
|
|
321
|
-
this.
|
|
336
|
+
this.fillNode(li, titleSpan, node.name, isSelected, level);
|
|
322
337
|
|
|
323
338
|
return li;
|
|
324
339
|
}
|
|
@@ -347,13 +362,6 @@ export default class ElementsRenderer {
|
|
|
347
362
|
classes += ` ${this.buttonLeft ? this.classNames.titleButtonLeft : this.classNames.titleButtonRight}`;
|
|
348
363
|
|
|
349
364
|
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
365
|
|
|
358
366
|
return titleSpan;
|
|
359
367
|
}
|
|
@@ -391,29 +399,35 @@ export default class ElementsRenderer {
|
|
|
391
399
|
if (this.ariaLabelledBy !== undefined) {
|
|
392
400
|
ul.setAttribute("aria-labelledby", this.ariaLabelledBy);
|
|
393
401
|
}
|
|
402
|
+
|
|
403
|
+
if (this.multiSelect) {
|
|
404
|
+
ul.setAttribute("aria-multiselectable", "true");
|
|
405
|
+
}
|
|
394
406
|
}
|
|
395
407
|
|
|
396
408
|
return ul;
|
|
397
409
|
}
|
|
398
410
|
|
|
399
411
|
/*
|
|
400
|
-
Set the parts of a cloned title span that differ per node
|
|
412
|
+
Set the parts of a cloned li and title span that differ per node
|
|
401
413
|
*/
|
|
402
|
-
private
|
|
414
|
+
private fillNode(
|
|
415
|
+
li: HTMLLIElement,
|
|
403
416
|
titleSpan: HTMLSpanElement,
|
|
404
417
|
nodeName: string,
|
|
405
418
|
isSelected: boolean,
|
|
406
419
|
level: number,
|
|
407
420
|
): void {
|
|
408
|
-
|
|
421
|
+
li.setAttribute("aria-level", String(level));
|
|
409
422
|
|
|
410
423
|
if (isSelected) {
|
|
411
|
-
|
|
424
|
+
li.setAttribute("aria-selected", "true");
|
|
412
425
|
|
|
413
426
|
const tabIndex = this.tabIndex;
|
|
414
427
|
|
|
415
|
-
|
|
416
|
-
|
|
428
|
+
// With multiSelect the tab stop is the active node, see updateTabStop
|
|
429
|
+
if (tabIndex !== undefined && !this.multiSelect) {
|
|
430
|
+
li.setAttribute("tabindex", String(tabIndex));
|
|
417
431
|
}
|
|
418
432
|
}
|
|
419
433
|
|
|
@@ -423,6 +437,10 @@ export default class ElementsRenderer {
|
|
|
423
437
|
// eslint-disable-next-line no-unsanitized/property -- the user opted out of escaping with autoEscape: false
|
|
424
438
|
titleSpan.innerHTML = nodeName;
|
|
425
439
|
}
|
|
440
|
+
|
|
441
|
+
// The treeitem is named by its title, without the html when autoEscape
|
|
442
|
+
// is false. Otherwise the name would contain everything in the row.
|
|
443
|
+
li.setAttribute("aria-label", titleSpan.textContent);
|
|
426
444
|
}
|
|
427
445
|
|
|
428
446
|
private getButtonClasses(isOpen: boolean): string {
|