@ixfx/components 0.5.20 → 0.6.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 (285) hide show
  1. package/bundle/index.d.ts +586 -261
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +8306 -6624
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.d.ts +12 -7
  6. package/dist/ac-text.d.ts.map +1 -1
  7. package/dist/ac-text.js +3 -4
  8. package/dist/ac-text.js.map +1 -1
  9. package/dist/ac-token-DVbmHmky.d.ts +107 -0
  10. package/dist/ac-token-DVbmHmky.d.ts.map +1 -0
  11. package/dist/ac-token.d.ts +2 -0
  12. package/dist/ac-token.js +646 -0
  13. package/dist/ac-token.js.map +1 -0
  14. package/dist/{button-pcDaCemT.js → button-Bn0BuLpO.js} +3 -4
  15. package/dist/{button-pcDaCemT.js.map → button-Bn0BuLpO.js.map} +1 -1
  16. package/dist/button.d.ts +2 -3
  17. package/dist/button.d.ts.map +1 -1
  18. package/dist/button.js +1 -1
  19. package/dist/checkbox.d.ts +0 -1
  20. package/dist/checkbox.d.ts.map +1 -1
  21. package/dist/checkbox.js +1 -1
  22. package/dist/checkbox.js.map +1 -1
  23. package/dist/{colour-picker-D43Q96r0.js → colour-picker-0stHB90J.js} +11 -14
  24. package/dist/{colour-picker-D43Q96r0.js.map → colour-picker-0stHB90J.js.map} +1 -1
  25. package/dist/colour-picker.d.ts +1 -1
  26. package/dist/colour-picker.js +1 -1
  27. package/dist/crumbs.d.ts +4 -2
  28. package/dist/crumbs.d.ts.map +1 -1
  29. package/dist/crumbs.js +1152 -1
  30. package/dist/crumbs.js.map +1 -0
  31. package/dist/data-controller-DOlmqNKb.js +171 -0
  32. package/dist/data-controller-DOlmqNKb.js.map +1 -0
  33. package/dist/data-display.d.ts +1 -2
  34. package/dist/data-display.d.ts.map +1 -1
  35. package/dist/data-display.js +24 -22
  36. package/dist/data-display.js.map +1 -1
  37. package/dist/data-grid-BeAkORz2.d.ts +215 -0
  38. package/dist/data-grid-BeAkORz2.d.ts.map +1 -0
  39. package/dist/data-grid.d.ts +2 -0
  40. package/dist/data-grid.js +1225 -0
  41. package/dist/data-grid.js.map +1 -0
  42. package/dist/data-provider-BAn_-oR1.d.ts +61 -0
  43. package/dist/data-provider-BAn_-oR1.d.ts.map +1 -0
  44. package/dist/{decorate-DdjvVHS-.js → decorate-VRK8rslU.js} +2 -2
  45. package/dist/{decorate-DdjvVHS-.js.map → decorate-VRK8rslU.js.map} +1 -1
  46. package/dist/{defaults-BkWCPJFL.js → defaults-BCiDhZbF.js} +20 -3
  47. package/dist/defaults-BCiDhZbF.js.map +1 -0
  48. package/dist/drag-ghost-DtSDvjri.js +85 -0
  49. package/dist/drag-ghost-DtSDvjri.js.map +1 -0
  50. package/dist/editable-label.d.ts +1 -2
  51. package/dist/editable-label.d.ts.map +1 -1
  52. package/dist/editable-label.js +1 -1
  53. package/dist/editable-label.js.map +1 -1
  54. package/dist/{incr-search-CeERfijK.js → element-search-Y8SRfPGM.js} +7 -135
  55. package/dist/element-search-Y8SRfPGM.js.map +1 -0
  56. package/dist/form-CsXJX15p.js +64 -0
  57. package/dist/form-CsXJX15p.js.map +1 -0
  58. package/dist/form.d.ts +15 -0
  59. package/dist/form.d.ts.map +1 -0
  60. package/dist/form.js +2 -0
  61. package/dist/grouped-item-lister-CNLMgdIr.d.ts +121 -0
  62. package/dist/grouped-item-lister-CNLMgdIr.d.ts.map +1 -0
  63. package/dist/grouped-item-lister.d.ts +2 -0
  64. package/dist/grouped-item-lister.js +177 -0
  65. package/dist/grouped-item-lister.js.map +1 -0
  66. package/dist/{hex-editor-B-UI4OZu.js → hex-editor-Bk6rxhfg.js} +6 -8
  67. package/dist/{hex-editor-B-UI4OZu.js.map → hex-editor-Bk6rxhfg.js.map} +1 -1
  68. package/dist/{hex-editor-CMIxtNgT.d.ts → hex-editor-Caq8vNQk.d.ts} +2 -3
  69. package/dist/hex-editor-Caq8vNQk.d.ts.map +1 -0
  70. package/dist/hex.d.ts +1 -1
  71. package/dist/hex.js +1 -1
  72. package/dist/highlight-B-eQrhhb.js.map +1 -1
  73. package/dist/{icon-Bd5BiU4b.d.ts → icon-BUwP1CZ1.d.ts} +3 -4
  74. package/dist/icon-BUwP1CZ1.d.ts.map +1 -0
  75. package/dist/{icon-CK14NUcE.js → icons-Bm2ByQ3s.js} +10 -5
  76. package/dist/icons-Bm2ByQ3s.js.map +1 -0
  77. package/dist/icons.d.ts +2 -2
  78. package/dist/icons.js +2 -8
  79. package/dist/incr-search-vCzuao-2.js +137 -0
  80. package/dist/incr-search-vCzuao-2.js.map +1 -0
  81. package/dist/incr-search.d.ts +1 -1
  82. package/dist/incr-search.js +2 -1
  83. package/dist/{index-DrKRD8_a.d.ts → index-BOUaDIW-.d.ts} +4 -4
  84. package/dist/index-BOUaDIW-.d.ts.map +1 -0
  85. package/dist/{index-CkMIKEWp.d.ts → index-BUB5SICW.d.ts} +3 -4
  86. package/dist/index-BUB5SICW.d.ts.map +1 -0
  87. package/dist/{index-tX6fKF8m.d.ts → index-BeuCXtiO.d.ts} +3 -3
  88. package/dist/index-BeuCXtiO.d.ts.map +1 -0
  89. package/dist/{index-BslNaMLo.d.ts → index-Cu5i8zZY.d.ts} +3 -3
  90. package/dist/index-Cu5i8zZY.d.ts.map +1 -0
  91. package/dist/{index-D87kRcEs.d.ts → index-RqMh1i-O.d.ts} +2 -1
  92. package/dist/index-RqMh1i-O.d.ts.map +1 -0
  93. package/dist/{index-Cdmrn0SZ.d.ts → index-ovq_GTdl.d.ts} +11 -11
  94. package/dist/index-ovq_GTdl.d.ts.map +1 -0
  95. package/dist/index.d.ts +139 -599
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +789 -2845
  98. package/dist/index.js.map +1 -1
  99. package/dist/{labelled-input-base-ORb1rGq6.d.ts → labelled-input-base-Dp_9KP0G.d.ts} +2 -3
  100. package/dist/labelled-input-base-Dp_9KP0G.d.ts.map +1 -0
  101. package/dist/{labelled-input-base-9K_w3oLj.js → labelled-input-base-hoKGwyKT.js} +2 -2
  102. package/dist/{labelled-input-base-9K_w3oLj.js.map → labelled-input-base-hoKGwyKT.js.map} +1 -1
  103. package/dist/labelled-radial-input.d.ts +4 -5
  104. package/dist/labelled-radial-input.d.ts.map +1 -1
  105. package/dist/labelled-radial-input.js +2 -3
  106. package/dist/labelled-radial-input.js.map +1 -1
  107. package/dist/labelled-range-input.d.ts +1 -1
  108. package/dist/labelled-range-input.js +2 -2
  109. package/dist/labelled-range-input.js.map +1 -1
  110. package/dist/led.d.ts +1 -2
  111. package/dist/led.d.ts.map +1 -1
  112. package/dist/led.js +1 -1
  113. package/dist/led.js.map +1 -1
  114. package/dist/menu-DIfU70AA.js +1609 -0
  115. package/dist/menu-DIfU70AA.js.map +1 -0
  116. package/dist/{menu-item-AtoP6bD4.d.ts → menu-item-BG5w7fi4.d.ts} +4 -4
  117. package/dist/menu-item-BG5w7fi4.d.ts.map +1 -0
  118. package/dist/menu.d.ts +2 -2
  119. package/dist/menu.js +1 -2
  120. package/dist/miller.d.ts +7 -5
  121. package/dist/miller.d.ts.map +1 -1
  122. package/dist/miller.js +23 -25
  123. package/dist/miller.js.map +1 -1
  124. package/dist/narrowed-text.d.ts +1 -2
  125. package/dist/narrowed-text.d.ts.map +1 -1
  126. package/dist/narrowed-text.js +14 -13
  127. package/dist/narrowed-text.js.map +1 -1
  128. package/dist/notification-pill-DyZydXoZ.d.ts +68 -0
  129. package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -0
  130. package/dist/notifier.d.ts +2 -0
  131. package/dist/notifier.js +146 -0
  132. package/dist/notifier.js.map +1 -0
  133. package/dist/panel.d.ts +1 -1
  134. package/dist/panel.d.ts.map +1 -1
  135. package/dist/panel.js +1 -2
  136. package/dist/panel.js.map +1 -1
  137. package/dist/plots.d.ts +1 -1
  138. package/dist/plots.js +1 -1
  139. package/dist/polar-pad.d.ts +1 -2
  140. package/dist/polar-pad.d.ts.map +1 -1
  141. package/dist/polar-pad.js +2 -2
  142. package/dist/polar-pad.js.map +1 -1
  143. package/dist/{radial-input-Dhu5ZGP5.d.ts → radial-input-BhZeyyP-.d.ts} +2 -3
  144. package/dist/radial-input-BhZeyyP-.d.ts.map +1 -0
  145. package/dist/{radial-input-C4f-2Bya.js → radial-input-Dk8Wydp5.js} +26 -16
  146. package/dist/radial-input-Dk8Wydp5.js.map +1 -0
  147. package/dist/radial-input.d.ts +1 -1
  148. package/dist/radial-input.js +1 -1
  149. package/dist/range-input.d.ts +2 -3
  150. package/dist/range-input.d.ts.map +1 -1
  151. package/dist/range-input.js +2 -2
  152. package/dist/range-input.js.map +1 -1
  153. package/dist/range.d.ts +2 -3
  154. package/dist/range.d.ts.map +1 -1
  155. package/dist/range.js +2 -2
  156. package/dist/range.js.map +1 -1
  157. package/dist/scroll-fade-CAs1EabA.js +70 -0
  158. package/dist/scroll-fade-CAs1EabA.js.map +1 -0
  159. package/dist/select-horiz.d.ts +50 -0
  160. package/dist/select-horiz.d.ts.map +1 -0
  161. package/dist/select-horiz.js +263 -0
  162. package/dist/select-horiz.js.map +1 -0
  163. package/dist/{slider-input-SW7e2Ac2.js → slider-input-CT4gcF-o.js} +30 -27
  164. package/dist/{slider-input-SW7e2Ac2.js.map → slider-input-CT4gcF-o.js.map} +1 -1
  165. package/dist/{slider-input-BEaztT-g.d.ts → slider-input-TidGhvXt.d.ts} +2 -3
  166. package/dist/slider-input-TidGhvXt.d.ts.map +1 -0
  167. package/dist/slider-input.d.ts +1 -1
  168. package/dist/slider-input.js +1 -1
  169. package/dist/snackbar.d.ts +58 -0
  170. package/dist/snackbar.d.ts.map +1 -0
  171. package/dist/snackbar.js +265 -0
  172. package/dist/snackbar.js.map +1 -0
  173. package/dist/snap-container.d.ts +1 -2
  174. package/dist/snap-container.d.ts.map +1 -1
  175. package/dist/snap-container.js +6 -5
  176. package/dist/snap-container.js.map +1 -1
  177. package/dist/split-layout.d.ts +1 -2
  178. package/dist/split-layout.d.ts.map +1 -1
  179. package/dist/split-layout.js +5 -3
  180. package/dist/split-layout.js.map +1 -1
  181. package/dist/swipe.d.ts +1 -2
  182. package/dist/swipe.d.ts.map +1 -1
  183. package/dist/swipe.js +4 -3
  184. package/dist/swipe.js.map +1 -1
  185. package/dist/{tab-list-B8v9KntS.d.ts → tab-list-Beuh2nom.d.ts} +4 -4
  186. package/dist/tab-list-Beuh2nom.d.ts.map +1 -0
  187. package/dist/tabs.d.ts +1 -1
  188. package/dist/tabs.js +533 -1
  189. package/dist/tabs.js.map +1 -0
  190. package/dist/{tickled-controller-JDOR9B8V.d.ts → tickled-controller-BtevrRLV.d.ts} +1 -2
  191. package/dist/tickled-controller-BtevrRLV.d.ts.map +1 -0
  192. package/dist/{tickled-styles-Bgo_Lpw-.js → tickled-controller-DkAU-0qL.js} +2 -88
  193. package/dist/tickled-controller-DkAU-0qL.js.map +1 -0
  194. package/dist/tickled-styles-BrAHEj1q.js +89 -0
  195. package/dist/tickled-styles-BrAHEj1q.js.map +1 -0
  196. package/dist/{timeline-CdSI003L.js → timeline-B2fgDL4U.js} +3 -3
  197. package/dist/{timeline-CdSI003L.js.map → timeline-B2fgDL4U.js.map} +1 -1
  198. package/dist/timeline.d.ts +1 -1
  199. package/dist/timeline.js +1 -1
  200. package/dist/{tooltip-BMTSUPBU.d.ts → tooltip-BlvBUQxx.d.ts} +2 -3
  201. package/dist/tooltip-BlvBUQxx.d.ts.map +1 -0
  202. package/dist/transitory-label.d.ts +30 -0
  203. package/dist/transitory-label.d.ts.map +1 -0
  204. package/dist/transitory-label.js +249 -0
  205. package/dist/transitory-label.js.map +1 -0
  206. package/dist/{tree-CqPhQRHo.js → tree-BO6BXI2b.js} +9 -10
  207. package/dist/{tree-CqPhQRHo.js.map → tree-BO6BXI2b.js.map} +1 -1
  208. package/dist/{tree-component-CDLO1ITF.d.ts → tree-component-BvV4muxx.d.ts} +8 -124
  209. package/dist/tree-component-BvV4muxx.d.ts.map +1 -0
  210. package/dist/tree.d.ts +9 -4
  211. package/dist/tree.d.ts.map +1 -1
  212. package/dist/tree.js +1 -1
  213. package/dist/types-Bc23SI94.d.ts +72 -0
  214. package/dist/types-Bc23SI94.d.ts.map +1 -0
  215. package/dist/types-DlVNJez_.d.ts +41 -0
  216. package/dist/types-DlVNJez_.d.ts.map +1 -0
  217. package/dist/vertical-list.d.ts +122 -0
  218. package/dist/vertical-list.d.ts.map +1 -0
  219. package/dist/vertical-list.js +737 -0
  220. package/dist/vertical-list.js.map +1 -0
  221. package/dist/{wheel-nudge-Dhzi5nHc.js → wheel-nudge-BlSCe_hD.js} +2 -2
  222. package/dist/{wheel-nudge-Dhzi5nHc.js.map → wheel-nudge-BlSCe_hD.js.map} +1 -1
  223. package/dist/{wheel-nudge-C-ra_1os.d.ts → wheel-nudge-DgMTRYlh.d.ts} +1 -2
  224. package/dist/wheel-nudge-DgMTRYlh.d.ts.map +1 -0
  225. package/dist/{xy-axis-CtDDQzGZ.d.ts → xy-axis-5KhVTNoX.d.ts} +8 -6
  226. package/dist/xy-axis-5KhVTNoX.d.ts.map +1 -0
  227. package/dist/{xy-axis-BpA2eJq6.js → xy-axis-B6yMjckW.js} +8 -112
  228. package/dist/xy-axis-B6yMjckW.js.map +1 -0
  229. package/dist/xy-pad.d.ts +1 -2
  230. package/dist/xy-pad.d.ts.map +1 -1
  231. package/dist/xy-pad.js +2 -2
  232. package/dist/xy-pad.js.map +1 -1
  233. package/docs-user/README.md +4 -2
  234. package/docs-user/data-grid.md +248 -0
  235. package/docs-user/index.json +222 -0
  236. package/docs-user/llms.txt +58 -0
  237. package/docs-user/rating.md +102 -0
  238. package/docs-user/toolbar.md +135 -0
  239. package/llms.txt +58 -0
  240. package/package.json +21 -11
  241. package/dist/crumb-navigation-Pif-kupA.js +0 -1221
  242. package/dist/crumb-navigation-Pif-kupA.js.map +0 -1
  243. package/dist/defaults-BkWCPJFL.js.map +0 -1
  244. package/dist/hex-editor-CMIxtNgT.d.ts.map +0 -1
  245. package/dist/icon-Bd5BiU4b.d.ts.map +0 -1
  246. package/dist/icon-CK14NUcE.js.map +0 -1
  247. package/dist/icons.js.map +0 -1
  248. package/dist/incr-search-CeERfijK.js.map +0 -1
  249. package/dist/index-BslNaMLo.d.ts.map +0 -1
  250. package/dist/index-Cdmrn0SZ.d.ts.map +0 -1
  251. package/dist/index-CkMIKEWp.d.ts.map +0 -1
  252. package/dist/index-D87kRcEs.d.ts.map +0 -1
  253. package/dist/index-DrKRD8_a.d.ts.map +0 -1
  254. package/dist/index-tX6fKF8m.d.ts.map +0 -1
  255. package/dist/interaction-_Y2qO3QI.js +0 -1
  256. package/dist/labelled-input-base-ORb1rGq6.d.ts.map +0 -1
  257. package/dist/menu-BiFarLUA.js +0 -826
  258. package/dist/menu-BiFarLUA.js.map +0 -1
  259. package/dist/menu-item-AtoP6bD4.d.ts.map +0 -1
  260. package/dist/menu-item-yHSPV8nS.js +0 -783
  261. package/dist/menu-item-yHSPV8nS.js.map +0 -1
  262. package/dist/radial-input-C4f-2Bya.js.map +0 -1
  263. package/dist/radial-input-Dhu5ZGP5.d.ts.map +0 -1
  264. package/dist/registry-CkW7q09Q.js +0 -21
  265. package/dist/registry-CkW7q09Q.js.map +0 -1
  266. package/dist/selecthorizontal.d.ts +0 -16
  267. package/dist/selecthorizontal.d.ts.map +0 -1
  268. package/dist/selecthorizontal.js +0 -43
  269. package/dist/selecthorizontal.js.map +0 -1
  270. package/dist/slider-input-BEaztT-g.d.ts.map +0 -1
  271. package/dist/tab-list-B8v9KntS.d.ts.map +0 -1
  272. package/dist/tab-list-CR0v53KJ.js +0 -615
  273. package/dist/tab-list-CR0v53KJ.js.map +0 -1
  274. package/dist/tickled-controller-JDOR9B8V.d.ts.map +0 -1
  275. package/dist/tickled-styles-Bgo_Lpw-.js.map +0 -1
  276. package/dist/tooltip-BMTSUPBU.d.ts.map +0 -1
  277. package/dist/tree-component-CDLO1ITF.d.ts.map +0 -1
  278. package/dist/types-DSfbtHs3.d.ts +0 -8
  279. package/dist/types-DSfbtHs3.d.ts.map +0 -1
  280. package/dist/wheel-nudge-C-ra_1os.d.ts.map +0 -1
  281. package/dist/xy-axis-BpA2eJq6.js.map +0 -1
  282. package/dist/xy-axis-CtDDQzGZ.d.ts.map +0 -1
  283. package/docs-user/selecthorizontal.md +0 -32
  284. /package/bundle/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
  285. /package/dist/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
@@ -10,8 +10,9 @@ function defaultSelector(item) {
10
10
  return String(item);
11
11
  }
12
12
  function createFuzzySearch(items, options = {}) {
13
+ const selector = options.selector ?? defaultSelector;
13
14
  const fzf = new Fzf(items, {
14
- selector: options.selector ?? defaultSelector,
15
+ selector,
15
16
  ...options.fzf ?? {}
16
17
  });
17
18
  const find = (query) => {
@@ -32,37 +33,6 @@ function createFuzzySearch(items, options = {}) {
32
33
  };
33
34
  }
34
35
  //#endregion
35
- //#region src/incr-search/ac-adapter.ts
36
- /**
37
- * Creates an ixfx-ac-text-compatible `completionProvider` function from a
38
- * static item list. Uses synchronous fuzzy search wrapped in a Promise so it
39
- * fits the async provider signature.
40
- *
41
- * Each returned item has `positions` populated with matched character indices,
42
- * which `ixfx-ac-text` uses to render CSS Custom Highlights on the label.
43
- *
44
- * Empty query returns all items in original order (no positions).
45
- * A cancelled signal causes the returned promise to reject with an AbortError.
46
- */
47
- function createAcCompletionProvider(items, options = {}) {
48
- const fuzzy = createFuzzySearch(items, { selector: options.selector ?? ((item) => item.label) });
49
- return (query, signal) => new Promise((resolve, reject) => {
50
- if (signal.aborted) {
51
- reject(new DOMException(`Aborted`, `AbortError`));
52
- return;
53
- }
54
- const results = fuzzy.find(query).map((r) => ({
55
- ...r.item,
56
- positions: r.positions
57
- }));
58
- if (signal.aborted) {
59
- reject(new DOMException(`Aborted`, `AbortError`));
60
- return;
61
- }
62
- resolve(results);
63
- });
64
- }
65
- //#endregion
66
36
  //#region src/incr-search/incr-search.ts
67
37
  function buildState(results, query) {
68
38
  const positionsByItem = /* @__PURE__ */ new Map();
@@ -131,105 +101,6 @@ function createIncrSearch(options = {}) {
131
101
  };
132
102
  }
133
103
  //#endregion
134
- //#region src/incr-search/controller-tree.ts
135
- const DEFAULT_HIGHLIGHT_KEY = `incr-search-highlight`;
136
- /**
137
- * Incremental-search controller for any component implementing `TreeSearchHost`
138
- * (i.e. `ixfx-tree-list`, `ixfx-miller-list`, `ixfx-crumb-navigation`).
139
- *
140
- * Replaces the previous separate `IncrSearchTreeController` / `IncrSearchMillerController`
141
- * classes — the unified `TreeFilterPredicate` (with depth) means one implementation works
142
- * for all tree-like components.
143
- *
144
- * @example Using with ixfx-tree-list:
145
- * ```typescript
146
- * const ctrl = new IncrSearchTreeController(myTreeList, {
147
- * selector: item => item.node.item.label,
148
- * });
149
- * ctrl.connect();
150
- * ```
151
- *
152
- * @example Using with ixfx-miller-list (different label selector):
153
- * ```typescript
154
- * const ctrl = new IncrSearchTreeController(myMillerList, {
155
- * selector: item => item.node.item.label,
156
- * labelSelector: '.label',
157
- * });
158
- * ctrl.connect();
159
- * ```
160
- */
161
- var IncrSearchTreeController = class {
162
- #el;
163
- #opts;
164
- #search;
165
- #unsub;
166
- #labelMap = /* @__PURE__ */ new Map();
167
- constructor(el, opts) {
168
- this.#el = el;
169
- this.#opts = opts;
170
- this.#search = createIncrSearch({
171
- selector: opts.selector,
172
- fzf: opts.fzf
173
- });
174
- }
175
- connect() {
176
- this.disconnect();
177
- const items = this.#el.getNodes();
178
- for (const item of items) this.#labelMap.set(this.#opts.selector(item), item);
179
- this.#search.setItems(items);
180
- if (this.#opts.input) {
181
- this.#opts.input.addEventListener(`input`, this.#onInput);
182
- this.#opts.input.addEventListener(`keydown`, this.#onKeydown);
183
- }
184
- this.#unsub = this.#search.onChange((state) => this.#onStateChange(state));
185
- }
186
- disconnect() {
187
- if (this.#unsub) {
188
- this.#unsub();
189
- this.#unsub = void 0;
190
- }
191
- if (this.#opts.input) {
192
- this.#opts.input.removeEventListener(`input`, this.#onInput);
193
- this.#opts.input.removeEventListener(`keydown`, this.#onKeydown);
194
- }
195
- this.#el.filterPredicate = void 0;
196
- clearHighlights({ key: this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY });
197
- this.#labelMap.clear();
198
- }
199
- async setQuery(query) {
200
- await this.#search.setQuery(query);
201
- }
202
- async clearQuery() {
203
- await this.#search.clearQuery();
204
- }
205
- #onInput = () => {
206
- const value = this.#opts.input?.value ?? ``;
207
- this.setQuery(value);
208
- };
209
- #onKeydown = (e) => {
210
- if (e.key === `Escape` && this.#opts.input) {
211
- this.#opts.input.value = ``;
212
- this.clearQuery();
213
- }
214
- };
215
- #onStateChange(state) {
216
- const matched = new Set(state.results.map((r) => r.item.node.item.key));
217
- const hasQuery = state.query.trim().length > 0;
218
- this.#el.filterPredicate = hasQuery && matched.size > 0 ? (node, _depth) => matched.has(node.item.key) : void 0;
219
- const key = this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY;
220
- if (!this.#el.shadowRoot) return;
221
- if (!state.query.trim()) {
222
- clearHighlights({ key });
223
- return;
224
- }
225
- const labelSelector = this.#opts.labelSelector ?? `.tree-label`;
226
- applyHighlights(buildHighlightTargets(this.#el.shadowRoot, labelSelector, state.positionsByItem, (labelEl) => {
227
- const text = labelEl.textContent ?? ``;
228
- return this.#labelMap.get(text);
229
- }, (labelEl) => labelEl.textContent ?? ``), { key });
230
- }
231
- };
232
- //#endregion
233
104
  //#region src/incr-search/element-search.ts
234
105
  const defaultGetLabel = (el) => el.textContent?.trim() ?? ``;
235
106
  function defaultGetKey(el) {
@@ -252,10 +123,11 @@ function createElementIncrSearch(opts) {
252
123
  clearHighlights({ key: highlightKey });
253
124
  return;
254
125
  }
255
- applyHighlights(buildHighlightTargets(container, labelSelector ?? itemSelector, state.positionsByItem, (el) => {
126
+ const targets = buildHighlightTargets(container, labelSelector ?? itemSelector, state.positionsByItem, (el) => {
256
127
  const key = getKey(el);
257
128
  return state.results.find((r) => r.item.key === key)?.item;
258
- }, (el) => getLabel(el)), { key: highlightKey });
129
+ }, (el) => getLabel(el));
130
+ applyHighlights(targets, { key: highlightKey });
259
131
  };
260
132
  const unsubscribeState = search.onChange((state) => {
261
133
  updateVisibility(state);
@@ -308,6 +180,6 @@ function createElementIncrSearch(opts) {
308
180
  };
309
181
  }
310
182
  //#endregion
311
- export { createFuzzySearch as a, createAcCompletionProvider as i, IncrSearchTreeController as n, createIncrSearch as r, createElementIncrSearch as t };
183
+ export { createIncrSearch as n, createFuzzySearch as r, createElementIncrSearch as t };
312
184
 
313
- //# sourceMappingURL=incr-search-CeERfijK.js.map
185
+ //# sourceMappingURL=element-search-Y8SRfPGM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-search-Y8SRfPGM.js","names":["FzfAny"],"sources":["../src/incr-search/fuzzy.ts","../src/incr-search/incr-search.ts","../src/incr-search/element-search.ts"],"sourcesContent":["import type { FuzzySearch, FuzzySearchOptions, IncrSearchResult } from './types.js';\nimport { Fzf } from 'fzf';\n\nfunction defaultSelector<T>(item: T): string {\n if (typeof item === `string`)\n return item;\n const r = item as Record<string, unknown>;\n if (typeof r.label === `string`)\n return r.label;\n if (typeof r.key === `string`)\n return r.key;\n if (typeof r.value === `string`)\n return r.value;\n return String(item);\n}\n\nexport function createFuzzySearch<T>(\n items: readonly T[],\n options: FuzzySearchOptions<T> = {},\n): FuzzySearch<T> {\n const selector = options.selector ?? defaultSelector<T>;\n // fzf uses a conditional SyncOptionsTuple<T> rest-parameter overload that TypeScript\n // cannot narrow through a spread when T is generic.\n const FzfAny = Fzf as new (list: readonly T[], opts: any) => { find: (q: string) => Array<{ item: T; positions: ReadonlySet<number>; score: number }> };\n const fzf = new FzfAny(items as readonly T[], { selector, ...(options.fzf ?? {}) });\n\n const find = (query: string): ReadonlyArray<IncrSearchResult<T>> => {\n if (!query.trim()) {\n return items.map(item => ({\n item,\n positions: new Set<number>(),\n score: 0,\n }));\n }\n return fzf.find(query).map(r => ({\n item: r.item,\n positions: r.positions,\n score: r.score,\n }));\n };\n\n return { find, items };\n}\n","import type {\n IncrSearch,\n IncrSearchChangeCallback,\n IncrSearchOptions,\n IncrSearchState,\n} from './types.js';\nimport { createFuzzySearch } from './fuzzy.js';\n\nfunction buildState<T>(results: ReturnType<ReturnType<typeof createFuzzySearch<T>>[`find`]>, query: string): IncrSearchState<T> {\n const positionsByItem = new Map<T, ReadonlySet<number>>();\n for (const r of results) {\n positionsByItem.set(r.item, r.positions);\n }\n return Object.freeze({\n query,\n results: Object.freeze([...results]),\n positionsByItem,\n });\n}\n\nexport function createIncrSearch<T>(options: IncrSearchOptions<T> = {}): IncrSearch<T> {\n let items: readonly T[] = options.items ?? [];\n let query = ``;\n let fuzzy = createFuzzySearch(items, { selector: options.selector, fzf: options.fzf });\n let state: IncrSearchState<T> = buildState(fuzzy.find(query), query);\n let disposed = false;\n\n const observers = new Set<IncrSearchChangeCallback<T>>();\n\n if (options.onChange) {\n observers.add(options.onChange);\n }\n\n const notify = (s: IncrSearchState<T>): void => {\n for (const cb of observers) {\n cb(s);\n }\n };\n\n const run = async (q: string, signal?: AbortSignal): Promise<IncrSearchState<T>> => {\n // Yield to the microtask queue for a consistent async interface\n await Promise.resolve();\n if (signal?.aborted || disposed) {\n return state;\n }\n const results = fuzzy.find(q);\n if (signal?.aborted || disposed) {\n return state;\n }\n state = buildState(results, q);\n notify(state);\n return state;\n };\n\n return {\n get state() {\n return state;\n },\n\n async setItems(newItems: readonly T[], signal?: AbortSignal): Promise<void> {\n items = newItems;\n fuzzy = createFuzzySearch(items, { selector: options.selector, fzf: options.fzf });\n await run(query, signal);\n },\n\n async setQuery(q: string, signal?: AbortSignal): Promise<IncrSearchState<T>> {\n query = q;\n return run(q, signal);\n },\n\n async clearQuery(): Promise<void> {\n query = ``;\n await run(``, undefined);\n },\n\n onChange(cb: IncrSearchChangeCallback<T>): () => void {\n observers.add(cb);\n cb(state);\n return () => {\n observers.delete(cb);\n };\n },\n\n dispose(): void {\n disposed = true;\n observers.clear();\n items = [];\n },\n };\n}\n","import type {\n ElementIncrSearchOptions,\n IncrSearchChangeCallback,\n IncrSearchState,\n} from './types.js';\nimport { applyHighlights, buildHighlightTargets, clearHighlights } from './highlight.js';\nimport { createIncrSearch } from './incr-search.js';\n\ntype ElementItem = { readonly key: string; readonly label: string };\n\nconst defaultGetLabel = (el: Element): string => el.textContent?.trim() ?? ``;\n\nfunction defaultGetKey(el: Element): string {\n return el.getAttribute(`data-key`)\n ?? el.getAttribute(`data-value`)\n ?? el.textContent?.trim()\n ?? ``;\n}\n\nexport type ElementIncrSearch = {\n readonly setQuery: (query: string, signal?: AbortSignal) => Promise<void>;\n readonly clearQuery: () => Promise<void>;\n readonly refresh: () => void;\n readonly onChange: (cb: IncrSearchChangeCallback<ElementItem>) => () => void;\n readonly dispose: () => void;\n};\n\nexport function createElementIncrSearch(opts: ElementIncrSearchOptions): ElementIncrSearch {\n const {\n container,\n itemSelector,\n getLabel = defaultGetLabel,\n getKey = defaultGetKey,\n labelSelector,\n highlightKey,\n } = opts;\n\n // Map from key → element reference for highlight lookup\n let elementByKey = new Map<string, Element>();\n\n const search = createIncrSearch<ElementItem>({\n selector: item => item.label,\n fzf: opts.fzf,\n });\n\n const updateVisibility = (state: IncrSearchState<ElementItem>): void => {\n const matchedKeys = new Set(state.results.map(r => r.item.key));\n const hasQuery = state.query.trim().length > 0;\n\n for (const [key, el] of elementByKey) {\n (el as HTMLElement).hidden = hasQuery ? !matchedKeys.has(key) : false;\n }\n };\n\n const updateHighlights = (state: IncrSearchState<ElementItem>): void => {\n if (!state.query.trim()) {\n clearHighlights({ key: highlightKey });\n return;\n }\n\n const sel = labelSelector ?? itemSelector;\n const targets = buildHighlightTargets(\n container,\n sel,\n state.positionsByItem,\n (el) => {\n const key = getKey(el);\n // Find the ElementItem whose key matches this element's key\n const result = state.results.find(r => r.item.key === key);\n return result?.item;\n },\n el => getLabel(el),\n );\n applyHighlights(targets, { key: highlightKey });\n };\n\n const unsubscribeState = search.onChange((state) => {\n updateVisibility(state);\n updateHighlights(state);\n opts.onChange?.(state);\n });\n\n let refreshPending = false;\n\n const refresh = (): void => {\n if (refreshPending)\n return;\n refreshPending = true;\n void Promise.resolve().then(() => {\n refreshPending = false;\n elementByKey = new Map<string, Element>();\n const els = container.querySelectorAll(itemSelector);\n const items: ElementItem[] = [];\n for (const el of els) {\n const key = getKey(el);\n elementByKey.set(key, el);\n items.push({ key, label: getLabel(el) });\n }\n void search.setItems(items);\n });\n };\n\n const observer = new MutationObserver(() => refresh());\n observer.observe(container, { childList: true, subtree: false });\n\n refresh();\n\n return {\n async setQuery(query, signal) {\n await search.setQuery(query, signal);\n },\n\n async clearQuery() {\n await search.clearQuery();\n },\n\n refresh,\n\n onChange(cb) {\n return search.onChange(cb);\n },\n\n dispose() {\n observer.disconnect();\n unsubscribeState();\n search.dispose();\n clearHighlights({ key: highlightKey });\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
2
+ import { LitElement } from "lit";
3
+ //#region src/form/form-values.ts
4
+ function collectFormValues(root) {
5
+ const values = {};
6
+ root.querySelectorAll(`[name]`).forEach((el) => {
7
+ const name = el.getAttribute(`name`);
8
+ if (!name) return;
9
+ if (el instanceof HTMLInputElement) {
10
+ if (el.type === `radio`) {
11
+ if (el.checked) values[name] = el.value;
12
+ } else if (el.type === `checkbox`) values[name] = el.checked;
13
+ else values[name] = el.value;
14
+ } else if (el instanceof HTMLSelectElement) {
15
+ if (el.multiple) values[name] = Array.from(el.selectedOptions).map((opt) => opt.value);
16
+ else values[name] = el.value;
17
+ } else if (el instanceof HTMLTextAreaElement) values[name] = el.value;
18
+ else if (`value` in el) values[name] = el.value;
19
+ });
20
+ return values;
21
+ }
22
+ function applyFormValues(root, data) {
23
+ root.querySelectorAll(`[name]`).forEach((el) => {
24
+ const name = el.getAttribute(`name`);
25
+ if (!name || !(name in data)) return;
26
+ const value = data[name];
27
+ if (el instanceof HTMLInputElement) {
28
+ if (el.type === `radio`) el.checked = el.value === value;
29
+ else if (el.type === `checkbox`) el.checked = Boolean(value);
30
+ else el.value = String(value ?? ``);
31
+ el.dispatchEvent(new Event(`input`, { bubbles: true }));
32
+ el.dispatchEvent(new Event(`change`, { bubbles: true }));
33
+ } else if (el instanceof HTMLSelectElement) {
34
+ if (el.multiple && Array.isArray(value)) Array.from(el.options).forEach((opt) => {
35
+ opt.selected = value.includes(opt.value);
36
+ });
37
+ else el.value = String(value ?? ``);
38
+ el.dispatchEvent(new Event(`input`, { bubbles: true }));
39
+ el.dispatchEvent(new Event(`change`, { bubbles: true }));
40
+ } else if (el instanceof HTMLTextAreaElement) {
41
+ el.value = String(value ?? ``);
42
+ el.dispatchEvent(new Event(`input`, { bubbles: true }));
43
+ el.dispatchEvent(new Event(`change`, { bubbles: true }));
44
+ } else if (`value` in el) el.value = value;
45
+ });
46
+ }
47
+ //#endregion
48
+ //#region src/form/form.ts
49
+ let FormElement = class FormElement extends LitElement {
50
+ createRenderRoot() {
51
+ return this;
52
+ }
53
+ getValues() {
54
+ return collectFormValues(this);
55
+ }
56
+ setValues(data) {
57
+ applyFormValues(this, data);
58
+ }
59
+ };
60
+ FormElement = __decorate([safeCustomElement(`ixfx-form`)], FormElement);
61
+ //#endregion
62
+ export { FormElement as t };
63
+
64
+ //# sourceMappingURL=form-CsXJX15p.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-CsXJX15p.js","names":[],"sources":["../src/form/form-values.ts","../src/form/form.ts"],"sourcesContent":["export function collectFormValues(root: ParentNode): Record<string, unknown> {\n const values: Record<string, unknown> = {};\n const elements = root.querySelectorAll(`[name]`);\n\n elements.forEach((el) => {\n const name = el.getAttribute(`name`);\n if (!name) {\n return;\n }\n\n if (el instanceof HTMLInputElement) {\n if (el.type === `radio`) {\n if (el.checked) {\n values[name] = el.value;\n }\n } else if (el.type === `checkbox`) {\n values[name] = el.checked;\n } else {\n values[name] = el.value;\n }\n } else if (el instanceof HTMLSelectElement) {\n if (el.multiple) {\n values[name] = Array.from(el.selectedOptions).map(opt => opt.value);\n } else {\n values[name] = el.value;\n }\n } else if (el instanceof HTMLTextAreaElement) {\n values[name] = el.value;\n } else if (`value` in el) {\n values[name] = (el as { value: unknown }).value;\n }\n });\n\n return values;\n}\n\nexport function applyFormValues(root: ParentNode, data: Record<string, unknown>): void {\n const elements = root.querySelectorAll(`[name]`);\n\n elements.forEach((el) => {\n const name = el.getAttribute(`name`);\n if (!name || !(name in data)) {\n return;\n }\n\n const value = data[name];\n\n if (el instanceof HTMLInputElement) {\n if (el.type === `radio`) {\n el.checked = el.value === value;\n } else if (el.type === `checkbox`) {\n el.checked = Boolean(value);\n } else {\n el.value = String(value ?? ``);\n }\n el.dispatchEvent(new Event(`input`, { bubbles: true }));\n el.dispatchEvent(new Event(`change`, { bubbles: true }));\n } else if (el instanceof HTMLSelectElement) {\n if (el.multiple && Array.isArray(value)) {\n Array.from(el.options).forEach((opt) => {\n opt.selected = value.includes(opt.value);\n });\n } else {\n el.value = String(value ?? ``);\n }\n el.dispatchEvent(new Event(`input`, { bubbles: true }));\n el.dispatchEvent(new Event(`change`, { bubbles: true }));\n } else if (el instanceof HTMLTextAreaElement) {\n el.value = String(value ?? ``);\n el.dispatchEvent(new Event(`input`, { bubbles: true }));\n el.dispatchEvent(new Event(`change`, { bubbles: true }));\n } else if (`value` in el) {\n (el as { value: unknown }).value = value;\n }\n });\n}","import { LitElement } from 'lit';\nimport { safeCustomElement } from '../util/custom-elements.js';\nimport { applyFormValues, collectFormValues } from './form-values.js';\nimport './form.css';\n\n@safeCustomElement(`ixfx-form`)\nexport class FormElement extends LitElement {\n protected override createRenderRoot() {\n return this;\n }\n\n getValues(): Record<string, unknown> {\n return collectFormValues(this);\n }\n\n setValues(data: Record<string, unknown>): void {\n applyFormValues(this, data);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-form': FormElement;\n }\n}"],"mappings":";;;AAAA,SAAgB,kBAAkB,MAA2C;CAC3E,MAAM,SAAkC,CAAC;CAGzC,KAFsB,iBAAiB,QAEhC,CAAC,CAAC,SAAS,OAAO;EACvB,MAAM,OAAO,GAAG,aAAa,MAAM;EACnC,IAAI,CAAC,MACH;EAGF,IAAI,cAAc,kBAAkB;GAClC,IAAI,GAAG,SAAS,SACV;QAAA,GAAG,SACL,OAAO,QAAQ,GAAG;GAAA,OAEf,IAAI,GAAG,SAAS,YACrB,OAAO,QAAQ,GAAG;QAElB,OAAO,QAAQ,GAAG;EAEtB,OAAO,IAAI,cAAc,mBAAmB;GAC1C,IAAI,GAAG,UACL,OAAO,QAAQ,MAAM,KAAK,GAAG,eAAe,CAAC,CAAC,KAAI,QAAO,IAAI,KAAK;QAElE,OAAO,QAAQ,GAAG;EAEtB,OAAO,IAAI,cAAc,qBACvB,OAAO,QAAQ,GAAG;OACb,IAAI,WAAW,IACpB,OAAO,QAAS,GAA0B;CAE9C,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,gBAAgB,MAAkB,MAAqC;CAGrF,KAFsB,iBAAiB,QAEhC,CAAC,CAAC,SAAS,OAAO;EACvB,MAAM,OAAO,GAAG,aAAa,MAAM;EACnC,IAAI,CAAC,QAAQ,EAAE,QAAQ,OACrB;EAGF,MAAM,QAAQ,KAAK;EAEnB,IAAI,cAAc,kBAAkB;GAClC,IAAI,GAAG,SAAS,SACd,GAAG,UAAU,GAAG,UAAU;QACrB,IAAI,GAAG,SAAS,YACrB,GAAG,UAAU,QAAQ,KAAK;QAE1B,GAAG,QAAQ,OAAO,SAAS,EAAE;GAE/B,GAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD,GAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACzD,OAAO,IAAI,cAAc,mBAAmB;GAC1C,IAAI,GAAG,YAAY,MAAM,QAAQ,KAAK,GACpC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,SAAS,QAAQ;IACtC,IAAI,WAAW,MAAM,SAAS,IAAI,KAAK;GACzC,CAAC;QAED,GAAG,QAAQ,OAAO,SAAS,EAAE;GAE/B,GAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD,GAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACzD,OAAO,IAAI,cAAc,qBAAqB;GAC5C,GAAG,QAAQ,OAAO,SAAS,EAAE;GAC7B,GAAG,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GACtD,GAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACzD,OAAO,IAAI,WAAW,IACpB,GAA2B,QAAQ;CAEvC,CAAC;AACH;;;ACrEO,IAAM,cAAN,MAAM,oBAAoB,WAAW;CAC1C,mBAAsC;EACpC,OAAO;CACT;CAEA,YAAqC;EACnC,OAAO,kBAAkB,IAAI;CAC/B;CAEA,UAAU,MAAqC;EAC7C,gBAAgB,MAAM,IAAI;CAC5B;AACF;AAbC,cAAA,WAAA,CAAA,kBAAkB,WAAW,CAAA,GAAA,WAAA"}
package/dist/form.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { LitElement } from "lit";
2
+ //#region src/form/form.d.ts
3
+ declare class FormElement extends LitElement {
4
+ protected createRenderRoot(): this;
5
+ getValues(): Record<string, unknown>;
6
+ setValues(data: Record<string, unknown>): void;
7
+ }
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'ixfx-form': FormElement;
11
+ }
12
+ }
13
+ //#endregion
14
+ export { FormElement };
15
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","names":[],"sources":["../src/form/form.ts"],"mappings":";;cAMa,oBAAoB;YACZ;EAInB,aAAa;EAIb,UAAU,MAAM;;QAKV;YACI;IACR,aAAa"}
package/dist/form.js ADDED
@@ -0,0 +1,2 @@
1
+ import { t as FormElement } from "./form-CsXJX15p.js";
2
+ export { FormElement };
@@ -0,0 +1,121 @@
1
+ import { r as IDataProvider } from "./data-provider-BAn_-oR1.js";
2
+ import { l as ListSelectionMode } from "./types-DlVNJez_.js";
3
+ import "./panel.js";
4
+ import { LitElement, PropertyValues, nothing } from "lit";
5
+ import { CommandId } from "@clinth/ui-commands";
6
+ //#region src/grouped-item-lister/types.d.ts
7
+ /**
8
+ * Fired when the selection set changes by any means.
9
+ * Event name: `select`
10
+ */
11
+ type SubComponentSelectDetail<T> = {
12
+ readonly selected: ReadonlySet<T>;
13
+ readonly previous: ReadonlySet<T>;
14
+ };
15
+ /**
16
+ * Fired when an item is explicitly activated (double-click or Enter key).
17
+ * Event name: `activate`
18
+ */
19
+ type SubComponentActivateDetail<T> = {
20
+ readonly item: T;
21
+ };
22
+ /**
23
+ * Fired on a raw pointer click before any selection change.
24
+ * Event name: `item-click`
25
+ */
26
+ type SubComponentItemClickDetail<T> = {
27
+ readonly item: T;
28
+ };
29
+ /**
30
+ * Fired when the pointer hovers over an item or the keyboard cursor moves to it.
31
+ * Event name: `tickle`
32
+ */
33
+ type SubComponentTickleDetail<T> = {
34
+ readonly item: T;
35
+ };
36
+ type SubComponentSelectEvent<T> = CustomEvent<SubComponentSelectDetail<T>>;
37
+ type SubComponentActivateEvent<T> = CustomEvent<SubComponentActivateDetail<T>>;
38
+ type SubComponentItemClickEvent<T> = CustomEvent<SubComponentItemClickDetail<T>>;
39
+ type SubComponentTickleEvent<T> = CustomEvent<SubComponentTickleDetail<T>>;
40
+ interface IGroupedSubComponentEventMap<T> {
41
+ 'select': SubComponentSelectEvent<T>;
42
+ 'activate': SubComponentActivateEvent<T>;
43
+ 'item-click': SubComponentItemClickEvent<T>;
44
+ 'tickle': SubComponentTickleEvent<T>;
45
+ }
46
+ /**
47
+ * Contract that sub-components passed to `ixfx-grouped-item-lister` must satisfy.
48
+ *
49
+ * All four events (`select`, `activate`, `item-click`, `tickle`) must bubble and be composed
50
+ * so they propagate through the lister's light DOM to external listeners.
51
+ */
52
+ interface IGroupedSubComponent<T> extends HTMLElement {
53
+ /** Replace the full item list. Called whenever the lister's items or grouping changes. */
54
+ setItems: (items: readonly T[]) => void;
55
+ /** Clear the current selection without firing a `select` event (avoids re-entrant loops). */
56
+ clearSelection: () => void;
57
+ /** The current selection set. */
58
+ readonly selectedItems: ReadonlySet<T>;
59
+ /** Controls how many items can be selected simultaneously. */
60
+ selectionMode: ListSelectionMode;
61
+ /**
62
+ * Forward a command (e.g. sort-order change) to this sub-component.
63
+ * Uses `CommandId` from `@clinth/ui-commands` for interoperability.
64
+ */
65
+ dispatch: (commandId: CommandId, args?: Record<string, unknown>) => void;
66
+ addEventListener: (<K extends keyof IGroupedSubComponentEventMap<T>>(type: K, listener: (event: IGroupedSubComponentEventMap<T>[K]) => void, options?: boolean | AddEventListenerOptions) => void) & ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => void);
67
+ }
68
+ /**
69
+ * Factory that creates a sub-component for a given group.
70
+ * Called once per group (or once with `undefined` in ungrouped mode).
71
+ */
72
+ type SubComponentFactory<T> = (groupKey: string | undefined) => HTMLElement & IGroupedSubComponent<T>;
73
+ /**
74
+ * Fired by `ixfx-grouped-item-lister` after any sub-component fires `select`.
75
+ * Carries the full cross-group selection union and the key of the group whose
76
+ * selection just changed (or `undefined` in ungrouped mode).
77
+ */
78
+ type GroupedSelectDetail<T> = {
79
+ readonly selected: ReadonlySet<T>;
80
+ readonly groupKey: string | undefined;
81
+ };
82
+ type GroupedSelectEvent<T> = CustomEvent<GroupedSelectDetail<T>>;
83
+ interface GroupedItemListerElementEventMap<T> {
84
+ 'grouped-select': GroupedSelectEvent<T>;
85
+ }
86
+ //#endregion
87
+ //#region src/grouped-item-lister/grouped-item-lister.d.ts
88
+ declare class GroupedItemListerElement<T = unknown> extends LitElement {
89
+ #private;
90
+ grouped: boolean;
91
+ crossGroupSelection: boolean;
92
+ get groupBy(): ((item: T) => string) | undefined;
93
+ set groupBy(value: ((item: T) => string) | undefined);
94
+ get factory(): SubComponentFactory<T> | undefined;
95
+ set factory(value: SubComponentFactory<T> | undefined);
96
+ get provider(): IDataProvider<void, T> | undefined;
97
+ set provider(value: IDataProvider<void, T> | undefined);
98
+ /** The union of all selected items across every sub-component. */
99
+ get selectedItems(): ReadonlySet<T>;
100
+ /**
101
+ * Forward a command to all live sub-components.
102
+ * Use this to propagate changes like sort order, filter state, or view mode.
103
+ */
104
+ dispatch(commandId: CommandId, args?: Record<string, unknown>): void;
105
+ protected createRenderRoot(): this;
106
+ connectedCallback(): void;
107
+ disconnectedCallback(): void;
108
+ protected updated(changed: PropertyValues): void;
109
+ protected render(): import("lit").TemplateResult<1> | typeof nothing;
110
+ }
111
+ declare global {
112
+ interface HTMLElementTagNameMap {
113
+ 'ixfx-grouped-item-lister': GroupedItemListerElement;
114
+ }
115
+ }
116
+ interface GroupedItemListerElement<T> {
117
+ addEventListener: (<K extends keyof GroupedItemListerElementEventMap<T>>(type: K, listener: (this: GroupedItemListerElement<T>, event: GroupedItemListerElementEventMap<T>[K]) => void, options?: boolean | AddEventListenerOptions) => void) & ((type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => void);
118
+ }
119
+ //#endregion
120
+ export { IGroupedSubComponent as a, SubComponentActivateEvent as c, SubComponentItemClickEvent as d, SubComponentSelectDetail as f, SubComponentTickleEvent as h, GroupedSelectEvent as i, SubComponentFactory as l, SubComponentTickleDetail as m, GroupedItemListerElementEventMap as n, IGroupedSubComponentEventMap as o, SubComponentSelectEvent as p, GroupedSelectDetail as r, SubComponentActivateDetail as s, GroupedItemListerElement as t, SubComponentItemClickDetail as u };
121
+ //# sourceMappingURL=grouped-item-lister-CNLMgdIr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grouped-item-lister-CNLMgdIr.d.ts","names":[],"sources":["../src/grouped-item-lister/types.ts","../src/grouped-item-lister/grouped-item-lister.ts"],"mappings":";;;;;;;;;;KAWY,yBAAyB;WAC1B,UAAU,YAAY;WACtB,UAAU,YAAY;;;;;;KAOrB,2BAA2B;WAC5B,MAAM;;;;;;KAOL,4BAA4B;WAC7B,MAAM;;;;;;KAOL,yBAAyB;WAC1B,MAAM;;KAGL,wBAAwB,KAAK,YAAY,yBAAyB;KAClE,0BAA0B,KAAK,YAAY,2BAA2B;KACtE,2BAA2B,KAAK,YAAY,4BAA4B;KACxE,wBAAwB,KAAK,YAAY,yBAAyB;UAE7D,6BAA6B;EAC5C,UAAU,wBAAwB;EAClC,YAAY,0BAA0B;EACtC,cAAc,2BAA2B;EACzC,UAAU,wBAAwB;;;;;;;;UAanB,qBAAqB,WAAW;;EAE/C,WAAW,gBAAgB;;EAG3B;;WAGS,eAAe,YAAY;;EAGpC,eAAe;;;;;EAMf,WAAW,WAAW,WAAW,OAAO;EAExC,oBAAoB,gBAAgB,6BAA6B,IAC/D,MAAM,GACN,WAAW,OAAO,6BAA6B,GAAG,aAClD,oBAAoB,sCAEpB,cACA,UAAU,oCACV,oBAAoB;;;;;;KAQZ,oBAAoB,MAAM,iCAAiC,cAAc,qBAAqB;;;;;;KAW9F,oBAAoB;WACrB,UAAU,YAAY;WACtB;;KAGC,mBAAmB,KAAK,YAAY,oBAAoB;UAEnD,iCAAiC;EAChD,kBAAkB,mBAAmB;;;;cC9E1B,yBAAyB,qBAAqB;;EACb;EAG5C;MAgBI,aAAa,MAAM;MAInB,QAAQ,SAAS,MAAM;MAKvB,WAAW,oBAAoB;MAI/B,QAAQ,OAAO,oBAAoB;MAQnC,YAAY,oBAAoB;MAIhC,SAAS,OAAO,oBAAoB;;MAWpC,iBAAiB,YAAY;;;;;EAcjC,SAAS,WAAW,WAAW,OAAO;YAUnB;EAIV;EAKA;YAKU,QAAQ,SAAS;YAQjB,wBAAM,2BAAA;;QAmJnB;YACI;IACR,4BAA4B;;;UAIf,yBAAyB;EACxC,oBAAoB,gBAAgB,iCAAiC,IACnE,MAAM,GACN,WAAW,MAAM,yBAAyB,IAAI,OAAO,iCAAiC,GAAG,aACzF,oBAAoB,sCAEpB,cACA,UAAU,oCACV,oBAAoB"}
@@ -0,0 +1,2 @@
1
+ import { a as IGroupedSubComponent, c as SubComponentActivateEvent, d as SubComponentItemClickEvent, f as SubComponentSelectDetail, h as SubComponentTickleEvent, i as GroupedSelectEvent, l as SubComponentFactory, m as SubComponentTickleDetail, n as GroupedItemListerElementEventMap, o as IGroupedSubComponentEventMap, p as SubComponentSelectEvent, r as GroupedSelectDetail, s as SubComponentActivateDetail, t as GroupedItemListerElement, u as SubComponentItemClickDetail } from "./grouped-item-lister-CNLMgdIr.js";
2
+ export { GroupedItemListerElement, type GroupedItemListerElementEventMap, type GroupedSelectDetail, type GroupedSelectEvent, type IGroupedSubComponent, type IGroupedSubComponentEventMap, type SubComponentActivateDetail, type SubComponentActivateEvent, type SubComponentFactory, type SubComponentItemClickDetail, type SubComponentItemClickEvent, type SubComponentSelectDetail, type SubComponentSelectEvent, type SubComponentTickleDetail, type SubComponentTickleEvent };
@@ -0,0 +1,177 @@
1
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
2
+ import { t as DataController } from "./data-controller-DOlmqNKb.js";
3
+ import "./panel.js";
4
+ import { LitElement, html, nothing } from "lit";
5
+ import { property } from "lit/decorators.js";
6
+ import { repeat } from "lit/directives/repeat.js";
7
+ //#region src/grouped-item-lister/grouped-item-lister.ts
8
+ const UNGROUPED_KEY = `__ungrouped__`;
9
+ let GroupedItemListerElement = class GroupedItemListerElement extends LitElement {
10
+ constructor(..._args) {
11
+ super(..._args);
12
+ this.grouped = false;
13
+ this.crossGroupSelection = false;
14
+ this.#subComponents = /* @__PURE__ */ new Map();
15
+ this.#clearingSelection = false;
16
+ this.#data = new DataController(this);
17
+ this.#onSelect = (event) => {
18
+ if (this.#clearingSelection) return;
19
+ const sourceEl = event.target;
20
+ let sourceKey;
21
+ for (const [key, subEl] of this.#subComponents) if (subEl === sourceEl) {
22
+ sourceKey = key;
23
+ break;
24
+ }
25
+ if (sourceKey === void 0) return;
26
+ if (this.grouped && !this.crossGroupSelection) {
27
+ this.#clearingSelection = true;
28
+ for (const [key, subEl] of this.#subComponents) if (key !== sourceKey) subEl.clearSelection();
29
+ this.#clearingSelection = false;
30
+ }
31
+ this.dispatchEvent(new CustomEvent(`grouped-select`, {
32
+ detail: {
33
+ selected: this.selectedItems,
34
+ groupKey: sourceKey === UNGROUPED_KEY ? void 0 : sourceKey
35
+ },
36
+ bubbles: true,
37
+ composed: true
38
+ }));
39
+ };
40
+ }
41
+ #groupBy;
42
+ #factory;
43
+ #subComponents;
44
+ #renderedGroups;
45
+ #clearingSelection;
46
+ #data;
47
+ get groupBy() {
48
+ return this.#groupBy;
49
+ }
50
+ set groupBy(value) {
51
+ this.#groupBy = value;
52
+ this.requestUpdate();
53
+ }
54
+ get factory() {
55
+ return this.#factory;
56
+ }
57
+ set factory(value) {
58
+ if (value === this.#factory) return;
59
+ this.#clearSubComponents();
60
+ this.#factory = value;
61
+ this.requestUpdate();
62
+ }
63
+ get provider() {
64
+ return this.#data.provider;
65
+ }
66
+ set provider(value) {
67
+ this.#data.provider = value;
68
+ if (value) this.#data.request(void 0);
69
+ }
70
+ /** The union of all selected items across every sub-component. */
71
+ get selectedItems() {
72
+ const all = /* @__PURE__ */ new Set();
73
+ for (const subEl of this.#subComponents.values()) for (const item of subEl.selectedItems) all.add(item);
74
+ return all;
75
+ }
76
+ /**
77
+ * Forward a command to all live sub-components.
78
+ * Use this to propagate changes like sort order, filter state, or view mode.
79
+ */
80
+ dispatch(commandId, args) {
81
+ for (const subEl of this.#subComponents.values()) subEl.dispatch(commandId, args);
82
+ }
83
+ createRenderRoot() {
84
+ return this;
85
+ }
86
+ connectedCallback() {
87
+ super.connectedCallback();
88
+ this.addEventListener(`select`, this.#onSelect);
89
+ }
90
+ disconnectedCallback() {
91
+ super.disconnectedCallback();
92
+ this.removeEventListener(`select`, this.#onSelect);
93
+ }
94
+ updated(changed) {
95
+ super.updated(changed);
96
+ if (changed.has(`grouped`)) this.#clearSubComponents();
97
+ this.#syncSubComponents();
98
+ }
99
+ render() {
100
+ if (!this.#factory) return nothing;
101
+ if (!this.grouped) return html`<div class="ungrouped-host"></div>`;
102
+ if (!this.#groupBy) return nothing;
103
+ this.#renderedGroups = this.#computeGroups();
104
+ return html`${repeat(this.#renderedGroups.keys(), (key) => key, (key) => html`<ixfx-panel data-group-key=${key} .title=${key}></ixfx-panel>`)}`;
105
+ }
106
+ #computeGroups() {
107
+ const groups = /* @__PURE__ */ new Map();
108
+ if (!this.#groupBy) return groups;
109
+ for (const item of this.#data.items) {
110
+ const key = this.#groupBy(item);
111
+ let bucket = groups.get(key);
112
+ if (!bucket) {
113
+ bucket = [];
114
+ groups.set(key, bucket);
115
+ }
116
+ bucket.push(item);
117
+ }
118
+ return groups;
119
+ }
120
+ #clearSubComponents() {
121
+ for (const subEl of this.#subComponents.values()) subEl.remove();
122
+ this.#subComponents.clear();
123
+ }
124
+ #syncSubComponents() {
125
+ if (!this.#factory) return;
126
+ if (!this.grouped) {
127
+ this.#syncUngrouped();
128
+ return;
129
+ }
130
+ if (!this.#groupBy) return;
131
+ this.#syncGrouped();
132
+ }
133
+ #syncUngrouped() {
134
+ const host = this.querySelector(`.ungrouped-host`);
135
+ if (!host) return;
136
+ let subEl = this.#subComponents.get(UNGROUPED_KEY);
137
+ if (!subEl) {
138
+ subEl = this.#factory(void 0);
139
+ host.appendChild(subEl);
140
+ this.#subComponents.set(UNGROUPED_KEY, subEl);
141
+ }
142
+ subEl.setItems(this.#data.items);
143
+ }
144
+ #syncGrouped() {
145
+ const groups = this.#renderedGroups ?? this.#computeGroups();
146
+ for (const key of [...this.#subComponents.keys()]) if (!groups.has(key)) {
147
+ this.#subComponents.get(key)?.remove();
148
+ this.#subComponents.delete(key);
149
+ }
150
+ for (const [key, items] of groups) {
151
+ const panel = [...this.children].find((el) => el instanceof HTMLElement && el.dataset.groupKey === key);
152
+ if (!panel) continue;
153
+ let subEl = this.#subComponents.get(key);
154
+ if (!subEl) {
155
+ subEl = this.#factory(key);
156
+ panel.appendChild(subEl);
157
+ this.#subComponents.set(key, subEl);
158
+ }
159
+ subEl.setItems(items);
160
+ }
161
+ }
162
+ #onSelect;
163
+ };
164
+ __decorate([property({
165
+ type: Boolean,
166
+ reflect: true
167
+ })], GroupedItemListerElement.prototype, "grouped", void 0);
168
+ __decorate([property({
169
+ type: Boolean,
170
+ attribute: `cross-group-selection`,
171
+ reflect: true
172
+ })], GroupedItemListerElement.prototype, "crossGroupSelection", void 0);
173
+ GroupedItemListerElement = __decorate([safeCustomElement(`ixfx-grouped-item-lister`)], GroupedItemListerElement);
174
+ //#endregion
175
+ export { GroupedItemListerElement };
176
+
177
+ //# sourceMappingURL=grouped-item-lister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grouped-item-lister.js","names":["#clearingSelection","#subComponents","#data","#groupBy","#factory","#clearSubComponents","#onSelect","#syncSubComponents","#renderedGroups","#computeGroups","#syncUngrouped","#syncGrouped","customElement"],"sources":["../src/grouped-item-lister/grouped-item-lister.ts"],"sourcesContent":["/* eslint-disable ts/no-unsafe-declaration-merging */\nimport type { CommandId } from '@clinth/ui-commands';\nimport type { PropertyValues } from 'lit';\nimport type { IDataProvider } from '../common/data-provider.js';\nimport type {\n GroupedItemListerElementEventMap,\n GroupedSelectDetail,\n IGroupedSubComponent,\n SubComponentFactory,\n} from './types.js';\nimport { html, LitElement, nothing } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { DataController } from '../common/data-controller.js';\nimport { safeCustomElement as customElement } from '../util/custom-elements.js';\nimport '../panel/panel.js';\n\nexport type {\n GroupedItemListerElementEventMap,\n GroupedSelectDetail,\n GroupedSelectEvent,\n IGroupedSubComponent,\n IGroupedSubComponentEventMap,\n SubComponentActivateDetail,\n SubComponentActivateEvent,\n SubComponentFactory,\n SubComponentItemClickDetail,\n SubComponentItemClickEvent,\n SubComponentSelectDetail,\n SubComponentSelectEvent,\n SubComponentTickleDetail,\n SubComponentTickleEvent,\n} from './types.js';\n\nconst UNGROUPED_KEY = `__ungrouped__`;\n\n@customElement(`ixfx-grouped-item-lister`)\nexport class GroupedItemListerElement<T = unknown> extends LitElement {\n @property({ type: Boolean, reflect: true }) grouped = false;\n\n @property({ type: Boolean, attribute: `cross-group-selection`, reflect: true })\n crossGroupSelection = false;\n\n #groupBy: ((item: T) => string) | undefined;\n #factory: SubComponentFactory<T> | undefined;\n #subComponents = new Map<string, HTMLElement & IGroupedSubComponent<T>>();\n // Computed in render() and reused in updated() to avoid a second groupBy pass\n #renderedGroups: Map<string, T[]> | undefined;\n // Guard against re-entrant select events when we call clearSelection() on siblings\n #clearingSelection = false;\n\n readonly #data = new DataController<void, T>(this);\n\n // ---------------------------------------------------------------------------\n // JS-only properties (not HTML attributes — carry function/type information)\n // ---------------------------------------------------------------------------\n\n get groupBy(): ((item: T) => string) | undefined {\n return this.#groupBy;\n }\n\n set groupBy(value: ((item: T) => string) | undefined) {\n this.#groupBy = value;\n this.requestUpdate();\n }\n\n get factory(): SubComponentFactory<T> | undefined {\n return this.#factory;\n }\n\n set factory(value: SubComponentFactory<T> | undefined) {\n if (value === this.#factory)\n return;\n this.#clearSubComponents();\n this.#factory = value;\n this.requestUpdate();\n }\n\n get provider(): IDataProvider<void, T> | undefined {\n return this.#data.provider;\n }\n\n set provider(value: IDataProvider<void, T> | undefined) {\n this.#data.provider = value;\n if (value)\n this.#data.request(undefined);\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /** The union of all selected items across every sub-component. */\n get selectedItems(): ReadonlySet<T> {\n const all = new Set<T>();\n for (const subEl of this.#subComponents.values()) {\n for (const item of subEl.selectedItems) {\n all.add(item);\n }\n }\n return all;\n }\n\n /**\n * Forward a command to all live sub-components.\n * Use this to propagate changes like sort order, filter state, or view mode.\n */\n dispatch(commandId: CommandId, args?: Record<string, unknown>): void {\n for (const subEl of this.#subComponents.values()) {\n subEl.dispatch(commandId, args);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Lit lifecycle\n // ---------------------------------------------------------------------------\n\n protected override createRenderRoot() {\n return this;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener(`select`, this.#onSelect as EventListener);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener(`select`, this.#onSelect as EventListener);\n }\n\n protected override updated(changed: PropertyValues): void {\n super.updated(changed);\n if (changed.has(`grouped`)) {\n this.#clearSubComponents();\n }\n this.#syncSubComponents();\n }\n\n protected override render() {\n if (!this.#factory)\n return nothing;\n\n if (!this.grouped) {\n return html`<div class=\"ungrouped-host\"></div>`;\n }\n\n if (!this.#groupBy)\n return nothing;\n\n // Store computed groups so updated() can reuse them without a second pass\n this.#renderedGroups = this.#computeGroups();\n return html`${repeat(\n this.#renderedGroups.keys(),\n key => key,\n key => html`<ixfx-panel data-group-key=${key} .title=${key}></ixfx-panel>`,\n )}`;\n }\n\n // ---------------------------------------------------------------------------\n // Internal — DOM management\n // ---------------------------------------------------------------------------\n\n #computeGroups(): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n if (!this.#groupBy)\n return groups;\n for (const item of this.#data.items) {\n const key = this.#groupBy(item);\n let bucket = groups.get(key);\n if (!bucket) {\n bucket = [];\n groups.set(key, bucket);\n }\n bucket.push(item);\n }\n return groups;\n }\n\n #clearSubComponents(): void {\n for (const subEl of this.#subComponents.values()) {\n subEl.remove();\n }\n this.#subComponents.clear();\n }\n\n #syncSubComponents(): void {\n if (!this.#factory)\n return;\n\n if (!this.grouped) {\n this.#syncUngrouped();\n return;\n }\n\n if (!this.#groupBy)\n return;\n this.#syncGrouped();\n }\n\n #syncUngrouped(): void {\n const host = this.querySelector<HTMLElement>(`.ungrouped-host`);\n if (!host)\n return;\n\n let subEl = this.#subComponents.get(UNGROUPED_KEY);\n if (!subEl) {\n subEl = this.#factory!(undefined);\n host.appendChild(subEl);\n this.#subComponents.set(UNGROUPED_KEY, subEl);\n }\n subEl.setItems(this.#data.items);\n }\n\n #syncGrouped(): void {\n const groups = this.#renderedGroups ?? this.#computeGroups();\n\n // Remove sub-components for groups that no longer exist\n for (const key of [...this.#subComponents.keys()]) {\n if (!groups.has(key)) {\n this.#subComponents.get(key)?.remove();\n this.#subComponents.delete(key);\n }\n }\n\n // Mount or update sub-components per group.\n // Panels are already in the DOM at this point (rendered by Lit's repeat above).\n for (const [key, items] of groups) {\n const panel = [...this.children].find(\n el => el instanceof HTMLElement && (el as HTMLElement).dataset.groupKey === key,\n ) as HTMLElement | undefined;\n if (!panel)\n continue;\n\n let subEl = this.#subComponents.get(key);\n if (!subEl) {\n subEl = this.#factory!(key);\n panel.appendChild(subEl);\n this.#subComponents.set(key, subEl);\n }\n subEl.setItems(items);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal — selection coordination\n // ---------------------------------------------------------------------------\n\n #onSelect = (event: Event): void => {\n if (this.#clearingSelection)\n return;\n\n const sourceEl = event.target as HTMLElement;\n let sourceKey: string | undefined;\n for (const [key, subEl] of this.#subComponents) {\n if (subEl === sourceEl) {\n sourceKey = key;\n break;\n }\n }\n\n if (sourceKey === undefined)\n return;\n\n if (this.grouped && !this.crossGroupSelection) {\n this.#clearingSelection = true;\n for (const [key, subEl] of this.#subComponents) {\n if (key !== sourceKey)\n subEl.clearSelection();\n }\n this.#clearingSelection = false;\n }\n\n this.dispatchEvent(\n new CustomEvent<GroupedSelectDetail<T>>(`grouped-select`, {\n detail: {\n selected: this.selectedItems,\n groupKey: sourceKey === UNGROUPED_KEY ? undefined : sourceKey,\n },\n bubbles: true,\n composed: true,\n }),\n );\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ixfx-grouped-item-lister': GroupedItemListerElement;\n }\n}\n\nexport interface GroupedItemListerElement<T> {\n addEventListener: (<K extends keyof GroupedItemListerElementEventMap<T>>(\n type: K,\n listener: (this: GroupedItemListerElement<T>, event: GroupedItemListerElementEventMap<T>[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ) => void) & ((\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ) => void);\n}\n"],"mappings":"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"}