tree-element 0.4.0 → 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.
Files changed (74) hide show
  1. package/README.md +22 -20
  2. package/lib/dataLoader.d.ts.map +1 -1
  3. package/lib/dataLoader.js +4 -1
  4. package/lib/dataLoader.js.map +1 -1
  5. package/lib/dragAndDropHandler/generateHitAreas.d.ts.map +1 -1
  6. package/lib/dragAndDropHandler/generateHitAreas.js +3 -2
  7. package/lib/dragAndDropHandler/generateHitAreas.js.map +1 -1
  8. package/lib/dragAndDropHandler/index.d.ts +2 -4
  9. package/lib/dragAndDropHandler/index.d.ts.map +1 -1
  10. package/lib/dragAndDropHandler/index.js +2 -9
  11. package/lib/dragAndDropHandler/index.js.map +1 -1
  12. package/lib/elementsRenderer.d.ts +7 -2
  13. package/lib/elementsRenderer.d.ts.map +1 -1
  14. package/lib/elementsRenderer.js +70 -23
  15. package/lib/elementsRenderer.js.map +1 -1
  16. package/lib/events.d.ts +13 -7
  17. package/lib/events.d.ts.map +1 -1
  18. package/lib/folderOpenState.d.ts +8 -0
  19. package/lib/folderOpenState.d.ts.map +1 -0
  20. package/lib/folderOpenState.js +22 -0
  21. package/lib/folderOpenState.js.map +1 -0
  22. package/lib/index.d.ts +17 -16
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +93 -49
  25. package/lib/index.js.map +1 -1
  26. package/lib/keyHandler.d.ts +2 -0
  27. package/lib/keyHandler.d.ts.map +1 -1
  28. package/lib/keyHandler.js +18 -0
  29. package/lib/keyHandler.js.map +1 -1
  30. package/lib/mouseHandler.d.ts.map +1 -1
  31. package/lib/mouseHandler.js +12 -12
  32. package/lib/mouseHandler.js.map +1 -1
  33. package/lib/node.d.ts +12 -10
  34. package/lib/node.d.ts.map +1 -1
  35. package/lib/node.js +12 -10
  36. package/lib/node.js.map +1 -1
  37. package/lib/nodeElement/folderElement.d.ts +7 -5
  38. package/lib/nodeElement/folderElement.d.ts.map +1 -1
  39. package/lib/nodeElement/folderElement.js +33 -53
  40. package/lib/nodeElement/folderElement.js.map +1 -1
  41. package/lib/options.d.ts +19 -17
  42. package/lib/options.d.ts.map +1 -1
  43. package/lib/options.js +12 -0
  44. package/lib/options.js.map +1 -0
  45. package/lib/saveStateHandler.d.ts.map +1 -1
  46. package/lib/saveStateHandler.js +6 -6
  47. package/lib/saveStateHandler.js.map +1 -1
  48. package/lib/selectNodeHandler.d.ts.map +1 -1
  49. package/lib/selectNodeHandler.js +13 -1
  50. package/lib/selectNodeHandler.js.map +1 -1
  51. package/lib/version.d.ts +1 -1
  52. package/lib/version.js +1 -1
  53. package/lib/version.js.map +1 -1
  54. package/package.json +35 -29
  55. package/src/dataLoader.ts +3 -1
  56. package/src/dragAndDropHandler/generateHitAreas.ts +3 -2
  57. package/src/dragAndDropHandler/index.ts +3 -17
  58. package/src/elementsRenderer.ts +84 -33
  59. package/src/events.ts +13 -7
  60. package/src/folderOpenState.ts +38 -0
  61. package/src/index.ts +109 -59
  62. package/src/keyHandler.ts +20 -0
  63. package/src/mouseHandler.ts +15 -13
  64. package/src/node.ts +12 -10
  65. package/src/nodeElement/folderElement.ts +45 -70
  66. package/src/options.ts +36 -17
  67. package/src/saveStateHandler.ts +5 -8
  68. package/src/selectNodeHandler.ts +12 -1
  69. package/src/version.ts +1 -1
  70. package/tree_element.css +32 -5
  71. package/tree_element.debug.js +291 -165
  72. package/tree_element.debug.js.map +1 -1
  73. package/tree_element.js +2 -2
  74. package/tree_element.js.map +1 -1
package/src/options.ts CHANGED
@@ -31,10 +31,32 @@ export type OnIsMoveHandle = (el: HTMLElement) => boolean;
31
31
 
32
32
  export type OnSetStateFromStorage = ((data: string) => void) | undefined;
33
33
 
34
+ /* The options that setOption can change. The others are read once, in the
35
+ * constructor, so changing them later would have no effect or leave the tree
36
+ * in an inconsistent state.
37
+ */
38
+ export const changeableOptions = [
39
+ "animationSpeed",
40
+ "dataUrl",
41
+ "dragAndDrop",
42
+ "keyboardSupport",
43
+ "multiSelect",
44
+ "onCanSelectNode",
45
+ "selectable",
46
+ "slide",
47
+ "startDndDelay",
48
+ "tabIndex",
49
+ ] as const;
50
+
51
+ /**
52
+ * The options that `setOption` can change.
53
+ */
54
+ export type ChangeableOption = (typeof changeableOptions)[number];
55
+
34
56
  export interface TreeElementOptions {
35
57
  /**
36
- * The duration of the slide animation: `"fast"` is 200ms, `"slow"` is
37
- * 600ms, a number is milliseconds.
58
+ * The duration of the slide animation: `"fast"` is 200 milliseconds,
59
+ * `"slow"` is 600, and a number is milliseconds.
38
60
  *
39
61
  * @defaultValue `"fast"`, or `0` when the user prefers reduced motion
40
62
  * (`prefers-reduced-motion: reduce`).
@@ -56,7 +78,7 @@ export interface TreeElementOptions {
56
78
  */
57
79
  ariaLabelledBy?: string;
58
80
  /**
59
- * Escape node names. With `false`, names are inserted as html — only do
81
+ * Escape node names. With `false`, names are inserted as HTML—only do
60
82
  * that for data you control.
61
83
  *
62
84
  * @defaultValue `true`
@@ -81,10 +103,10 @@ export interface TreeElementOptions {
81
103
  */
82
104
  buttonLeft: boolean;
83
105
  /**
84
- * The prefix of the css classes the widget puts on the elements it
106
+ * The prefix of the CSS classes the widget puts on the elements it
85
107
  * creates: with `"my-tree"` a title gets `my-tree-title` instead of
86
- * `tree-element-title`. The bundled stylesheet uses the default prefix, so
87
- * change it only if you bring your own css. See
108
+ * `tree-element-title`. The bundled style sheet uses the default prefix,
109
+ * so change it only if you bring your own CSS. See
88
110
  * [Styling](/guide/styling#changing-the-class-names).
89
111
  *
90
112
  * @defaultValue `"tree-element"`
@@ -92,12 +114,12 @@ export interface TreeElementOptions {
92
114
  */
93
115
  classPrefix: string;
94
116
  /**
95
- * The icon of a closed folder. A string is inserted as html; an element is
96
- * cloned for every node.
117
+ * The icon of a closed folder. A string is inserted as HTML, and an element
118
+ * is cloned for every node.
97
119
  *
98
120
  * Type: `HTMLElement | string`
99
121
  *
100
- * @defaultValue An svg triangle pointing right, or left when `rtl` is
122
+ * @defaultValue An SVG triangle pointing right, or left when `rtl` is
101
123
  * true. It is sized in em and filled with `currentColor`.
102
124
  * @group Appearance
103
125
  */
@@ -125,7 +147,7 @@ export interface TreeElementOptions {
125
147
  */
126
148
  dataFilter?: DataFilter;
127
149
  /**
128
- * A url to fetch the data from, or a function that returns one. The
150
+ * A URL to fetch the data from, or a function that returns one. The
129
151
  * function is called with the node that is being loaded, and without an
130
152
  * argument for the initial load. See
131
153
  * [Loading on demand](/guide/loading-on-demand).
@@ -157,9 +179,6 @@ export interface TreeElementOptions {
157
179
  * focus instead of the selection, and `Space` adds the focused node or
158
180
  * removes it. See [Selection](/guide/selection#multiple-nodes).
159
181
  *
160
- * Set it in the constructor; changing it with `setOption` is not
161
- * supported.
162
- *
163
182
  * @defaultValue `false`
164
183
  * @group Selection
165
184
  */
@@ -240,7 +259,7 @@ export interface TreeElementOptions {
240
259
  */
241
260
  onDragStop?: DragMethod;
242
261
  /**
243
- * Read the saved state, as a json string, from somewhere other than
262
+ * Read the saved state, as a JSON string, from somewhere other than
244
263
  * `localStorage`.
245
264
  *
246
265
  * Type: `() => string`
@@ -258,7 +277,7 @@ export interface TreeElementOptions {
258
277
  */
259
278
  onIsMoveHandle?: OnIsMoveHandle;
260
279
  /**
261
- * Write the state, as a json string, to somewhere other than
280
+ * Write the state, as a JSON string, to somewhere other than
262
281
  * `localStorage`.
263
282
  *
264
283
  * Type: `(state: string) => void`
@@ -271,7 +290,7 @@ export interface TreeElementOptions {
271
290
  *
272
291
  * Type: `HTMLElement | string`
273
292
  *
274
- * @defaultValue An svg triangle pointing down, sized in em and filled with
293
+ * @defaultValue An SVG triangle pointing down, sized in em and filled with
275
294
  * `currentColor`.
276
295
  * @group Appearance
277
296
  */
@@ -293,7 +312,7 @@ export interface TreeElementOptions {
293
312
  rtl?: boolean;
294
313
  /**
295
314
  * Remember the open and selected nodes in `localStorage`. A string is used
296
- * as the storage key; `true` uses `tree`. See
315
+ * as the storage key, and `true` uses `tree`. See
297
316
  * [Saving state](/guide/saving-state).
298
317
  *
299
318
  * @defaultValue `false`
@@ -67,10 +67,7 @@ export default class SaveStateHandler {
67
67
  }
68
68
 
69
69
  public getNodeIdToBeSelected(): NodeId | null {
70
- if (!this.saveStateOption) {
71
- return null;
72
- }
73
-
70
+ // Without the saveState option there is no state
74
71
  const state = this.getStateFromStorage();
75
72
 
76
73
  return state?.selected_node ? (state.selected_node[0] ?? null) : null;
@@ -243,8 +240,9 @@ export default class SaveStateHandler {
243
240
  }
244
241
  }
245
242
 
243
+ // result: is a node selected?
246
244
  private selectInitialNodes(nodeIds: NodeId[]): boolean {
247
- let selectCount = 0;
245
+ let isNodeSelected = false;
248
246
 
249
247
  for (const nodeId of nodeIds) {
250
248
  const node = this.getNodeById(nodeId);
@@ -253,11 +251,10 @@ export default class SaveStateHandler {
253
251
  continue;
254
252
  }
255
253
 
256
- selectCount += 1;
257
-
258
254
  this.addToSelection(node);
255
+ isNodeSelected = true;
259
256
  }
260
257
 
261
- return selectCount !== 0;
258
+ return isNodeSelected;
262
259
  }
263
260
  }
@@ -166,12 +166,19 @@ export default class SelectNodeHandler {
166
166
  for (const otherNode of otherSelectedNodes) {
167
167
  this.deselectNode(otherNode);
168
168
  }
169
+
170
+ for (const otherNode of otherSelectedNodes) {
171
+ this.triggerEvent("tree.deselect", { node: otherNode });
172
+ }
169
173
  } else if (selectOptions.mustToggle) {
170
174
  this.deselectNode(node);
171
175
  this.triggerEvent("tree.deselect", { node });
172
176
  }
173
177
  } else {
174
- const deselectedNode = otherSelectedNodes[0] ?? null;
178
+ // tree.select reports the first deselected node; the others, from a
179
+ // multiple selection, get a tree.deselect.
180
+ const [deselectedNode = null, ...alsoDeselectedNodes] =
181
+ otherSelectedNodes;
175
182
 
176
183
  for (const otherNode of otherSelectedNodes) {
177
184
  this.deselectNode(otherNode);
@@ -181,6 +188,10 @@ export default class SelectNodeHandler {
181
188
  this.openParents(node);
182
189
  this.getNodeElementForNode(node).select(selectOptions.mustSetFocus);
183
190
 
191
+ for (const otherNode of alsoDeselectedNodes) {
192
+ this.triggerEvent("tree.deselect", { node: otherNode });
193
+ }
194
+
184
195
  this.triggerEvent("tree.select", {
185
196
  deselectedNode,
186
197
  node,
package/src/version.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  // Generated by config/generateVersion.mjs. Do not edit.
2
- const version = "0.4.0";
2
+ const version = "0.4.1";
3
3
 
4
4
  export default version;
package/tree_element.css CHANGED
@@ -2,6 +2,7 @@
2
2
  Colors. Override them on :root or on an element around the tree, for example
3
3
  for a dark theme. :where keeps the specificity at 0, so any rule wins.
4
4
  --tree-element-selected-color defaults to --tree-element-title-color.
5
+ Set --tree-element-line-color to transparent to hide the tree lines.
5
6
  */
6
7
  :where(:root) {
7
8
  --tree-element-title-color: #1c4257;
@@ -12,6 +13,19 @@
12
13
  --tree-element-drop-hint-color: #00f;
13
14
  --tree-element-dragging-color: #fff;
14
15
  --tree-element-dragging-background: #000;
16
+ --tree-element-line-color: #d5dbe1;
17
+ }
18
+
19
+ /*
20
+ The width of the toggler. The indentation and the tree lines follow it, so
21
+ the line stays under the center of the toggler. 1.5em is 24px at a 16px
22
+ font: above that the toggler grows with the font, so the space around the
23
+ 0.8em icon, which separates it from the title, grows along. Set it to the
24
+ width of a custom icon that is wider. It is resolved where it is used, so em
25
+ is the font size of the tree.
26
+ */
27
+ :where(:root) {
28
+ --tree-element-toggler-size: max(24px, 1.5em);
15
29
  }
16
30
 
17
31
  ul.tree-element {
@@ -27,11 +41,14 @@ ul.tree-element.tree-element-dnd {
27
41
 
28
42
  ul.tree-element ul.tree-element-common {
29
43
  list-style: none outside;
30
- margin-left: 12px;
44
+ margin-left: calc(var(--tree-element-toggler-size) / 2);
31
45
  margin-right: 0;
32
46
  margin-bottom: 0;
33
47
  padding: 0;
34
48
  display: block;
49
+ /* The line down from the toggler of the folder: the indent is half the
50
+ width of the toggler, so the line is under its center. */
51
+ border-left: 1px solid var(--tree-element-line-color);
35
52
  }
36
53
 
37
54
  ul.tree-element li.tree-element-closed > ul.tree-element-common {
@@ -52,12 +69,16 @@ ul.tree-element .tree-element-toggler {
52
69
  align-items: center;
53
70
  justify-content: center;
54
71
  box-sizing: border-box;
55
- min-width: 24px;
72
+ min-width: var(--tree-element-toggler-size);
56
73
  min-height: 24px;
57
74
  border-bottom: none;
58
75
  color: var(--tree-element-toggler-color);
59
76
  text-decoration: none;
60
77
  vertical-align: middle;
78
+ /* The line under the toggler is 1px wide, so its center is half a pixel
79
+ after the center of the toggler. Move the toggler along.
80
+ Margin or padding would be rounded to a whole pixel; translate isn't. */
81
+ translate: 0.5px;
61
82
  }
62
83
 
63
84
  ul.tree-element .tree-element-toggler:hover {
@@ -105,7 +126,7 @@ ul.tree-element li.tree-element-common:focus-visible
105
126
  /* Line up with the titles of folders, after the toggler */
106
127
 
107
128
  ul.tree-element .tree-element-title-button-left {
108
- margin-left: 24px;
129
+ margin-left: var(--tree-element-toggler-size);
109
130
  }
110
131
 
111
132
  ul.tree-element .tree-element-title-button-left.tree-element-title-folder {
@@ -186,12 +207,18 @@ ul.tree-element.tree-element-rtl {
186
207
 
187
208
  ul.tree-element.tree-element-rtl ul.tree-element-common {
188
209
  margin-left: 0;
189
- margin-right: 12px;
210
+ margin-right: calc(var(--tree-element-toggler-size) / 2);
211
+ border-left: none;
212
+ border-right: 1px solid var(--tree-element-line-color);
213
+ }
214
+
215
+ ul.tree-element.tree-element-rtl .tree-element-toggler {
216
+ translate: -0.5px;
190
217
  }
191
218
 
192
219
  ul.tree-element.tree-element-rtl .tree-element-title {
193
220
  margin-left: 0;
194
- margin-right: 24px;
221
+ margin-right: var(--tree-element-toggler-size);
195
222
  }
196
223
 
197
224
  ul.tree-element.tree-element-rtl .tree-element-title.tree-element-title-folder {