@ixfx/components 0.2.3 → 0.2.5

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 (291) hide show
  1. package/LICENSE +1 -1
  2. package/bundle/chunk-pbuEa-1d.js +13 -0
  3. package/bundle/index.d.ts +4702 -569
  4. package/bundle/index.d.ts.map +1 -1
  5. package/bundle/index.js +34929 -2800
  6. package/bundle/index.js.map +1 -1
  7. package/bundle/style.css +133 -0
  8. package/dist/ac-text.d.ts +97 -0
  9. package/dist/ac-text.d.ts.map +1 -0
  10. package/dist/ac-text.js +484 -0
  11. package/dist/ac-text.js.map +1 -0
  12. package/dist/button-DgKUzE5o.js +121 -0
  13. package/dist/button-DgKUzE5o.js.map +1 -0
  14. package/dist/button.d.ts +21 -0
  15. package/dist/button.d.ts.map +1 -0
  16. package/dist/button.js +3 -0
  17. package/dist/checkbox.d.ts +34 -0
  18. package/dist/checkbox.d.ts.map +1 -0
  19. package/dist/checkbox.js +251 -0
  20. package/dist/checkbox.js.map +1 -0
  21. package/dist/chunk-pbuEa-1d.js +13 -0
  22. package/dist/colour-C3MQIjFJ-BJV-QC-3.js +4138 -0
  23. package/dist/colour-C3MQIjFJ-BJV-QC-3.js.map +1 -0
  24. package/dist/colour-picker-Bw-AZoWx.js +922 -0
  25. package/dist/colour-picker-Bw-AZoWx.js.map +1 -0
  26. package/dist/colour-picker.d.ts +2 -0
  27. package/dist/colour-picker.js +2 -0
  28. package/dist/crumbs.d.ts +92 -0
  29. package/dist/crumbs.d.ts.map +1 -0
  30. package/dist/crumbs.js +789 -0
  31. package/dist/crumbs.js.map +1 -0
  32. package/dist/data-display.d.ts +24 -12
  33. package/dist/data-display.d.ts.map +1 -1
  34. package/dist/data-display.js +96 -105
  35. package/dist/data-display.js.map +1 -1
  36. package/dist/decorate-BZbkqIhA.js +9 -0
  37. package/dist/defaults-FEZaGiYZ.js +38 -0
  38. package/dist/defaults-FEZaGiYZ.js.map +1 -0
  39. package/dist/dist-By5URG0Y.js +1290 -0
  40. package/dist/dist-By5URG0Y.js.map +1 -0
  41. package/dist/dist-D7kJgnSE.js +1039 -0
  42. package/dist/dist-D7kJgnSE.js.map +1 -0
  43. package/dist/editable-label.d.ts +71 -0
  44. package/dist/editable-label.d.ts.map +1 -0
  45. package/dist/editable-label.js +279 -0
  46. package/dist/editable-label.js.map +1 -0
  47. package/dist/hex-editor-Gib1Grg2.d.ts +131 -0
  48. package/dist/hex-editor-Gib1Grg2.d.ts.map +1 -0
  49. package/dist/hex-editor-Wd6ZtZA2.js +1147 -0
  50. package/dist/hex-editor-Wd6ZtZA2.js.map +1 -0
  51. package/dist/hex.d.ts +2 -0
  52. package/dist/hex.js +2 -0
  53. package/dist/highlight-CAjwQ4GE.js +50 -0
  54. package/dist/highlight-CAjwQ4GE.js.map +1 -0
  55. package/dist/icon-BR5aTIiP.js +100 -0
  56. package/dist/icon-BR5aTIiP.js.map +1 -0
  57. package/dist/icon-DQKdfR7U.d.ts +36 -0
  58. package/dist/icon-DQKdfR7U.d.ts.map +1 -0
  59. package/dist/icons.d.ts +3 -0
  60. package/dist/icons.js +9 -0
  61. package/dist/icons.js.map +1 -0
  62. package/dist/incr-search-BjCNClnC.js +311 -0
  63. package/dist/incr-search-BjCNClnC.js.map +1 -0
  64. package/dist/incr-search.d.ts +2 -0
  65. package/dist/incr-search.js +3 -0
  66. package/dist/index-B1hNR7Z9.d.ts +36 -0
  67. package/dist/index-B1hNR7Z9.d.ts.map +1 -0
  68. package/dist/index-C5dK8RZa.d.ts +23 -0
  69. package/dist/index-C5dK8RZa.d.ts.map +1 -0
  70. package/dist/index-C9iBN908.d.ts +160 -0
  71. package/dist/index-C9iBN908.d.ts.map +1 -0
  72. package/dist/index-CPVcTTXo.d.ts +396 -0
  73. package/dist/index-CPVcTTXo.d.ts.map +1 -0
  74. package/dist/index-CQxanqXF.d.ts +183 -0
  75. package/dist/index-CQxanqXF.d.ts.map +1 -0
  76. package/dist/index-CpbBI3n7.d.ts +1239 -0
  77. package/dist/index-CpbBI3n7.d.ts.map +1 -0
  78. package/dist/index.d.ts +422 -13
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +5729 -14
  81. package/dist/index.js.map +1 -1
  82. package/dist/interaction-CQDQJf2Q.js +1 -0
  83. package/dist/keyboard-De_vOHLg.js +20 -0
  84. package/dist/keyboard-De_vOHLg.js.map +1 -0
  85. package/dist/labelled-input-base-DS6-6C0r.js +155 -0
  86. package/dist/labelled-input-base-DS6-6C0r.js.map +1 -0
  87. package/dist/labelled-input-base-DnRh4reU.d.ts +43 -0
  88. package/dist/labelled-input-base-DnRh4reU.d.ts.map +1 -0
  89. package/dist/labelled-radial-input.d.ts +34 -0
  90. package/dist/labelled-radial-input.d.ts.map +1 -0
  91. package/dist/labelled-radial-input.js +188 -0
  92. package/dist/labelled-radial-input.js.map +1 -0
  93. package/dist/labelled-range-input.d.ts +2 -0
  94. package/dist/labelled-range-input.js +132 -0
  95. package/dist/labelled-range-input.js.map +1 -0
  96. package/dist/led.d.ts +25 -0
  97. package/dist/led.d.ts.map +1 -0
  98. package/dist/led.js +92 -0
  99. package/dist/led.js.map +1 -0
  100. package/dist/menu-BV6UFx_z.js +813 -0
  101. package/dist/menu-BV6UFx_z.js.map +1 -0
  102. package/dist/menu-item-Cikacp4F.d.ts +128 -0
  103. package/dist/menu-item-Cikacp4F.d.ts.map +1 -0
  104. package/dist/menu-item-DEzL_Yjk.js +766 -0
  105. package/dist/menu-item-DEzL_Yjk.js.map +1 -0
  106. package/dist/menu.d.ts +3 -0
  107. package/dist/menu.js +3 -0
  108. package/dist/miller.d.ts +324 -0
  109. package/dist/miller.d.ts.map +1 -0
  110. package/dist/miller.js +1343 -0
  111. package/dist/miller.js.map +1 -0
  112. package/dist/narrowed-text.d.ts +72 -0
  113. package/dist/narrowed-text.d.ts.map +1 -0
  114. package/dist/narrowed-text.js +571 -0
  115. package/dist/narrowed-text.js.map +1 -0
  116. package/dist/panel.d.ts +43 -0
  117. package/dist/panel.d.ts.map +1 -0
  118. package/dist/panel.js +408 -0
  119. package/dist/panel.js.map +1 -0
  120. package/dist/plots.d.ts +2 -0
  121. package/dist/plots.js +2 -0
  122. package/dist/polar-pad.d.ts +67 -0
  123. package/dist/polar-pad.d.ts.map +1 -0
  124. package/dist/polar-pad.js +537 -0
  125. package/dist/polar-pad.js.map +1 -0
  126. package/dist/popup-Ddx9LCzY.js +24 -0
  127. package/dist/popup-Ddx9LCzY.js.map +1 -0
  128. package/dist/prominence-CreDBdCs.js +98 -0
  129. package/dist/prominence-CreDBdCs.js.map +1 -0
  130. package/dist/radial-input-CNJ8asY2.js +849 -0
  131. package/dist/radial-input-CNJ8asY2.js.map +1 -0
  132. package/dist/radial-input-xSTd4kEz.d.ts +70 -0
  133. package/dist/radial-input-xSTd4kEz.d.ts.map +1 -0
  134. package/dist/radial-input.d.ts +2 -0
  135. package/dist/radial-input.js +2 -0
  136. package/dist/range-input.d.ts +43 -0
  137. package/dist/range-input.d.ts.map +1 -0
  138. package/dist/range-input.js +459 -0
  139. package/dist/range-input.js.map +1 -0
  140. package/dist/range.d.ts +28 -0
  141. package/dist/range.d.ts.map +1 -0
  142. package/dist/range.js +136 -0
  143. package/dist/range.js.map +1 -0
  144. package/dist/registry-CZEB1YI9.js +21 -0
  145. package/dist/registry-CZEB1YI9.js.map +1 -0
  146. package/dist/selecthorizontal.d.ts +13 -8
  147. package/dist/selecthorizontal.d.ts.map +1 -1
  148. package/dist/selecthorizontal.js +21 -25
  149. package/dist/selecthorizontal.js.map +1 -1
  150. package/dist/snap-container.d.ts +50 -0
  151. package/dist/snap-container.d.ts.map +1 -0
  152. package/dist/snap-container.js +297 -0
  153. package/dist/snap-container.js.map +1 -0
  154. package/dist/split-layout.d.ts +56 -0
  155. package/dist/split-layout.d.ts.map +1 -0
  156. package/dist/split-layout.js +545 -0
  157. package/dist/split-layout.js.map +1 -0
  158. package/dist/style.css +133 -0
  159. package/dist/swipe.d.ts +48 -0
  160. package/dist/swipe.d.ts.map +1 -0
  161. package/dist/swipe.js +204 -0
  162. package/dist/swipe.js.map +1 -0
  163. package/dist/tab-list-CFF7uSYH.d.ts +66 -0
  164. package/dist/tab-list-CFF7uSYH.d.ts.map +1 -0
  165. package/dist/tabs.d.ts +2 -0
  166. package/dist/tabs.js +203 -0
  167. package/dist/tabs.js.map +1 -0
  168. package/dist/tickled-controller-Cq1Va0rR.d.ts +218 -0
  169. package/dist/tickled-controller-Cq1Va0rR.d.ts.map +1 -0
  170. package/dist/tickled-styles-Bg3QbcrD.js +327 -0
  171. package/dist/tickled-styles-Bg3QbcrD.js.map +1 -0
  172. package/dist/timeline-DBWJsSjd.js +1748 -0
  173. package/dist/timeline-DBWJsSjd.js.map +1 -0
  174. package/dist/timeline.d.ts +2 -0
  175. package/dist/timeline.js +2 -0
  176. package/dist/tooltip-DMxmBNky.js +104 -0
  177. package/dist/tooltip-DMxmBNky.js.map +1 -0
  178. package/dist/tooltip-kfUBWYVB.d.ts +26 -0
  179. package/dist/tooltip-kfUBWYVB.d.ts.map +1 -0
  180. package/dist/tree-component-CQpoPo4s.d.ts +326 -0
  181. package/dist/tree-component-CQpoPo4s.d.ts.map +1 -0
  182. package/dist/tree-data-model-DGvRVOFY.js +190 -0
  183. package/dist/tree-data-model-DGvRVOFY.js.map +1 -0
  184. package/dist/tree.d.ts +100 -0
  185. package/dist/tree.d.ts.map +1 -0
  186. package/dist/tree.js +1144 -0
  187. package/dist/tree.js.map +1 -0
  188. package/dist/types-BKaqnpwx.d.ts +8 -0
  189. package/dist/types-BKaqnpwx.d.ts.map +1 -0
  190. package/dist/xy-axis-BANbgrI5.js +6243 -0
  191. package/dist/xy-axis-BANbgrI5.js.map +1 -0
  192. package/dist/xy-axis-CIFok7MF.d.ts +141 -0
  193. package/dist/xy-axis-CIFok7MF.d.ts.map +1 -0
  194. package/dist/xy-pad.d.ts +81 -0
  195. package/dist/xy-pad.d.ts.map +1 -0
  196. package/dist/xy-pad.js +435 -0
  197. package/dist/xy-pad.js.map +1 -0
  198. package/package.json +48 -39
  199. package/README.md +0 -14
  200. package/dist/charts/frequency-histogram-plot.d.ts +0 -33
  201. package/dist/charts/frequency-histogram-plot.d.ts.map +0 -1
  202. package/dist/charts/frequency-histogram-plot.js +0 -54
  203. package/dist/charts/frequency-histogram-plot.js.map +0 -1
  204. package/dist/charts/histogram-vis.d.ts +0 -57
  205. package/dist/charts/histogram-vis.d.ts.map +0 -1
  206. package/dist/charts/histogram-vis.js +0 -222
  207. package/dist/charts/histogram-vis.js.map +0 -1
  208. package/dist/charts/plot.d.ts +0 -199
  209. package/dist/charts/plot.d.ts.map +0 -1
  210. package/dist/charts/plot.js +0 -914
  211. package/dist/charts/plot.js.map +0 -1
  212. package/dist/crumb-nav.d.ts +0 -41
  213. package/dist/crumb-nav.d.ts.map +0 -1
  214. package/dist/crumb-nav.js +0 -198
  215. package/dist/crumb-nav.js.map +0 -1
  216. package/dist/crumb-navigation.d.ts +0 -30
  217. package/dist/crumb-navigation.d.ts.map +0 -1
  218. package/dist/crumb-navigation.js +0 -307
  219. package/dist/crumb-navigation.js.map +0 -1
  220. package/dist/dial.d.ts +0 -89
  221. package/dist/dial.d.ts.map +0 -1
  222. package/dist/dial.js +0 -506
  223. package/dist/dial.js.map +0 -1
  224. package/dist/my-element.d.ts +0 -14
  225. package/dist/my-element.d.ts.map +0 -1
  226. package/dist/my-element.js +0 -44
  227. package/dist/my-element.js.map +0 -1
  228. package/dist/split-layout/split-layout.d.ts +0 -35
  229. package/dist/split-layout/split-layout.d.ts.map +0 -1
  230. package/dist/split-layout/split-layout.js +0 -220
  231. package/dist/split-layout/split-layout.js.map +0 -1
  232. package/dist/svg-edit-label.d.ts +0 -2
  233. package/dist/svg-edit-label.d.ts.map +0 -1
  234. package/dist/svg-edit-label.js +0 -110
  235. package/dist/svg-edit-label.js.map +0 -1
  236. package/dist/tabs/tab-layout-item.d.ts +0 -10
  237. package/dist/tabs/tab-layout-item.d.ts.map +0 -1
  238. package/dist/tabs/tab-layout-item.js +0 -20
  239. package/dist/tabs/tab-layout-item.js.map +0 -1
  240. package/dist/tabs/tab-layout.d.ts +0 -10
  241. package/dist/tabs/tab-layout.d.ts.map +0 -1
  242. package/dist/tabs/tab-layout.js +0 -25
  243. package/dist/tabs/tab-layout.js.map +0 -1
  244. package/dist/tabs/tab-list-item.d.ts +0 -18
  245. package/dist/tabs/tab-list-item.d.ts.map +0 -1
  246. package/dist/tabs/tab-list-item.js +0 -63
  247. package/dist/tabs/tab-list-item.js.map +0 -1
  248. package/dist/tabs/tab-list.d.ts +0 -21
  249. package/dist/tabs/tab-list.d.ts.map +0 -1
  250. package/dist/tabs/tab-list.js +0 -148
  251. package/dist/tabs/tab-list.js.map +0 -1
  252. package/dist/tabs/tab-panel.d.ts +0 -11
  253. package/dist/tabs/tab-panel.d.ts.map +0 -1
  254. package/dist/tabs/tab-panel.js +0 -30
  255. package/dist/tabs/tab-panel.js.map +0 -1
  256. package/dist/tabs/tab-panels.d.ts +0 -30
  257. package/dist/tabs/tab-panels.d.ts.map +0 -1
  258. package/dist/tabs/tab-panels.js +0 -80
  259. package/dist/tabs/tab-panels.js.map +0 -1
  260. package/dist/util/circle-arc.d.ts +0 -34
  261. package/dist/util/circle-arc.d.ts.map +0 -1
  262. package/dist/util/circle-arc.js +0 -77
  263. package/dist/util/circle-arc.js.map +0 -1
  264. package/dist/util/compare.d.ts +0 -53
  265. package/dist/util/compare.d.ts.map +0 -1
  266. package/dist/util/compare.js +0 -91
  267. package/dist/util/compare.js.map +0 -1
  268. package/dist/util/content-editable.d.ts +0 -2
  269. package/dist/util/content-editable.d.ts.map +0 -1
  270. package/dist/util/content-editable.js +0 -13
  271. package/dist/util/content-editable.js.map +0 -1
  272. package/dist/util/drag-controller.d.ts +0 -13
  273. package/dist/util/drag-controller.d.ts.map +0 -1
  274. package/dist/util/drag-controller.js +0 -21
  275. package/dist/util/drag-controller.js.map +0 -1
  276. package/dist/util/key-value.d.ts +0 -5
  277. package/dist/util/key-value.d.ts.map +0 -1
  278. package/dist/util/key-value.js +0 -30
  279. package/dist/util/key-value.js.map +0 -1
  280. package/dist/util/primitive-types.d.ts +0 -6
  281. package/dist/util/primitive-types.d.ts.map +0 -1
  282. package/dist/util/primitive-types.js +0 -2
  283. package/dist/util/primitive-types.js.map +0 -1
  284. package/dist/util/svg-editable-text.d.ts +0 -22
  285. package/dist/util/svg-editable-text.d.ts.map +0 -1
  286. package/dist/util/svg-editable-text.js +0 -121
  287. package/dist/util/svg-editable-text.js.map +0 -1
  288. package/dist/util/time.d.ts +0 -2
  289. package/dist/util/time.d.ts.map +0 -1
  290. package/dist/util/time.js +0 -2
  291. package/dist/util/time.js.map +0 -1
package/dist/tree.js ADDED
@@ -0,0 +1,1144 @@
1
+ import { n as tickledStyles, r as TickledController, t as tickledItemStyles } from "./tickled-styles-Bg3QbcrD.js";
2
+ import { t as __decorate } from "./decorate-BZbkqIhA.js";
3
+ import "./interaction-CQDQJf2Q.js";
4
+ import { t as TreeDataModel } from "./tree-data-model-DGvRVOFY.js";
5
+ import { LitElement, css, html, nothing } from "lit";
6
+ import { customElement, property, state } from "lit/decorators.js";
7
+ //#region src/tree/tree-base.ts
8
+ let TreeBaseElement = class TreeBaseElement extends LitElement {
9
+ constructor(..._args) {
10
+ super(..._args);
11
+ this.exclusivity = `none`;
12
+ this.selectionFilter = `all`;
13
+ this.interactionMode = `standard`;
14
+ this._selectedNodes = /* @__PURE__ */ new Set();
15
+ this.disableKeyboardNavigation = false;
16
+ this._expandedKeys = /* @__PURE__ */ new Set();
17
+ this.tickled = new TickledController(this, {
18
+ getItems: () => {
19
+ return this.#getAllNodes().map(({ node }) => ({
20
+ id: node.item.key,
21
+ enabled: true
22
+ }));
23
+ },
24
+ onStateChange: () => {
25
+ this.setAttribute(`data-input-mode`, this.tickled.inputMode);
26
+ }
27
+ });
28
+ this._focusedDepth = 0;
29
+ this.#config = { indent: 20 };
30
+ this.#onKeyDown = (event) => {
31
+ const nodes = this.#getAllNodes();
32
+ if (nodes.length === 0) return;
33
+ if ([
34
+ `ArrowDown`,
35
+ `ArrowUp`,
36
+ `ArrowLeft`,
37
+ `ArrowRight`,
38
+ `Enter`,
39
+ ` `
40
+ ].includes(event.key)) this.tickled.setInputMode(`keyboard`);
41
+ const currentIdx = this.#getFocusedIndex();
42
+ switch (event.key) {
43
+ case `ArrowDown`: {
44
+ event.preventDefault();
45
+ const nextIdx = Math.min(currentIdx + 1, nodes.length - 1);
46
+ if (this.interactionMode === `implicit`) this.select(nodes[nextIdx].node);
47
+ this.#setFocusedIndex(nextIdx);
48
+ if (event.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`)) this.#extendRangeSelection(nodes, currentIdx, nextIdx);
49
+ else if (this.interactionMode === `vscode`) this.#anchor = void 0;
50
+ this.requestUpdate();
51
+ break;
52
+ }
53
+ case `ArrowUp`: {
54
+ event.preventDefault();
55
+ const prevIdx = Math.max(currentIdx - 1, 0);
56
+ if (this.interactionMode === `implicit`) this.select(nodes[prevIdx].node);
57
+ this.#setFocusedIndex(prevIdx);
58
+ if (event.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`)) this.#extendRangeSelection(nodes, currentIdx, prevIdx);
59
+ else if (this.interactionMode === `vscode`) this.#anchor = void 0;
60
+ this.requestUpdate();
61
+ break;
62
+ }
63
+ case `ArrowRight`: {
64
+ event.preventDefault();
65
+ const current = nodes[currentIdx];
66
+ if (current && current.node.item.isLeaf !== true) {
67
+ if (!this._expandedKeys.has(current.node.item.key)) this.#toggleExpand(current.node, current.depth);
68
+ else if (currentIdx + 1 < nodes.length) this.#setFocusedIndex(currentIdx + 1);
69
+ }
70
+ this.requestUpdate();
71
+ break;
72
+ }
73
+ case `ArrowLeft`: {
74
+ event.preventDefault();
75
+ const current = nodes[currentIdx];
76
+ if (current && this._expandedKeys.has(current.node.item.key)) {
77
+ this._expandedKeys.delete(current.node.item.key);
78
+ this.dispatchEvent(new CustomEvent(`collapse`, {
79
+ detail: {
80
+ node: current.node,
81
+ depth: current.depth
82
+ },
83
+ bubbles: true,
84
+ composed: true
85
+ }));
86
+ } else if (current.depth > 1) {
87
+ const parentDepth = current.depth - 1;
88
+ for (let i = currentIdx - 1; i >= 0; i--) if (nodes[i].depth === parentDepth) {
89
+ this.#setFocusedIndex(i);
90
+ break;
91
+ }
92
+ }
93
+ this.requestUpdate();
94
+ break;
95
+ }
96
+ case `Enter`: {
97
+ event.preventDefault();
98
+ const current = nodes[currentIdx];
99
+ if (current) {
100
+ if (this.interactionMode !== `manual` && this.interactionMode !== `checked`) this.select(current.node);
101
+ this.dispatchEvent(new CustomEvent(`activate`, {
102
+ detail: {
103
+ node: current.node,
104
+ depth: current.depth
105
+ },
106
+ bubbles: true,
107
+ composed: true
108
+ }));
109
+ this.requestUpdate();
110
+ }
111
+ break;
112
+ }
113
+ case ` `: {
114
+ event.preventDefault();
115
+ const current = nodes[currentIdx];
116
+ if (!current) break;
117
+ if (this.interactionMode === `checked`) this._handleCheckboxClick(new MouseEvent("click"), current.node, current.depth);
118
+ else if (this.interactionMode === `standard` || this.interactionMode === `vscode` || this.interactionMode === `manual`) this.#toggleSelection(current.node, current.depth);
119
+ break;
120
+ }
121
+ case `Escape`:
122
+ event.preventDefault();
123
+ this.#abortPending();
124
+ break;
125
+ }
126
+ };
127
+ this.#onDblClick = (e) => {
128
+ const li = e.target.closest(`li`);
129
+ if (!li) return;
130
+ const key = li.dataset.key;
131
+ if (!key) return;
132
+ const node = this.#getAllNodes().find((n) => n.node.item.key === key);
133
+ if (!node) return;
134
+ this.dispatchEvent(new CustomEvent(`activate`, {
135
+ detail: {
136
+ node: node.node,
137
+ depth: node.depth
138
+ },
139
+ bubbles: true,
140
+ composed: true
141
+ }));
142
+ };
143
+ }
144
+ get selectedNodes() {
145
+ return this._selectedNodes;
146
+ }
147
+ #pendingNode;
148
+ #pendingController;
149
+ #modelUnsubscribe;
150
+ #anchor;
151
+ #config;
152
+ updated(changedProps) {
153
+ if (changedProps.has(`root`) && this.root) {
154
+ this.#abortPending();
155
+ this._expandedKeys.clear();
156
+ this.#clearSelectionInternal();
157
+ this.tickled.clearTickled();
158
+ this._focusedDepth = 0;
159
+ }
160
+ if (changedProps.has(`model`)) {
161
+ this.#modelUnsubscribe?.();
162
+ this.#modelUnsubscribe = void 0;
163
+ const model = this.model;
164
+ if (model) {
165
+ this.root = model.root;
166
+ this.#modelUnsubscribe = model._subscribe((event) => {
167
+ if (event.type === `root`) {
168
+ this.root = event.root;
169
+ this.requestUpdate();
170
+ } else if (event.type === `invalidate`) {
171
+ if (this._expandedKeys.has(event.key)) {
172
+ this._expandedKeys.delete(event.key);
173
+ this.requestUpdate();
174
+ }
175
+ }
176
+ });
177
+ }
178
+ }
179
+ if (changedProps.has(`interactionMode`)) {
180
+ this.#anchor = void 0;
181
+ this.clearSelection();
182
+ }
183
+ if (changedProps.has(`selectedNode`) && this.selectedNode) {
184
+ if (changedProps.get(`selectedNode`) !== this.selectedNode) this.tickled.setTickled(this.selectedNode.item.key);
185
+ }
186
+ }
187
+ connectedCallback() {
188
+ super.connectedCallback();
189
+ this.setAttribute(`data-input-mode`, `mouse`);
190
+ }
191
+ disconnectedCallback() {
192
+ super.disconnectedCallback();
193
+ this.#modelUnsubscribe?.();
194
+ this.#modelUnsubscribe = void 0;
195
+ }
196
+ getConfig() {
197
+ return { ...this.#config };
198
+ }
199
+ setConfig(config) {
200
+ this.#config = {
201
+ ...this.#config,
202
+ ...config
203
+ };
204
+ this.requestUpdate();
205
+ }
206
+ select(node) {
207
+ if (!this.#canSelect(node)) return;
208
+ const prev = new Set(this._selectedNodes);
209
+ this._selectedNodes.clear();
210
+ this._selectedNodes.add(node);
211
+ this.selectedNode = node;
212
+ this.#dispatchSelect(prev);
213
+ this.requestUpdate();
214
+ }
215
+ deselect(node) {
216
+ if (!this._selectedNodes.has(node)) return;
217
+ const prev = new Set(this._selectedNodes);
218
+ this._selectedNodes.delete(node);
219
+ if (this.selectedNode === node) this.selectedNode = [...this._selectedNodes].at(-1);
220
+ this.#dispatchSelect(prev);
221
+ this.requestUpdate();
222
+ }
223
+ clearSelection() {
224
+ if (this._selectedNodes.size === 0) return;
225
+ const prev = new Set(this._selectedNodes);
226
+ this.#clearSelectionInternal();
227
+ this.#dispatchSelect(prev);
228
+ this.requestUpdate();
229
+ }
230
+ navigateTo(keys) {
231
+ if (!this.root?.children) return false;
232
+ this.#abortPending();
233
+ this.#clearSelectionInternal();
234
+ this.tickled.clearTickled();
235
+ this._focusedDepth = 0;
236
+ let currentNode = this.root;
237
+ for (const key of keys) {
238
+ const children = currentNode?.children;
239
+ if (!children) return false;
240
+ const found = children.find((n) => n.item.key === key);
241
+ if (!found) return false;
242
+ this.#applyExclusivity(found.item.key);
243
+ this._expandedKeys.add(found.item.key);
244
+ if (key === keys.at(-1)) {
245
+ this.select(found);
246
+ this.tickled.setTickled(found.item.key);
247
+ }
248
+ currentNode = found;
249
+ }
250
+ this.requestUpdate();
251
+ this.#scrollKeyIntoView(keys.at(-1));
252
+ return true;
253
+ }
254
+ navigateToNode(predicate) {
255
+ if (!this.root?.children) return false;
256
+ const keyPath = this.#findNodePath(this.root.children, predicate);
257
+ if (!keyPath) return false;
258
+ return this.navigateTo(keyPath);
259
+ }
260
+ getNodes() {
261
+ if (!this.root?.children) return [];
262
+ const result = [];
263
+ const visit = (node, depth) => {
264
+ result.push({
265
+ node,
266
+ depth
267
+ });
268
+ if (node.children) for (const child of node.children) visit(child, depth + 1);
269
+ };
270
+ for (const child of this.root.children) visit(child, 0);
271
+ return result;
272
+ }
273
+ #canSelect(node) {
274
+ if (this.selectionFilter === `none`) return false;
275
+ if (this.selectionFilter === `leaf`) return node.item.isLeaf === true;
276
+ if (this.selectionFilter === `branch`) return node.item.isLeaf === false || node.children !== void 0 && node.children.length > 0;
277
+ return true;
278
+ }
279
+ #clearSelectionInternal() {
280
+ this._selectedNodes.clear();
281
+ this.selectedNode = void 0;
282
+ }
283
+ #dispatchSelect(previous) {
284
+ const detail = {
285
+ selected: new Set(this._selectedNodes),
286
+ previous
287
+ };
288
+ this.dispatchEvent(new CustomEvent(`select`, {
289
+ detail,
290
+ bubbles: true,
291
+ composed: true
292
+ }));
293
+ }
294
+ #findNodePath(nodes, predicate, prefix = []) {
295
+ for (const node of nodes) {
296
+ const path = [...prefix, node.item.key];
297
+ if (predicate(node)) return path;
298
+ if (node.children?.length) {
299
+ const result = this.#findNodePath(node.children, predicate, path);
300
+ if (result) return result;
301
+ }
302
+ }
303
+ }
304
+ renderNodeContent(node, _depth) {
305
+ const isBranch = node.item.isLeaf !== true;
306
+ const isExpanded = this._isExpanded(node);
307
+ return html`
308
+ <span class="tree-caret">
309
+ ${isBranch ? html`<ixfx-icon class="caret-icon" name="chevron-down" .rotate=${isExpanded ? nothing : `-90deg`}></ixfx-icon>` : ``}
310
+ </span>
311
+ <span class="tree-label">${node.item.label}</span>
312
+ `;
313
+ }
314
+ _handleItemClick(node, depth, event) {
315
+ if (this.#pendingNode && this._expandedKeys.has(this.#pendingNode.item.key)) this.#abortPending();
316
+ this.tickled.handlePointerEnter(node.item.key);
317
+ this._focusedDepth = depth;
318
+ this.dispatchEvent(new CustomEvent(`item-click`, {
319
+ detail: {
320
+ node,
321
+ depth
322
+ },
323
+ bubbles: true,
324
+ composed: true
325
+ }));
326
+ switch (this.interactionMode) {
327
+ case `implicit`:
328
+ this.#handleImplicitClick(node, depth);
329
+ break;
330
+ case `standard`:
331
+ this.#handleStandardClick(node, depth, event);
332
+ break;
333
+ case `vscode`:
334
+ this.#handleStandardClick(node, depth, event);
335
+ break;
336
+ case `manual`:
337
+ this.#handleManualClick(node, depth, event);
338
+ break;
339
+ case `checked`:
340
+ this.#handleExpandOnly(node, depth);
341
+ break;
342
+ }
343
+ this.requestUpdate();
344
+ }
345
+ #handleImplicitClick(node, depth) {
346
+ if (this.#canSelect(node)) {
347
+ const prev = new Set(this._selectedNodes);
348
+ this._selectedNodes.clear();
349
+ this._selectedNodes.add(node);
350
+ this.selectedNode = node;
351
+ if (!prev.has(node) || prev.size !== 1) this.#dispatchSelect(prev);
352
+ }
353
+ this.#toggleExpand(node, depth);
354
+ }
355
+ #handleStandardClick(node, depth, event) {
356
+ const isMeta = event ? event.metaKey || event.ctrlKey : false;
357
+ const isShift = event ? event.shiftKey : false;
358
+ if (isMeta) {
359
+ if (this.#canSelect(node)) {
360
+ const prev = new Set(this._selectedNodes);
361
+ if (this._selectedNodes.has(node)) {
362
+ this._selectedNodes.delete(node);
363
+ if (this.selectedNode === node) this.selectedNode = [...this._selectedNodes].at(-1);
364
+ } else {
365
+ this._selectedNodes.add(node);
366
+ this.selectedNode = node;
367
+ this.#anchor = {
368
+ node,
369
+ flatIndex: this.#getFlatIndexOf(node)
370
+ };
371
+ }
372
+ this.#dispatchSelect(prev);
373
+ }
374
+ return;
375
+ }
376
+ if (isShift && this.#anchor) {
377
+ const visible = this.#getAllNodes();
378
+ const anchorIdx = visible.findIndex((n) => n.node === this.#anchor.node);
379
+ const targetIdx = visible.findIndex((n) => n.node === node);
380
+ if (anchorIdx >= 0 && targetIdx >= 0) {
381
+ const prev = new Set(this._selectedNodes);
382
+ const from = Math.min(anchorIdx, targetIdx);
383
+ const to = Math.max(anchorIdx, targetIdx);
384
+ const rangeNodes = visible.slice(from, to + 1).map((n) => n.node).filter((n) => this.#canSelect(n));
385
+ this._selectedNodes = new Set(rangeNodes);
386
+ this.selectedNode = node;
387
+ this.#dispatchSelect(prev);
388
+ }
389
+ return;
390
+ }
391
+ if (this.#canSelect(node)) {
392
+ const prev = new Set(this._selectedNodes);
393
+ this._selectedNodes.clear();
394
+ this._selectedNodes.add(node);
395
+ this.selectedNode = node;
396
+ this.#anchor = {
397
+ node,
398
+ flatIndex: this.#getFlatIndexOf(node)
399
+ };
400
+ if (!prev.has(node) || prev.size !== 1) this.#dispatchSelect(prev);
401
+ }
402
+ this.#toggleExpand(node, depth);
403
+ }
404
+ #handleExpandOnly(node, depth) {
405
+ this.#toggleExpand(node, depth);
406
+ }
407
+ #handleManualClick(node, depth, event) {
408
+ const isMeta = event ? event.metaKey || event.ctrlKey : false;
409
+ const isShift = event ? event.shiftKey : false;
410
+ if (isMeta) {
411
+ if (this.#canSelect(node)) {
412
+ const prev = new Set(this._selectedNodes);
413
+ if (this._selectedNodes.has(node)) {
414
+ this._selectedNodes.delete(node);
415
+ if (this.selectedNode === node) this.selectedNode = [...this._selectedNodes].at(-1);
416
+ } else {
417
+ this._selectedNodes.add(node);
418
+ this.selectedNode = node;
419
+ this.#anchor = {
420
+ node,
421
+ flatIndex: this.#getFlatIndexOf(node)
422
+ };
423
+ }
424
+ this.#dispatchSelect(prev);
425
+ }
426
+ return;
427
+ }
428
+ if (isShift && this.#anchor) {
429
+ const visible = this.#getAllNodes();
430
+ const anchorIdx = visible.findIndex((n) => n.node === this.#anchor.node);
431
+ const targetIdx = visible.findIndex((n) => n.node === node);
432
+ if (anchorIdx >= 0 && targetIdx >= 0) {
433
+ const prev = new Set(this._selectedNodes);
434
+ const from = Math.min(anchorIdx, targetIdx);
435
+ const to = Math.max(anchorIdx, targetIdx);
436
+ const rangeNodes = visible.slice(from, to + 1).map((n) => n.node).filter((n) => this.#canSelect(n));
437
+ this._selectedNodes = new Set(rangeNodes);
438
+ this.selectedNode = node;
439
+ this.#dispatchSelect(prev);
440
+ }
441
+ return;
442
+ }
443
+ this.#toggleExpand(node, depth);
444
+ }
445
+ #toggleSelection(node, _depth) {
446
+ if (!this.#canSelect(node)) return;
447
+ const prev = new Set(this._selectedNodes);
448
+ if (this._selectedNodes.has(node)) {
449
+ this._selectedNodes.delete(node);
450
+ if (this.selectedNode === node) this.selectedNode = [...this._selectedNodes].at(-1);
451
+ } else {
452
+ this._selectedNodes.add(node);
453
+ this.selectedNode = node;
454
+ this.#anchor = {
455
+ node,
456
+ flatIndex: this.#getFlatIndexOf(node)
457
+ };
458
+ }
459
+ this.#dispatchSelect(prev);
460
+ }
461
+ _handleCheckboxClick(event, node, _depth) {
462
+ event.stopPropagation();
463
+ const prev = new Set(this._selectedNodes);
464
+ const allSelected = this.#allDescendantsSelected(node);
465
+ this.#applyCheckedToggle(node, !allSelected);
466
+ if (this.selectedNode === void 0 || !this._selectedNodes.has(this.selectedNode)) this.selectedNode = [...this._selectedNodes].at(-1);
467
+ this.#dispatchSelect(prev);
468
+ this.requestUpdate();
469
+ }
470
+ #toggleExpand(node, depth) {
471
+ if (this._expandedKeys.has(node.item.key)) {
472
+ this._expandedKeys.delete(node.item.key);
473
+ this.dispatchEvent(new CustomEvent(`collapse`, {
474
+ detail: {
475
+ node,
476
+ depth
477
+ },
478
+ bubbles: true,
479
+ composed: true
480
+ }));
481
+ } else if (node.children === void 0) {
482
+ if (node.item.isLeaf !== false) return;
483
+ if (this.model ?? (this.loadChildren ? this : void 0)) {
484
+ this.#pendingNode = node;
485
+ const controller = new AbortController();
486
+ this.#pendingController = controller;
487
+ this.#invokeLoadChildren(node, depth, controller).catch(() => {});
488
+ }
489
+ } else {
490
+ this.#applyExclusivity(node.item.key);
491
+ this._expandedKeys.add(node.item.key);
492
+ this.dispatchEvent(new CustomEvent(`expand`, {
493
+ detail: {
494
+ node,
495
+ depth
496
+ },
497
+ bubbles: true,
498
+ composed: true
499
+ }));
500
+ }
501
+ }
502
+ #allDescendantsSelected(node) {
503
+ if (!this._selectedNodes.has(node)) return false;
504
+ if (!node.children?.length) return true;
505
+ return node.children.every((child) => this.#allDescendantsSelected(child));
506
+ }
507
+ #applyCheckedToggle(node, selected) {
508
+ if (this.#canSelect(node)) if (selected) this._selectedNodes.add(node);
509
+ else this._selectedNodes.delete(node);
510
+ if (node.children) for (const child of node.children) this.#applyCheckedToggle(child, selected);
511
+ }
512
+ #getFlatIndexOf(node) {
513
+ return this.#getAllNodes().findIndex((n) => n.node === node);
514
+ }
515
+ #extendRangeSelection(nodes, fromIdx, toIdx) {
516
+ if (!this.#anchor) {
517
+ const anchorNode = nodes[fromIdx]?.node;
518
+ if (anchorNode) this.#anchor = {
519
+ node: anchorNode,
520
+ flatIndex: fromIdx
521
+ };
522
+ }
523
+ if (!this.#anchor) return;
524
+ const anchorIdx = nodes.findIndex((n) => n.node === this.#anchor.node);
525
+ if (anchorIdx < 0) return;
526
+ const from = Math.min(anchorIdx, toIdx);
527
+ const to = Math.max(anchorIdx, toIdx);
528
+ if (this.interactionMode === `vscode`) {
529
+ if (!this.#anchor.selectionSnapshot) this.#anchor = {
530
+ ...this.#anchor,
531
+ selectionSnapshot: new Set(this._selectedNodes)
532
+ };
533
+ const snap = this.#anchor.selectionSnapshot;
534
+ const rangeNodes = nodes.slice(from, to + 1).map((n) => n.node).filter((n) => this.#canSelect(n));
535
+ const next = new Set(snap);
536
+ for (const n of rangeNodes) if (snap.has(n)) next.delete(n);
537
+ else next.add(n);
538
+ const prev = new Set(this._selectedNodes);
539
+ this._selectedNodes = next;
540
+ this.selectedNode = nodes[toIdx]?.node ?? this.selectedNode;
541
+ this.#dispatchSelect(prev);
542
+ } else {
543
+ const prev = new Set(this._selectedNodes);
544
+ const rangeNodes = nodes.slice(from, to + 1).map((n) => n.node).filter((n) => this.#canSelect(n));
545
+ this._selectedNodes = new Set(rangeNodes);
546
+ this.selectedNode = nodes[toIdx]?.node ?? this.selectedNode;
547
+ this.#dispatchSelect(prev);
548
+ }
549
+ }
550
+ async #invokeLoadChildren(node, depth, controller) {
551
+ try {
552
+ let children;
553
+ if (this.model) children = await this.model.getChildren(node, depth, controller.signal);
554
+ else {
555
+ const result = this.loadChildren(node, depth, controller.signal);
556
+ children = result instanceof Promise ? await result : result;
557
+ }
558
+ if (controller.signal.aborted) return;
559
+ node.children = children;
560
+ this.#applyExclusivity(node.item.key);
561
+ this._expandedKeys.add(node.item.key);
562
+ this.#pendingNode = void 0;
563
+ this.#pendingController = void 0;
564
+ this.dispatchEvent(new CustomEvent(`expand`, {
565
+ detail: {
566
+ node,
567
+ depth
568
+ },
569
+ bubbles: true,
570
+ composed: true
571
+ }));
572
+ this.requestUpdate();
573
+ } catch (err) {
574
+ if (controller.signal.aborted) return;
575
+ node.children = [];
576
+ this.#pendingNode = void 0;
577
+ this.#pendingController = void 0;
578
+ this.requestUpdate();
579
+ }
580
+ }
581
+ _isSelected(node) {
582
+ return this._selectedNodes.has(node);
583
+ }
584
+ _isFocused(node) {
585
+ return this.tickled.isTickled(node.item.key);
586
+ }
587
+ _isExpanded(node) {
588
+ if (this._expandedKeys.has(node.item.key)) return true;
589
+ if (this.filterPredicate && node.children) return node.children.some((child) => this.#matchesFilter(child, 0));
590
+ return false;
591
+ }
592
+ #matchesFilter(node, depth) {
593
+ if (!this.filterPredicate) return true;
594
+ if (this.filterPredicate(node, depth)) return true;
595
+ return node.children?.some((child) => this.#matchesFilter(child, depth + 1)) ?? false;
596
+ }
597
+ get _pendingNode() {
598
+ return this.#pendingNode;
599
+ }
600
+ #scrollKeyIntoView(key) {
601
+ requestAnimationFrame(() => {
602
+ const el = this.shadowRoot?.querySelector(`[data-key="${CSS.escape(key)}"]`);
603
+ if (!el) return;
604
+ const elRect = el.getBoundingClientRect();
605
+ const hostRect = this.getBoundingClientRect();
606
+ const relativeTop = elRect.top - hostRect.top;
607
+ const relativeBottom = relativeTop + elRect.height;
608
+ if (relativeTop < 0) this.scrollTop += relativeTop;
609
+ else if (relativeBottom > this.clientHeight) this.scrollTop += relativeBottom - this.clientHeight;
610
+ });
611
+ }
612
+ #abortPending() {
613
+ if (this.#pendingController) {
614
+ this.#pendingController.abort();
615
+ this.#pendingController = void 0;
616
+ this.#pendingNode = void 0;
617
+ }
618
+ }
619
+ /** Apply exclusivity rules when a node is about to be expanded. */
620
+ #applyExclusivity(expandKey) {
621
+ if (this.exclusivity === `none`) return;
622
+ if (this.exclusivity === `depth`) this.#collapseSiblingsAtDepth(expandKey);
623
+ else if (this.exclusivity === `global`) {
624
+ const ancestorKeys = this.#findAncestorKeys(expandKey);
625
+ for (const key of [...this._expandedKeys]) if (!ancestorKeys.has(key) && key !== expandKey) this._expandedKeys.delete(key);
626
+ }
627
+ }
628
+ #collapseSiblingsAtDepth(expandKey) {
629
+ if (!this.root) return;
630
+ const parent = this.#findParent(this.root, expandKey);
631
+ if (!parent?.children) return;
632
+ for (const child of parent.children) if (child.item.key !== expandKey && this._expandedKeys.has(child.item.key)) this._expandedKeys.delete(child.item.key);
633
+ }
634
+ #findAncestorKeys(targetKey) {
635
+ const keys = /* @__PURE__ */ new Set();
636
+ const walk = (node) => {
637
+ if (node.item.key === targetKey) return true;
638
+ if (!node.children) return false;
639
+ for (const child of node.children) if (walk(child)) {
640
+ keys.add(node.item.key);
641
+ return true;
642
+ }
643
+ return false;
644
+ };
645
+ if (this.root) walk(this.root);
646
+ return keys;
647
+ }
648
+ #findParent(node, targetKey, parent = void 0) {
649
+ if (!node.children) return void 0;
650
+ for (const child of node.children) {
651
+ if (child.item.key === targetKey) return parent ?? node;
652
+ const found = this.#findParent(child, targetKey, child);
653
+ if (found) return found;
654
+ }
655
+ }
656
+ #getVisibleNodes(node, depth) {
657
+ const result = [];
658
+ const visit = (n, d) => {
659
+ result.push({
660
+ node: n,
661
+ depth: d
662
+ });
663
+ if (this._expandedKeys.has(n.item.key) && n.children) for (const child of n.children) visit(child, d + 1);
664
+ };
665
+ if (node.children) for (const child of node.children) visit(child, depth + 1);
666
+ return result;
667
+ }
668
+ #getAllNodes() {
669
+ if (!this.root) return [];
670
+ return this.#getVisibleNodes(this.root, 0);
671
+ }
672
+ #getFocusedIndex() {
673
+ const nodes = this.#getAllNodes();
674
+ const tickledKey = this.tickled.tickledId;
675
+ if (!tickledKey) return 0;
676
+ return nodes.findIndex((n) => n.node.item.key === tickledKey);
677
+ }
678
+ #setFocusedIndex(index) {
679
+ const nodes = this.#getAllNodes();
680
+ if (index < 0 || index >= nodes.length) return;
681
+ const { node, depth } = nodes[index];
682
+ this.tickled.setTickled(node.item.key);
683
+ this._focusedDepth = depth;
684
+ this.dispatchEvent(new CustomEvent(`tickle`, {
685
+ detail: {
686
+ node,
687
+ depth
688
+ },
689
+ bubbles: true,
690
+ composed: true
691
+ }));
692
+ this.requestUpdate();
693
+ this.updateComplete.then(() => this.#scrollTickledIntoView());
694
+ }
695
+ #scrollTickledIntoView() {
696
+ const tickledKey = this.tickled.tickledId;
697
+ if (tickledKey === void 0) return;
698
+ (this.shadowRoot?.querySelector(`[data-key="${tickledKey}"]`))?.scrollIntoView({
699
+ block: `nearest`,
700
+ behavior: `smooth`
701
+ });
702
+ }
703
+ #onKeyDown;
704
+ renderLoadingIndicator() {
705
+ return html`<span class="loading-spinner"></span>`;
706
+ }
707
+ render() {
708
+ const nodes = this.#getAllNodes();
709
+ return html`
710
+ <div
711
+ id="container"
712
+ data-input-mode=${this.tickled.inputMode}
713
+ @keydown=${this.#onKeyDown}
714
+ @dblclick=${this.#onDblClick}
715
+ @pointerleave=${(e) => this.tickled.handlePointerLeave(e)}
716
+ tabindex="0"
717
+ >
718
+ ${this.root ? this.#renderNodes(nodes) : nothing}
719
+ </div>
720
+ `;
721
+ }
722
+ #onDblClick;
723
+ #renderNodes(nodes) {
724
+ if (nodes.length === 0 && this.#pendingNode) return html`
725
+ <div class="tree-loading">
726
+ ${this.renderLoadingIndicator()}
727
+ </div>
728
+ `;
729
+ return html`
730
+ <ul class="tree-list" role="tree">
731
+ ${nodes.map(({ node, depth }) => this._renderNode(node, depth))}
732
+ </ul>
733
+ `;
734
+ }
735
+ _renderNode(node, depth) {
736
+ const selected = this._isSelected(node);
737
+ const tickled = this._isFocused(node);
738
+ const isBranch = node.item.isLeaf !== true;
739
+ const isExpanded = this._isExpanded(node);
740
+ const isPending = this.#pendingNode === node;
741
+ const isFiltered = this.filterPredicate !== void 0 && !this.#matchesFilter(node, depth);
742
+ return html`
743
+ <li
744
+ class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${isFiltered ? `filtered` : ``}"
745
+ role="treeitem"
746
+ aria-selected=${selected}
747
+ aria-expanded=${isBranch ? isExpanded : nothing}
748
+ aria-level=${depth + 1}
749
+ data-key=${node.item.key}
750
+ style=${`--depth: ${depth}`}
751
+ @click=${(e) => this._handleItemClick(node, depth, e)}
752
+ @pointerenter=${() => {
753
+ this.tickled.handlePointerEnter(node.item.key);
754
+ this.dispatchEvent(new CustomEvent(`tickle`, {
755
+ detail: {
756
+ node,
757
+ depth
758
+ },
759
+ bubbles: true,
760
+ composed: true
761
+ }));
762
+ }}
763
+ >
764
+ ${this.renderNodeContent(node, depth)}
765
+ ${isPending ? this.renderLoadingIndicator() : nothing}
766
+ </li>
767
+ `;
768
+ }
769
+ static {
770
+ this.styles = [
771
+ tickledStyles,
772
+ tickledItemStyles,
773
+ css`
774
+ :host {
775
+ --tree-bg: var(--surface-3);
776
+ --tree-text: var(--surface-3-text);
777
+ --tree-muted-text: var(--surface-muted-text);
778
+ --tree-indent: 1ch;
779
+ --tree-item-height: 28px;
780
+ --tree-caret-size: 10px;
781
+ --tree-radius: var(--radius-s);
782
+
783
+ display: block;
784
+ overflow: auto;
785
+ box-sizing: border-box;
786
+ background: var(--tree-bg);
787
+ color: var(--tree-text);
788
+ }
789
+
790
+ #container {
791
+ outline: none;
792
+ min-height: 100%;
793
+ }
794
+
795
+ .tree-list {
796
+ list-style: none;
797
+ margin: 0;
798
+ padding: var(--space-xs);
799
+ }
800
+
801
+ .item {
802
+ display: flex;
803
+ align-items: center;
804
+ height: var(--tree-item-height);
805
+ padding-left: calc(var(--tree-indent) * var(--depth, 0));
806
+ cursor: pointer;
807
+ user-select: none;
808
+ font-family: var(--font-family);
809
+ font-size: var(--text-m);
810
+ border-radius: var(--tree-radius);
811
+ transition: background var(--transition, 0.15s ease-out);
812
+ margin: 1px 0;
813
+ }
814
+
815
+ .tree-caret {
816
+ display: inline-flex;
817
+ align-items: center;
818
+ justify-content: center;
819
+ width: var(--tree-caret-size);
820
+ height: var(--tree-caret-size);
821
+ flex-shrink: 0;
822
+ margin-right: var(--space-xs);
823
+ }
824
+
825
+ .caret-icon {
826
+ width: var(--tree-caret-size);
827
+ height: var(--tree-caret-size);
828
+ color: var(--tree-muted-text);
829
+ transition: transform var(--transition, 0.15s ease-out);
830
+ }
831
+
832
+ .item.selected .caret-icon {
833
+ color: var(--accent-text);
834
+ }
835
+
836
+ .tree-label {
837
+ flex: 1;
838
+ overflow: hidden;
839
+ text-overflow: ellipsis;
840
+ white-space: nowrap;
841
+ }
842
+
843
+ .tree-loading {
844
+ display: flex;
845
+ align-items: center;
846
+ justify-content: center;
847
+ padding: var(--space-m);
848
+ }
849
+
850
+ .loading-spinner {
851
+ width: 16px;
852
+ height: 16px;
853
+ border: 2px solid var(--surface-5);
854
+ border-top-color: var(--accent);
855
+ border-radius: 50%;
856
+ animation: tree-spin 0.7s linear infinite;
857
+ }
858
+
859
+ @keyframes tree-spin {
860
+ to { transform: rotate(360deg); }
861
+ }
862
+
863
+ .item.filtered {
864
+ display: none;
865
+ }
866
+
867
+ ::highlight(incr-search-highlight) {
868
+ background: var(--search-highlight-bg, var(--accent));
869
+ color: var(--search-highlight-color, var(--accent-text, #fff));
870
+ border-radius: 2px;
871
+ padding: 0 1px;
872
+ }
873
+ `
874
+ ];
875
+ }
876
+ };
877
+ __decorate([property({ type: Object })], TreeBaseElement.prototype, "root", void 0);
878
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "model", void 0);
879
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "loadChildren", void 0);
880
+ __decorate([property({
881
+ reflect: true,
882
+ attribute: `exclusivity`
883
+ })], TreeBaseElement.prototype, "exclusivity", void 0);
884
+ __decorate([property({
885
+ reflect: true,
886
+ attribute: `selection-filter`
887
+ })], TreeBaseElement.prototype, "selectionFilter", void 0);
888
+ __decorate([property({
889
+ type: String,
890
+ attribute: `interaction-mode`
891
+ })], TreeBaseElement.prototype, "interactionMode", void 0);
892
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "selectedNode", void 0);
893
+ __decorate([state()], TreeBaseElement.prototype, "_selectedNodes", void 0);
894
+ __decorate([property({ attribute: false })], TreeBaseElement.prototype, "filterPredicate", void 0);
895
+ __decorate([property({
896
+ type: Boolean,
897
+ attribute: `disable-keyboard-navigation`
898
+ })], TreeBaseElement.prototype, "disableKeyboardNavigation", void 0);
899
+ __decorate([state()], TreeBaseElement.prototype, "_expandedKeys", void 0);
900
+ __decorate([state()], TreeBaseElement.prototype, "_focusedDepth", void 0);
901
+ TreeBaseElement = __decorate([customElement(`ixfx-tree-base`)], TreeBaseElement);
902
+ //#endregion
903
+ //#region src/tree/tree-controller.ts
904
+ var TreeController = class {
905
+ #root;
906
+ #element;
907
+ #options;
908
+ #nodeToTree = /* @__PURE__ */ new WeakMap();
909
+ #identityToTreeNode = /* @__PURE__ */ new Map();
910
+ #connectSignal;
911
+ constructor(root, element, options) {
912
+ this.#root = root;
913
+ this.#element = element;
914
+ this.#options = options;
915
+ }
916
+ connect() {
917
+ this.disconnect();
918
+ this.#connectSignal = new AbortController();
919
+ const { signal } = this.#connectSignal;
920
+ const rootValue = this.#root.getValue();
921
+ const rootItem = this.#options.valueToItem(rootValue, this.#root);
922
+ const rootChildren = this.#childrenOf(this.#root);
923
+ this.#element.root = {
924
+ item: rootItem,
925
+ children: rootChildren
926
+ };
927
+ this.#element.addEventListener(`expand`, (e) => {
928
+ const { node } = e.detail;
929
+ const treeNode = this.#nodeToTree.get(node);
930
+ if (!treeNode) return;
931
+ node.children = this.#childrenOf(treeNode);
932
+ this.#element.requestUpdate();
933
+ }, { signal });
934
+ this.#element.addEventListener(`collapse`, (_e) => {}, { signal });
935
+ }
936
+ disconnect() {
937
+ this.#connectSignal?.abort();
938
+ this.#connectSignal = void 0;
939
+ }
940
+ navigateToValue(predicate) {
941
+ const treePath = this.#findTreePath(this.#root, predicate);
942
+ if (!treePath) return false;
943
+ const keys = [];
944
+ for (let i = 1; i < treePath.length; i++) {
945
+ const treeNode = treePath[i];
946
+ const key = this.#options.valueToItem(treeNode.getValue(), treeNode).key;
947
+ keys.push(key);
948
+ if (i < treePath.length - 1) {
949
+ const currentChildren = i === 1 ? this.#element.root?.children ?? [] : this.#findInChildren(this.#element.root?.children ?? [], keys);
950
+ const treeNodeMap = this.#nodeToTree;
951
+ const node = currentChildren.find((n) => n.item.key === key);
952
+ if (!node) return false;
953
+ if (node.children === void 0) {
954
+ const mapped = treeNodeMap.get(node);
955
+ if (mapped) node.children = this.#childrenOf(mapped);
956
+ }
957
+ }
958
+ }
959
+ return this.#element.navigateTo(keys);
960
+ }
961
+ #findInChildren(children, keys) {
962
+ if (keys.length === 0) return children;
963
+ const key = keys[0];
964
+ return children.find((n) => n.item.key === key)?.children ?? [];
965
+ }
966
+ #childrenOf(treeNode) {
967
+ return Array.from(treeNode.children(), (child) => this.#toTreeNode(child));
968
+ }
969
+ #toTreeNode(treeNode) {
970
+ const value = treeNode.getValue();
971
+ const childList = [...treeNode.children()];
972
+ const userItem = this.#options.valueToItem(value, treeNode);
973
+ const isLeaf = userItem.isLeaf ?? childList.length === 0;
974
+ const treeNodeMap = { item: {
975
+ ...userItem,
976
+ isLeaf
977
+ } };
978
+ this.#nodeToTree.set(treeNodeMap, treeNode);
979
+ this.#identityToTreeNode.set(treeNode.getIdentity(), treeNode);
980
+ return treeNodeMap;
981
+ }
982
+ #findTreePath(node, predicate, ancestors = []) {
983
+ const path = [...ancestors, node];
984
+ if (predicate(node.getValue())) return path;
985
+ for (const child of node.children()) {
986
+ const result = this.#findTreePath(child, predicate, path);
987
+ if (result) return result;
988
+ }
989
+ }
990
+ };
991
+ //#endregion
992
+ //#region src/tree/tree-list.ts
993
+ function computeCheckboxState(node, selectedNodes) {
994
+ if (node.item.isLeaf || !node.children?.length) return selectedNodes.has(node) ? `checked` : `unchecked`;
995
+ const childStates = node.children.map((c) => computeCheckboxState(c, selectedNodes));
996
+ const allChecked = childStates.every((s) => s === `checked`);
997
+ const allUnchecked = childStates.every((s) => s === `unchecked`);
998
+ if (allChecked) return `checked`;
999
+ if (allUnchecked) return `unchecked`;
1000
+ return `indeterminate`;
1001
+ }
1002
+ let TreeListElement = class TreeListElement extends TreeBaseElement {
1003
+ #depthsWithIcons = /* @__PURE__ */ new Set();
1004
+ render() {
1005
+ this.#detectIcons();
1006
+ return super.render();
1007
+ }
1008
+ #detectIcons() {
1009
+ this.#depthsWithIcons.clear();
1010
+ const visit = (node, depth) => {
1011
+ if (node.item.icon) this.#depthsWithIcons.add(depth);
1012
+ if (node.children) for (const child of node.children) visit(child, depth + 1);
1013
+ };
1014
+ if (this.root) visit(this.root, 0);
1015
+ }
1016
+ renderNodeContent(node, depth) {
1017
+ const isBranch = node.item.isLeaf !== true;
1018
+ const isExpanded = this._isExpanded(node);
1019
+ const icon = node.item.icon;
1020
+ const tooltip = node.item.tooltip;
1021
+ const caretAndLabel = html`
1022
+ <span class="tree-caret">
1023
+ ${isBranch ? html`<ixfx-icon class="caret-icon" name="chevron-down" .rotate=${isExpanded ? nothing : `-90deg`}></ixfx-icon>` : ``}
1024
+ </span>
1025
+ ${icon ? html`<ixfx-icon class="tree-icon" name="${icon}"></ixfx-icon>` : nothing}
1026
+ <span class="tree-label">
1027
+ ${node.item.label}
1028
+ ${tooltip ? html`<ixfx-tooltip class="tree-tooltip" anchor="--tooltip-tree-${node.item.key}" .value=${tooltip}></ixfx-tooltip>` : nothing}
1029
+ </span>
1030
+ `;
1031
+ if (this.interactionMode === `checked`) {
1032
+ const state = computeCheckboxState(node, this._selectedNodes);
1033
+ return html`
1034
+ <input
1035
+ type="checkbox"
1036
+ class="tree-checkbox"
1037
+ .checked=${state === `checked`}
1038
+ .indeterminate=${state === `indeterminate`}
1039
+ aria-label="Select ${node.item.label}"
1040
+ @click=${(e) => this._handleCheckboxClick(e, node, depth)}
1041
+ />${caretAndLabel}`;
1042
+ }
1043
+ return caretAndLabel;
1044
+ }
1045
+ _renderNode(node, depth) {
1046
+ const selected = this._isSelected(node);
1047
+ const tickled = this._isFocused(node);
1048
+ const isBranch = node.item.isLeaf !== true;
1049
+ const isExpanded = this._isExpanded(node);
1050
+ const isPending = this._pendingNode === node;
1051
+ const hasIconAtDepth = this.#depthsWithIcons.has(depth);
1052
+ return html`
1053
+ <li
1054
+ class="item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${isBranch ? `branch` : `leaf`} ${hasIconAtDepth ? `has-icon-at-depth` : ``}"
1055
+ data-key=${node.item.key}
1056
+ role="treeitem"
1057
+ aria-selected=${selected}
1058
+ aria-expanded=${isBranch ? isExpanded : nothing}
1059
+ aria-level=${depth + 1}
1060
+ style=${`--depth: ${depth}`}
1061
+ @click=${(e) => this._handleItemClick(node, depth, e)}
1062
+ @pointerenter=${() => this.tickled.handlePointerEnter(node.item.key)}
1063
+ >
1064
+ ${this.renderNodeContent(node, depth)}
1065
+ ${isPending ? this.renderLoadingIndicator() : nothing}
1066
+ </li>
1067
+ `;
1068
+ }
1069
+ static {
1070
+ this.styles = [...TreeBaseElement.styles, css`
1071
+ .tree-caret {
1072
+ display: inline-flex;
1073
+ align-items: center;
1074
+ justify-content: center;
1075
+ width: var(--tree-caret-size);
1076
+ height: var(--tree-caret-size);
1077
+ flex-shrink: 0;
1078
+ margin-right: var(--space-xs);
1079
+ }
1080
+
1081
+ .caret-icon {
1082
+ width: var(--tree-caret-size);
1083
+ height: var(--tree-caret-size);
1084
+ color: var(--tree-muted-text);
1085
+ transition: transform var(--transition, 0.15s ease-out);
1086
+ }
1087
+
1088
+ .item.selected .caret-icon {
1089
+ color: var(--accent-text);
1090
+ }
1091
+
1092
+ .tree-icon {
1093
+ flex-shrink: 0;
1094
+ width: var(--icon-size, 1em);
1095
+ height: var(--icon-size, 1em);
1096
+ margin-right: var(--icon-spacing, 0.3em);
1097
+ }
1098
+
1099
+ .item:not(.has-icon-at-depth) .tree-icon {
1100
+ display: none;
1101
+ }
1102
+
1103
+ .item.has-icon-at-depth .tree-label {
1104
+ margin-left: var(--icon-size, 1em);
1105
+ }
1106
+
1107
+ .tree-tooltip {
1108
+ position: absolute;
1109
+ anchor-name: --tooltip-tree;
1110
+ }
1111
+
1112
+ .tree-checkbox {
1113
+ flex-shrink: 0;
1114
+ margin-right: var(--space-xs, 3px);
1115
+ cursor: pointer;
1116
+ accent-color: var(--accent);
1117
+ pointer-events: auto;
1118
+ }
1119
+
1120
+ /* In checked mode the checkbox communicates selection state —
1121
+ suppress the row highlight so it doesn't double-signal */
1122
+ :host([interaction-mode="checked"]) .item.selected {
1123
+ background: transparent;
1124
+ color: inherit;
1125
+ outline: none;
1126
+ }
1127
+
1128
+
1129
+
1130
+ .tree-label {
1131
+ flex: 1;
1132
+ overflow: hidden;
1133
+ text-overflow: ellipsis;
1134
+ white-space: nowrap;
1135
+ position: relative;
1136
+ }
1137
+ `];
1138
+ }
1139
+ };
1140
+ TreeListElement = __decorate([customElement(`ixfx-tree-list`)], TreeListElement);
1141
+ //#endregion
1142
+ export { TreeBaseElement, TreeController, TreeDataModel, TreeListElement };
1143
+
1144
+ //# sourceMappingURL=tree.js.map