@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
@@ -0,0 +1,248 @@
1
+ # `ixfx-data-grid`
2
+
3
+ A virtualised data grid with sortable/resizable/reorderable columns, single/multi selection, grouping, and inline cell editing. Handles 200+ rows with windowed rendering and keyboard navigation.
4
+
5
+ ---
6
+
7
+ ## Quick start
8
+
9
+ ```html
10
+ <ixfx-data-grid id="grid" style="height:400px"></ixfx-data-grid>
11
+
12
+ <script type="module">
13
+ import '@ixfx/components/data-grid';
14
+
15
+ const grid = document.querySelector('#grid');
16
+ grid.columns = [
17
+ { key: 'name', label: 'Name', width: 160 },
18
+ { key: 'age', label: 'Age', width: 70, align: 'right' },
19
+ { key: 'score', label: 'Score', width: 90, align: 'right' },
20
+ ];
21
+ grid.rows = [
22
+ { key: 'r0', name: 'Ada', age: 30, score: 9.123 },
23
+ { key: 'r1', name: 'Bob', age: 25, score: 4 },
24
+ ];
25
+ </script>
26
+ ```
27
+
28
+ Import via bundle: `import '@ixfx/components';` or per-component `import '@ixfx/components/data-grid';`.
29
+
30
+ ---
31
+
32
+ ## Columns
33
+
34
+ ```ts
35
+ type DataGridColumn = {
36
+ key: string;
37
+ label: string;
38
+ width?: number; minWidth?: number; maxWidth?: number;
39
+ visible?: boolean;
40
+ sortable?: boolean; sortFn?: (a,b)=>number;
41
+ resizable?: boolean; reorderable?: boolean;
42
+ align?: 'left'|'center'|'right'; // default left, applied as justify-content + tabular-nums for numbers
43
+ renderer?: (row, col, rowIndex) => unknown; // Lit template or string
44
+ selectable?: boolean;
45
+ groupable?: boolean;
46
+ editable?: boolean;
47
+ editor?: 'text'|'number'|'boolean'|'select' | DataGridEditorFactory;
48
+ editorOptions?: readonly (string|{value,label})[];
49
+ formatter?: (value, row, col) => unknown;
50
+ };
51
+ ```
52
+
53
+ * `width` defaults to `150`. Drag the handle between headers to resize; double-click handle to auto-size to content. Drag the `⠿` handle to reorder.
54
+ * `align` controls cell content justification. Numeric columns should use `right` (or `center` for Age) – cells use `font-variant-numeric: tabular-nums` so decimal points align when paired with a fixed formatter.
55
+ * `renderer` overrides default `String(value)` display. Use `html` from `lit`.
56
+
57
+ ---
58
+
59
+ ## Rows & keys
60
+
61
+ ```ts
62
+ grid.rows = [{ key: 'r0', ... }, ...];
63
+ grid.getRowKey = (row, i) => row.key ?? row.id ?? String(i);
64
+ ```
65
+
66
+ Reassign `rows` to update. Sorting is derived from `sortState`.
67
+
68
+ ---
69
+
70
+ ## Selection & interaction
71
+
72
+ | Property | Type | Default | Description |
73
+ |---|---|---|---|
74
+ | `selectionMode` / `selection-mode` | `'none'|'single'|'multiple'` | `'single'` | How many rows can be selected |
75
+ | `interactionMode` / `interaction-mode` | `'standard'|'implicit'|'checked'|'sticky'|'vscode'|'manual'` | `'standard'` | Gesture mapping (same as `ixfx-vertical-list`) |
76
+
77
+ See `ixfx-vertical-list` README for mode table. Data-grid adds checkbox column in `checked` mode.
78
+
79
+ Programmatic: selection is `Set<string>` of row keys (`_selectedKeys` private, read via events).
80
+
81
+ ---
82
+
83
+ ## Sorting
84
+
85
+ Click header to cycle `asc → desc → none`. Or set:
86
+
87
+ ```ts
88
+ grid.sortState = { key: 'age', direction: 'asc' };
89
+ ```
90
+
91
+ Events: `datagrid-sort` `{ sort, previous }`. Custom `sortFn` per column.
92
+
93
+ ---
94
+
95
+ ## Grouping
96
+
97
+ ```ts
98
+ grid.allowGrouping = true;
99
+ grid.groupBy = 'department'; // or (row)=>String(row.dept)
100
+ grid.groupLabel = (key, rows) => `${key} (${rows.length})`;
101
+ grid.groupableColumns = ['department','age']; // optional allowlist
102
+ ```
103
+
104
+ Collapsible group headers (no background, just divider). Header bar `ixfx-menu-trigger` (gear) shows `Columns` check-items and `Group by` radio group – uses CSS anchor positioning with `position-try-fallbacks` so it never goes off-screen (flips `block`/`inline`, max-height `min(60vh,400px)` scrolls).
105
+
106
+ Cross-group selection:
107
+
108
+ ```html
109
+ <ixfx-data-grid cross-group-selection></ixfx-data-grid>
110
+ ```
111
+
112
+ * `cross-group-selection` (boolean, default `false` – matches `ixfx-grouped-item-lister`). When `false`, selecting in one group clears other groups; `Shift+click` range and `Ctrl+A` are clamped to the anchor's group. When `true`, selection spans groups. Logic lifted to `src/util/grouped-selection.ts`.
113
+
114
+ Events: `datagrid-group-change` `{ groupBy }`.
115
+
116
+ ---
117
+
118
+ ## Editing
119
+
120
+ ### Editability modes
121
+
122
+ | `editability` | Behaviour |
123
+ |---|---|
124
+ | `disabled` | No editing |
125
+ | `manual` | Only via `startEdit` / `startRowEdit` |
126
+ | `user` (default) | Slow double-click (two clicks 150–700ms apart on same cell) opens editor; `Enter` on a tickled row opens `lastClickedCell` if editable else first editable |
127
+ | `immediate` | Editable cells render as editors by default |
128
+
129
+ ```html
130
+ <ixfx-data-grid editability="user"></ixfx-data-grid>
131
+ ```
132
+
133
+ ```ts
134
+ grid.editable = (row, col) => col.key==='name' || col.key==='department';
135
+ // or per-column editable:true
136
+ ```
137
+
138
+ ### Editors
139
+
140
+ * `editor:'text'` (default) → `<input type="text">`
141
+ * `editor:'select'` + `editorOptions: ['A','B']` → `<select>`
142
+ * `editor:'boolean'` → checkbox
143
+ * `editor: (ctx)=>html`...` ` custom factory with `{ row, column, rowIndex, colIndex, value, commit, cancel }`
144
+
145
+ Editors are seeded with the cell's current value (`curVal`). For `select`, the matching `editorOptions` entry is preselected.
146
+
147
+ ### Programmatic
148
+
149
+ ```ts
150
+ grid.startEdit('r4','name'); // single cell
151
+ grid.startRowEdit('r4'); // all editable cells in row
152
+ grid.stopEdit();
153
+ ```
154
+
155
+ ### Keyboard while editing
156
+
157
+ * `Tab` / `Shift+Tab` – commit and move to next/prev editable cell in same row, focus follows.
158
+ * `Up` / `Down` – commit and move to same column in adjacent row if editable, also moves tickled cursor.
159
+ * `Enter` – commit.
160
+ * `Escape` – cancel (revert) when grid has focus.
161
+ * Focus leaving the component reverts editors (unless `immediate`), committing only via explicit `Tab`/`Enter`/`Down`.
162
+
163
+ Implementation: `updated` focuses the editor (`data-col` attribute), `_suppressBlurCommit` prevents double-commit on Tab navigation, `_onFocusOut` reverts.
164
+
165
+ ---
166
+
167
+ ## Formatting & alignment
168
+
169
+ Global or per-column formatters transform display value (not stored value):
170
+
171
+ ```ts
172
+ type GlobalFormatter = (value, columnName, row) => unknown;
173
+ grid.globalFormatter = (v, col) => col==='score' ? Number(v).toFixed(2) : v;
174
+ grid.setColumnFormatter('score', 'fixed', { digits: 3 }); // built-ins
175
+ grid.setColumnFormatter('score', v => String(v)); // custom
176
+ grid.setColumnFormatter('score', null); // clear
177
+ ```
178
+
179
+ Built-ins:
180
+
181
+ | Name | Options | Example |
182
+ |---|---|---|
183
+ | `fixed` | `{digits:2}` | `9.1 → 9.10` |
184
+ | `percentage` | `{digits:0}` | `0.75 → 75%` |
185
+ | `integer` | – | `9.6 → 10` |
186
+
187
+ Column `formatter?: (value,row,col)=>unknown` also supported. Formatted value is shown when no `renderer`; if `renderer` exists it receives raw row.
188
+
189
+ Demo: `Score` is random float `0–100` with 0–3 decimals, `align:'right'`, `font-variant-numeric: tabular-nums`, `fixed` 3 digits for decimal alignment. `Age` is `align:'center'` to demo middle alignment.
190
+
191
+ ---
192
+
193
+ ## Column visibility & menu
194
+
195
+ Gear menu (`ixfx-menu-trigger` placement `bottom-end`, `inset-area: block-end span-inline-end`) lists `Columns` (check-items) and `Group by` (radio). Toggle visibility via `col.visible=false` or menu. Event: `datagrid-column-visibility` `{key, visible}`.
196
+
197
+ Other events: `datagrid-column-reorder` `{from,to,columns}`, `datagrid-column-resize` `{key,width}`, `datagrid-cell-click` `{rowKey,colKey,rowIndex,colIndex}`, `datagrid-cell-edit` `{rowKey,colKey,oldValue,newValue}`.
198
+
199
+ ---
200
+
201
+ ## Keyboard navigation
202
+
203
+ Grid is focusable (`tabindex=0`). When not editing:
204
+
205
+ | Key | Action |
206
+ |---|---|
207
+ | `ArrowDown/Up` | Move tickled cursor, scroll into view; with `Shift` + `vscode`/`standard` extends range |
208
+ | `Home/End` | First/last |
209
+ | `Enter` | If `editability=user` and row has editable cell, open editor (prefers `lastClickedCell`); else select row |
210
+ | `Space` | Toggle selection |
211
+ | `Escape` | Clear selection (or cancel edit) |
212
+ | `Ctrl/Cmd+A` | Select all (clamped to anchor group if `!crossGroupSelection`) |
213
+
214
+ ---
215
+
216
+ ## CSS variables & parts
217
+
218
+ | Variable | Default | Description |
219
+ |---|---|---|
220
+ | `--data-grid-row-height` | `28px` | Row height |
221
+ | `--data-grid-header-height` | `32px` | Header height |
222
+ | `--data-grid-bg` | `var(--surface-3)` | Grid background |
223
+ | `--data-grid-header-bg` | `var(--surface-2)` | Header background |
224
+ | `--data-grid-border` | `var(--border)` | Outer/between border |
225
+ | `--data-grid-header-text` | `var(--surface-3-text)` | Header text |
226
+
227
+ Inherits global theme (`--surface-*`, `--accent`, `--item-*`, `--space-*`, `--radius-*`, `--shadow-m`). Cells use `font-variant-numeric: tabular-nums`. Group rows are transparent with divider.
228
+
229
+ No `::part` exports yet (header/row/cell are shadow-internal).
230
+
231
+ ---
232
+
233
+ ## Methods
234
+
235
+ | Method | Signature | Description |
236
+ |---|---|---|
237
+ | `setColumnFormatter` | `(key, preset\|fn\|null, opts?)` | Set built-in or custom formatter |
238
+ | `startEdit` | `(rowKey, colKey)` | Open single-cell editor |
239
+ | `startRowEdit` | `(rowKey)` | Open row editors |
240
+ | `stopEdit` | `()` | Cancel editors |
241
+ | `getVisibleRange` | `()=>{startRow,endRow,startCol,endCol}` | Virtual window |
242
+
243
+ ---
244
+
245
+ ## Demo
246
+
247
+ `demo/data-grid.html` + `demo/data-grid.ts`: 200 rows, `Department` random, `Score` float, `Name`/`Department` editable (`user` mode, slow double-click; `Tab`/`Up`/`Down` navigation; `Enter` from tickled row). Controls: Selection/Interaction/Group by/Editability + `Edit first row` (manual).
248
+
@@ -0,0 +1,222 @@
1
+ {
2
+ "name": "@ixfx/components",
3
+ "version": "0.6.1",
4
+ "generated": "2026-08-29T21:26:06.851Z",
5
+ "components": [
6
+ {
7
+ "name": "ac-text",
8
+ "summary": "VS Code command-palette style autocomplete text input. Shows a popup with matching results as the user types.",
9
+ "path": "docs-user/ac-text.md"
10
+ },
11
+ {
12
+ "name": "ac-token",
13
+ "summary": "Token/tag input with per-token autocomplete. Looks like a text box — committed tokens appear as inline chips, and a suggestion popup follows the typing cursor. Typical use cases are email To: fields a",
14
+ "path": "docs-user/ac-token.md"
15
+ },
16
+ {
17
+ "name": "button",
18
+ "summary": "Three related button components:",
19
+ "path": "docs-user/button.md"
20
+ },
21
+ {
22
+ "name": "checkbox",
23
+ "summary": "A custom checkbox with a diagonal line indicator and indeterminate state support. Uses shadow DOM with CSS variables for full appearance customization.",
24
+ "path": "docs-user/checkbox.md"
25
+ },
26
+ {
27
+ "name": "colour-picker",
28
+ "summary": "Two components for picking OKLCH colours. Use `ixfx-colour-picker` when you need a full inline picker; use `ixfx-colour-picker-popup` when you need a compact swatch that opens a picker in a popover.",
29
+ "path": "docs-user/colour-picker.md"
30
+ },
31
+ {
32
+ "name": "crumbs",
33
+ "summary": "Breadcrumb navigation with lazy-loading child support. Uses the same `TreeNode` / `TreeItem` data model as `ixfx-tree-list`, allowing easy swapping between the two components.",
34
+ "path": "docs-user/crumbs.md"
35
+ },
36
+ {
37
+ "name": "data-display",
38
+ "summary": "A debug data display component that renders key-value pairs from a plain object. Supports 7 inline render modes (`led`, `plot`, `plot-xy`, `plot-heat`, `plot-histogram`, `time-elapsed`, `hide`) via `c",
39
+ "path": "docs-user/data-display.md"
40
+ },
41
+ {
42
+ "name": "data-grid",
43
+ "summary": "A virtualised data grid with sortable/resizable/reorderable columns, single/multi selection, grouping, and inline cell editing. Handles 200+ rows with windowed rendering and keyboard navigation.",
44
+ "path": "docs-user/data-grid.md"
45
+ },
46
+ {
47
+ "name": "editable-label",
48
+ "summary": "Inline editable text components. `ixfx-editable-number` extends `ixfx-editable-label` with drag-to-adjust and fill-bar visualization.",
49
+ "path": "docs-user/editable-label.md"
50
+ },
51
+ {
52
+ "name": "form",
53
+ "summary": "Primitives for typical form layout with responsive label positioning and data binding.",
54
+ "path": "docs-user/form.md"
55
+ },
56
+ {
57
+ "name": "grouped-item-lister",
58
+ "summary": "A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own",
59
+ "path": "docs-user/grouped-item-lister.md"
60
+ },
61
+ {
62
+ "name": "icons",
63
+ "summary": "A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.",
64
+ "path": "docs-user/icons.md"
65
+ },
66
+ {
67
+ "name": "incr-search",
68
+ "summary": "Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.",
69
+ "path": "docs-user/incr-search.md"
70
+ },
71
+ {
72
+ "name": "labelled-radial-input",
73
+ "summary": "A composite radial input that pairs an editable label (`ixfx-editable-number`) with a rotary dial (`ixfx-radial-input`). The two controls are always in sync — turning the dial updates the number, and ",
74
+ "path": "docs-user/labelled-radial-input.md"
75
+ },
76
+ {
77
+ "name": "labelled-range-input",
78
+ "summary": "A composite range input that pairs an editable label (`ixfx-editable-number`) with a slider (`ixfx-range-input`). The two controls are always in sync — dragging the slider updates the number, and edit",
79
+ "path": "docs-user/labelled-range-input.md"
80
+ },
81
+ {
82
+ "name": "led",
83
+ "summary": "An LED indicator component with color mixing and optional blinking animation.",
84
+ "path": "docs-user/led.md"
85
+ },
86
+ {
87
+ "name": "menu",
88
+ "summary": "A custom menu system built on native HTML popovers with keyboard navigation, tickled focus management, and command routing.",
89
+ "path": "docs-user/menu.md"
90
+ },
91
+ {
92
+ "name": "miller",
93
+ "summary": "A Miller column component displays hierarchical data as multiple vertical columns, like macOS Finder or email clients. Each column shows the children of the selected item in the previous column.",
94
+ "path": "docs-user/miller.md"
95
+ },
96
+ {
97
+ "name": "narrowed-text",
98
+ "summary": "A filtered text selection component with inline search. Shows slotted items filtered by a query string as the user types.",
99
+ "path": "docs-user/narrowed-text.md"
100
+ },
101
+ {
102
+ "name": "notifier",
103
+ "summary": "A \"lookless\" app-wide notification system. `NotificationManager` is a singleton that owns notification state, queueing, and timing — it doesn't know how to draw anything. A `NotificationRenderer` (the",
104
+ "path": "docs-user/notifier.md"
105
+ },
106
+ {
107
+ "name": "panel",
108
+ "summary": "An collapsible panel component with support for titles, icons, toolbars, and multiple visual variants.",
109
+ "path": "docs-user/panel.md"
110
+ },
111
+ {
112
+ "name": "plots",
113
+ "summary": "SVG-based plotting components for visualising data streams.",
114
+ "path": "docs-user/plots.md"
115
+ },
116
+ {
117
+ "name": "polar-pad",
118
+ "summary": "A circular pad for setting polar coordinates (angle + radius). The user clicks or drags within a circle to position a marker. The marker's position encodes an angle (degrees, 0 = right, clockwise) and",
119
+ "path": "docs-user/polar-pad.md"
120
+ },
121
+ {
122
+ "name": "radial-input",
123
+ "summary": "A rotary dial input component inspired by music production interfaces (Ableton Live, synthesiser knobs). Renders as a flattened arc SVG and responds to drag and keyboard input.",
124
+ "path": "docs-user/radial-input.md"
125
+ },
126
+ {
127
+ "name": "range",
128
+ "summary": "Range: Coloured bar that suggests values",
129
+ "path": "docs-user/range.md"
130
+ },
131
+ {
132
+ "name": "range-input",
133
+ "summary": "A label-free, fully styled range slider wrapping `<input type=\"range\">`. Supports horizontal and vertical orientations, CSS variable theming, gradient fills, custom thumb shapes, and an optional value",
134
+ "path": "docs-user/range-input.md"
135
+ },
136
+ {
137
+ "name": "rating",
138
+ "summary": "A rating input that renders a row of glyphs (star / heart / square). Supports half-step increments, keyboard control, per-rank theming via CSS variables, and prominence-based hover feedback. Clicking ",
139
+ "path": "docs-user/rating.md"
140
+ },
141
+ {
142
+ "name": "select-horiz",
143
+ "summary": "A horizontally scrolling, snap-to-center single-item picker. Children are ordinary elements you provide — the component doesn't render its own item markup, it just lays them out with scroll-snap, keep",
144
+ "path": "docs-user/select-horiz.md"
145
+ },
146
+ {
147
+ "name": "slider-input",
148
+ "summary": "A linear SVG-based slider. Supports horizontal and vertical orientations, three value modes (single/dual/triple), three single-mode styles (range/bipolar/unipolar), triangle/needle/dot markers, and `v",
149
+ "path": "docs-user/slider-input.md"
150
+ },
151
+ {
152
+ "name": "snackbar",
153
+ "summary": "A small floating toolbar anchored in visual proximity to another element on the page (the \"target\"), built on the native Popover and CSS Anchor Positioning APIs.",
154
+ "path": "docs-user/snackbar.md"
155
+ },
156
+ {
157
+ "name": "snap-container",
158
+ "summary": "A container that positions its child element at configurable snap positions along an axis. The child can be dragged, clicked to snap, or controlled via keyboard.",
159
+ "path": "docs-user/snap-container.md"
160
+ },
161
+ {
162
+ "name": "split-layout",
163
+ "summary": "A Lit web component that lays out two elements within a container with a split bar to adjust their proportions.",
164
+ "path": "docs-user/split-layout.md"
165
+ },
166
+ {
167
+ "name": "swipe",
168
+ "summary": "A touch-friendly swipe/nudge component. User taps and drags to generate a value in range [-1, 1]. Value is emitted only on pointer release.",
169
+ "path": "docs-user/swipe.md"
170
+ },
171
+ {
172
+ "name": "tabs",
173
+ "summary": "A complete tabbed interface system using several coordinated components:",
174
+ "path": "docs-user/tabs.md"
175
+ },
176
+ {
177
+ "name": "timeline",
178
+ "summary": "A canvas-based timeline component with tracks, events, zoom/pan, and vertical track resizing. Suitable for visualizing time-series data, event logs, or scheduling.",
179
+ "path": "docs-user/timeline.md"
180
+ },
181
+ {
182
+ "name": "toolbar",
183
+ "summary": "Two components for building toolbars:",
184
+ "path": "docs-user/toolbar.md"
185
+ },
186
+ {
187
+ "name": "transitory-label",
188
+ "summary": "An inline text label that animates between values instead of changing instantly. Displays as `inline-block` and inherits text styling from its parent.",
189
+ "path": "docs-user/transitory-label.md"
190
+ },
191
+ {
192
+ "name": "tree",
193
+ "summary": "Three components share a common `TreeComponent` interface: **`ixfx-tree-list`**, **`ixfx-miller-list`**, and **`ixfx-crumb-navigation`**. This document describes the shared API, plus component-specifi",
194
+ "path": "docs-user/tree.md"
195
+ },
196
+ {
197
+ "name": "util",
198
+ "summary": "A tooltip component that renders a value with optional icon and formatted number display. Uses native render root (no shadow DOM).",
199
+ "path": "docs-user/util.md"
200
+ },
201
+ {
202
+ "name": "vertical-list",
203
+ "summary": "A scrollable vertical list of items with selection, keyboard navigation, and incremental search. Items can be plain `<li>` elements declared in HTML or rich custom elements added programmatically.",
204
+ "path": "docs-user/vertical-list.md"
205
+ },
206
+ {
207
+ "name": "xy-pad",
208
+ "summary": "A 2D touch-friendly XY pad for controlling two parameters simultaneously. Each axis supports independent linear/logarithmic scaling.",
209
+ "path": "docs-user/xy-pad.md"
210
+ }
211
+ ],
212
+ "overviews": [
213
+ {
214
+ "name": "user-catalog.md",
215
+ "path": "docs-user/user-catalog.md"
216
+ },
217
+ {
218
+ "name": "user-theming.md",
219
+ "path": "docs-user/user-theming.md"
220
+ }
221
+ ]
222
+ }
@@ -0,0 +1,58 @@
1
+ # @ixfx/components
2
+ > Web components for ixfx — Lit-based UI toolkit
3
+
4
+ ## Quick start
5
+ ```bash
6
+ pnpm add @ixfx/components
7
+ ```
8
+ ```ts
9
+ import '@ixfx/components/button';
10
+ import '@ixfx/components/data-grid';
11
+ ```
12
+
13
+ ## Components
14
+ - [ac-text](docs-user/ac-text.md): VS Code command-palette style autocomplete text input. Shows a popup with matching results as the user types.
15
+ - [ac-token](docs-user/ac-token.md): Token/tag input with per-token autocomplete. Looks like a text box — committed tokens appear as inline chips, and a suggestion popup follows the typing cursor. Typical use cases are email To: fields a
16
+ - [button](docs-user/button.md): Three related button components:
17
+ - [checkbox](docs-user/checkbox.md): A custom checkbox with a diagonal line indicator and indeterminate state support. Uses shadow DOM with CSS variables for full appearance customization.
18
+ - [colour-picker](docs-user/colour-picker.md): Two components for picking OKLCH colours. Use `ixfx-colour-picker` when you need a full inline picker; use `ixfx-colour-picker-popup` when you need a compact swatch that opens a picker in a popover.
19
+ - [crumbs](docs-user/crumbs.md): Breadcrumb navigation with lazy-loading child support. Uses the same `TreeNode` / `TreeItem` data model as `ixfx-tree-list`, allowing easy swapping between the two components.
20
+ - [data-display](docs-user/data-display.md): A debug data display component that renders key-value pairs from a plain object. Supports 7 inline render modes (`led`, `plot`, `plot-xy`, `plot-heat`, `plot-histogram`, `time-elapsed`, `hide`) via `c
21
+ - [data-grid](docs-user/data-grid.md): A virtualised data grid with sortable/resizable/reorderable columns, single/multi selection, grouping, and inline cell editing. Handles 200+ rows with windowed rendering and keyboard navigation.
22
+ - [editable-label](docs-user/editable-label.md): Inline editable text components. `ixfx-editable-number` extends `ixfx-editable-label` with drag-to-adjust and fill-bar visualization.
23
+ - [form](docs-user/form.md): Primitives for typical form layout with responsive label positioning and data binding.
24
+ - [grouped-item-lister](docs-user/grouped-item-lister.md): A meta-component that renders a typed item list using any sub-component you supply. Items can be displayed in a single sub-component (_ungrouped_ mode) or split into labelled groups, each with its own
25
+ - [icons](docs-user/icons.md): A centralized SVG icon system for `@ixfx/components`. Icons are stored by name, can be overridden globally, and all components that use a given icon re-render automatically when it changes.
26
+ - [incr-search](docs-user/incr-search.md): Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
27
+ - [labelled-radial-input](docs-user/labelled-radial-input.md): A composite radial input that pairs an editable label (`ixfx-editable-number`) with a rotary dial (`ixfx-radial-input`). The two controls are always in sync — turning the dial updates the number, and
28
+ - [labelled-range-input](docs-user/labelled-range-input.md): A composite range input that pairs an editable label (`ixfx-editable-number`) with a slider (`ixfx-range-input`). The two controls are always in sync — dragging the slider updates the number, and edit
29
+ - [led](docs-user/led.md): An LED indicator component with color mixing and optional blinking animation.
30
+ - [menu](docs-user/menu.md): A custom menu system built on native HTML popovers with keyboard navigation, tickled focus management, and command routing.
31
+ - [miller](docs-user/miller.md): A Miller column component displays hierarchical data as multiple vertical columns, like macOS Finder or email clients. Each column shows the children of the selected item in the previous column.
32
+ - [narrowed-text](docs-user/narrowed-text.md): A filtered text selection component with inline search. Shows slotted items filtered by a query string as the user types.
33
+ - [notifier](docs-user/notifier.md): A "lookless" app-wide notification system. `NotificationManager` is a singleton that owns notification state, queueing, and timing — it doesn't know how to draw anything. A `NotificationRenderer` (the
34
+ - [panel](docs-user/panel.md): An collapsible panel component with support for titles, icons, toolbars, and multiple visual variants.
35
+ - [plots](docs-user/plots.md): SVG-based plotting components for visualising data streams.
36
+ - [polar-pad](docs-user/polar-pad.md): A circular pad for setting polar coordinates (angle + radius). The user clicks or drags within a circle to position a marker. The marker's position encodes an angle (degrees, 0 = right, clockwise) and
37
+ - [radial-input](docs-user/radial-input.md): A rotary dial input component inspired by music production interfaces (Ableton Live, synthesiser knobs). Renders as a flattened arc SVG and responds to drag and keyboard input.
38
+ - [range](docs-user/range.md): Range: Coloured bar that suggests values
39
+ - [range-input](docs-user/range-input.md): A label-free, fully styled range slider wrapping `<input type="range">`. Supports horizontal and vertical orientations, CSS variable theming, gradient fills, custom thumb shapes, and an optional value
40
+ - [rating](docs-user/rating.md): A rating input that renders a row of glyphs (star / heart / square). Supports half-step increments, keyboard control, per-rank theming via CSS variables, and prominence-based hover feedback. Clicking
41
+ - [select-horiz](docs-user/select-horiz.md): A horizontally scrolling, snap-to-center single-item picker. Children are ordinary elements you provide — the component doesn't render its own item markup, it just lays them out with scroll-snap, keep
42
+ - [slider-input](docs-user/slider-input.md): A linear SVG-based slider. Supports horizontal and vertical orientations, three value modes (single/dual/triple), three single-mode styles (range/bipolar/unipolar), triangle/needle/dot markers, and `v
43
+ - [snackbar](docs-user/snackbar.md): A small floating toolbar anchored in visual proximity to another element on the page (the "target"), built on the native Popover and CSS Anchor Positioning APIs.
44
+ - [snap-container](docs-user/snap-container.md): A container that positions its child element at configurable snap positions along an axis. The child can be dragged, clicked to snap, or controlled via keyboard.
45
+ - [split-layout](docs-user/split-layout.md): A Lit web component that lays out two elements within a container with a split bar to adjust their proportions.
46
+ - [swipe](docs-user/swipe.md): A touch-friendly swipe/nudge component. User taps and drags to generate a value in range [-1, 1]. Value is emitted only on pointer release.
47
+ - [tabs](docs-user/tabs.md): A complete tabbed interface system using several coordinated components:
48
+ - [timeline](docs-user/timeline.md): A canvas-based timeline component with tracks, events, zoom/pan, and vertical track resizing. Suitable for visualizing time-series data, event logs, or scheduling.
49
+ - [toolbar](docs-user/toolbar.md): Two components for building toolbars:
50
+ - [transitory-label](docs-user/transitory-label.md): An inline text label that animates between values instead of changing instantly. Displays as `inline-block` and inherits text styling from its parent.
51
+ - [tree](docs-user/tree.md): Three components share a common `TreeComponent` interface: **`ixfx-tree-list`**, **`ixfx-miller-list`**, and **`ixfx-crumb-navigation`**. This document describes the shared API, plus component-specifi
52
+ - [util](docs-user/util.md): A tooltip component that renders a value with optional icon and formatted number display. Uses native render root (no shadow DOM).
53
+ - [vertical-list](docs-user/vertical-list.md): A scrollable vertical list of items with selection, keyboard navigation, and incremental search. Items can be plain `<li>` elements declared in HTML or rich custom elements added programmatically.
54
+ - [xy-pad](docs-user/xy-pad.md): A 2D touch-friendly XY pad for controlling two parameters simultaneously. Each axis supports independent linear/logarithmic scaling.
55
+
56
+ ## Overviews
57
+ - [user-catalog.md](docs-user/user-catalog.md)
58
+ - [user-theming.md](docs-user/user-theming.md)
@@ -0,0 +1,102 @@
1
+ # `ixfx-rating`
2
+
3
+ A rating input that renders a row of glyphs (star / heart / square). Supports half-step increments, keyboard control, per-rank theming via CSS variables, and prominence-based hover feedback. Clicking the current value clears it to `0` (unrated).
4
+
5
+ ```html
6
+ <ixfx-rating value="3"></ixfx-rating>
7
+ <ixfx-rating allow-half value="2.5"></ixfx-rating>
8
+ <ixfx-rating max="10" value="7"></ixfx-rating>
9
+ <ixfx-rating variant="heart" value="4"></ixfx-rating>
10
+ <ixfx-rating variant="square" value="3"></ixfx-rating>
11
+ <ixfx-rating disabled value="3"></ixfx-rating>
12
+ <ixfx-rating readonly value="3"></ixfx-rating>
13
+ ```
14
+
15
+ ### Properties / attributes
16
+
17
+ | Attribute / Property | Type | Default | Description |
18
+ |---|---|---|---|
19
+ | `value` | `number` | `0` | Current rating. `0` means unrated. Clamped `0..max` |
20
+ | `max` | `number` | `5` | Number of glyphs rendered |
21
+ | `allow-half` / `allowHalf` | `boolean` | `false` | When set, pointer position within a glyph selects `n` or `n+0.5`; keyboard step becomes `0.5` |
22
+ | `variant` | `"star"` \| `"heart"` \| `"square"` | `"star"` | Glyph shape |
23
+ | `disabled` | `boolean` | `false` | Disables interaction; muted via prominence filter and `pointer-events: none` |
24
+ | `readonly` | `boolean` | `false` | Prevents interaction but retains full visual prominence |
25
+
26
+ ### Events
27
+
28
+ Both events bubble and are `composed`.
29
+
30
+ | Event | Detail | Description |
31
+ |---|---|---|
32
+ | `input` | `{ value: number, oldValue: number }` | Fired on commit |
33
+ | `change` | `{ value: number, oldValue: number }` | Fired on commit (same timing as `input` for this component) |
34
+
35
+ Clicking the glyph that already equals `value` clears to `0`. Both events carry the previous value as `oldValue`.
36
+
37
+ ```ts
38
+ import type { RatingChangeEvent } from './rating.js';
39
+ el.addEventListener('change', (e: RatingChangeEvent) => {
40
+ console.log(e.detail.value, e.detail.oldValue);
41
+ });
42
+ el.addEventListener('input', (e: RatingChangeEvent) => {
43
+ console.log(e.detail.value, e.detail.oldValue);
44
+ });
45
+ ```
46
+
47
+ ### Interaction
48
+
49
+ **Pointer:** hovering previews the value that would be set (filled glyphs use the hover colour, host gains prominence saturation). Click commits; clicking the active value clears to `0`. With `allow-half`, the left/right half of a glyph resolves to `n+0.5` / `n+1`.
50
+
51
+ **Keyboard** (when focused, not `disabled`/`readonly`):
52
+
53
+ | Key | Action |
54
+ |---|---|
55
+ | `ArrowRight` / `ArrowUp` | Increase by `1` (or `0.5` with `allow-half`) |
56
+ | `ArrowLeft` / `ArrowDown` | Decrease by `1` (or `0.5`) |
57
+ | `Home` | Clear to `0` |
58
+ | `End` | Set to `max` |
59
+ | `0`–`9` | Set to that number (clamped to `max`) |
60
+
61
+ Accessibility: `role="slider"` with `aria-valuenow` / `aria-valuemin="0"` / `aria-valuemax` / `aria-disabled` / `aria-readonly`; `tabindex` is `0` when interactive, `-1` when `disabled` or `readonly`.
62
+
63
+ ### CSS custom properties
64
+
65
+ | Property | Default | Description |
66
+ |---|---|---|
67
+ | `--ixfx-rating-size` | `1.5rem` | Glyph width/height |
68
+ | `--ixfx-rating-gap` | `0.2rem` | Gap between glyphs |
69
+ | `--ixfx-rating-color` | `var(--surface-4)` | Idle glyph colour |
70
+ | `--ixfx-rating-color-active` | `var(--accent)` | Filled glyph colour |
71
+ | `--ixfx-rating-color-hover` | `var(--accent)` | Hover-preview glyph colour |
72
+ | `--ixfx-rating-<n>-color` | — | Per-rank idle override (e.g. `--ixfx-rating-3-color`) |
73
+ | `--ixfx-rating-<n>-color-active` | — | Per-rank filled override |
74
+ | `--ixfx-rating-<n>-color-hover` | — | Per-rank hover override |
75
+
76
+ Per-rank variables are set **outside** the shadow DOM (e.g. on the `ixfx-rating` element or a parent) and bridged via inline `var()` fallbacks on each glyph, so they pierce the shadow boundary without extra plumbing.
77
+
78
+ ```html
79
+ <style>
80
+ ixfx-rating.saturated {
81
+ --ixfx-rating-1-color-active: oklch(70% 0.05 280);
82
+ --ixfx-rating-5-color-active: oklch(60% 0.25 30);
83
+ }
84
+ </style>
85
+ <ixfx-rating class="saturated" value="4"></ixfx-rating>
86
+ ```
87
+
88
+ ### Prominence
89
+
90
+ Includes `prominenceStyles` from `src/styles/prominence.ts`: idle state is desaturated/dimmed, `:hover` / `:focus-within` raises opacity and saturation, `:active` removes the filter, `disabled` applies a heavy mute. `readonly` explicitly resets the filter so it remains visually full. Override via `--prominence-*` variables if needed.
91
+
92
+ ### TypeScript types
93
+
94
+ ```ts
95
+ import type { RatingElement, RatingVariant, RatingChangeDetail, RatingChangeEvent } from 'ixfxfun-components';
96
+ ```
97
+
98
+ ### Implementation notes
99
+
100
+ * Half-fill is a stacked pair ofidentical SVGs — a background glyph plus an absolutely-positioned foreground glyph clipped with `clip-path: inset(0 50% 0 0)`.
101
+ * Glyph paths are inline `d` strings (`star` is a 5-point star, `heart` a cardioid-like path, `square` a rect) sharing a single `24×24` viewBox.
102
+ * Preview is local `@state _preview: number | null`, derived into render via `_effective() = _preview ?? value`; `pointerleave` on the host clears it.