@ixfx/components 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/bundle/index.d.ts +1086 -603
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +16527 -14258
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +14 -4
  6. package/dist/ac-text.d.ts.map +1 -1
  7. package/dist/ac-text.js +11 -6
  8. package/dist/ac-text.js.map +1 -1
  9. package/dist/{button-DMt5XWUK.js → button-C3fLMKUv.js} +4 -4
  10. package/dist/{button-DMt5XWUK.js.map → button-C3fLMKUv.js.map} +1 -1
  11. package/dist/button.d.ts.map +1 -1
  12. package/dist/button.js +1 -1
  13. package/dist/checkbox.d.ts +2 -2
  14. package/dist/checkbox.d.ts.map +1 -1
  15. package/dist/checkbox.js +14 -8
  16. package/dist/checkbox.js.map +1 -1
  17. package/dist/{colour-C3MQIjFJ-BLXg8W3U.js → colour-C3MQIjFJ-vRCdr8W-.js} +146 -552
  18. package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +1 -0
  19. package/dist/{colour-picker-K6zu82lm.js → colour-picker-0sgAha1T.js} +14 -9
  20. package/dist/colour-picker-0sgAha1T.js.map +1 -0
  21. package/dist/colour-picker.d.ts +1 -1
  22. package/dist/colour-picker.js +1 -1
  23. package/dist/crumbs.d.ts +1 -1
  24. package/dist/crumbs.d.ts.map +1 -1
  25. package/dist/crumbs.js +11 -6
  26. package/dist/crumbs.js.map +1 -1
  27. package/dist/data-display.d.ts +9 -4
  28. package/dist/data-display.d.ts.map +1 -1
  29. package/dist/data-display.js +270 -43
  30. package/dist/data-display.js.map +1 -1
  31. package/dist/data-provider-DgI909FP.js +23 -0
  32. package/dist/data-provider-DgI909FP.js.map +1 -0
  33. package/dist/{decorate-D7rC1gLP.js → decorate-DdjvVHS-.js} +2 -2
  34. package/dist/{decorate-D7rC1gLP.js.map → decorate-DdjvVHS-.js.map} +1 -1
  35. package/dist/{defaults-C7elhuyJ.js → defaults-C3b9OWJD.js} +2 -2
  36. package/dist/{defaults-C7elhuyJ.js.map → defaults-C3b9OWJD.js.map} +1 -1
  37. package/dist/{dist-TAGYwaju.js → dist-CA13FvMn.js} +7 -66
  38. package/dist/dist-CA13FvMn.js.map +1 -0
  39. package/dist/editable-label.d.ts.map +1 -1
  40. package/dist/editable-label.js +3 -2
  41. package/dist/editable-label.js.map +1 -1
  42. package/dist/fallbacks-pNMOMx38.js +168 -0
  43. package/dist/fallbacks-pNMOMx38.js.map +1 -0
  44. package/dist/{hex-editor-DlfoA65y.d.ts → hex-editor-CMIxtNgT.d.ts} +1 -1
  45. package/dist/hex-editor-CMIxtNgT.d.ts.map +1 -0
  46. package/dist/{hex-editor-CsN_ySkH.js → hex-editor-WZrjRtvL.js} +3 -3
  47. package/dist/{hex-editor-CsN_ySkH.js.map → hex-editor-WZrjRtvL.js.map} +1 -1
  48. package/dist/hex.d.ts +1 -1
  49. package/dist/hex.js +1 -1
  50. package/dist/{highlight-TPbSHruI.js → highlight-B-eQrhhb.js} +1 -1
  51. package/dist/{highlight-TPbSHruI.js.map → highlight-B-eQrhhb.js.map} +1 -1
  52. package/dist/{icon-CeTDJhC7.d.ts → icon-Bd5BiU4b.d.ts} +2 -2
  53. package/dist/icon-Bd5BiU4b.d.ts.map +1 -0
  54. package/dist/{icon-9NWTXtE1.js → icon-Ndo40kNO.js} +6 -5
  55. package/dist/icon-Ndo40kNO.js.map +1 -0
  56. package/dist/icons.d.ts +2 -2
  57. package/dist/icons.js +3 -3
  58. package/dist/{incr-search-DSKKsk6w.js → incr-search-CeERfijK.js} +2 -2
  59. package/dist/{incr-search-DSKKsk6w.js.map → incr-search-CeERfijK.js.map} +1 -1
  60. package/dist/incr-search.d.ts +1 -1
  61. package/dist/incr-search.js +2 -2
  62. package/dist/{index-hXFn14tM.d.ts → index-BMjri2-S.d.ts} +2 -2
  63. package/dist/{index-hXFn14tM.d.ts.map → index-BMjri2-S.d.ts.map} +1 -1
  64. package/dist/{index-DSlPeWLK.d.ts → index-CWpNcQKE.d.ts} +5 -5
  65. package/dist/index-CWpNcQKE.d.ts.map +1 -0
  66. package/dist/{index--hrxPBkQ.d.ts → index-CowKi2mo.d.ts} +2 -1
  67. package/dist/index-CowKi2mo.d.ts.map +1 -0
  68. package/dist/{index-B1hNR7Z9.d.ts → index-D87kRcEs.d.ts} +1 -1
  69. package/dist/{index-B1hNR7Z9.d.ts.map → index-D87kRcEs.d.ts.map} +1 -1
  70. package/dist/{index-CnL9krsH.d.ts → index-DgXpCg_G.d.ts} +32 -26
  71. package/dist/index-DgXpCg_G.d.ts.map +1 -0
  72. package/dist/{index-DrQjA8Gs.d.ts → index-s_IhSnwc.d.ts} +2 -2
  73. package/dist/index-s_IhSnwc.d.ts.map +1 -0
  74. package/dist/index.d.ts +432 -121
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2370 -1707
  77. package/dist/index.js.map +1 -1
  78. package/dist/interaction-D6efrBLo.js +1 -0
  79. package/dist/{keyboard-De_vOHLg.js → keyboard-CimiEskD.js} +1 -1
  80. package/dist/{keyboard-De_vOHLg.js.map → keyboard-CimiEskD.js.map} +1 -1
  81. package/dist/{labelled-input-base-CqPL5rKm.js → labelled-input-base-9K_w3oLj.js} +2 -2
  82. package/dist/{labelled-input-base-CqPL5rKm.js.map → labelled-input-base-9K_w3oLj.js.map} +1 -1
  83. package/dist/{labelled-input-base-D-Y7agky.d.ts → labelled-input-base-ORb1rGq6.d.ts} +1 -1
  84. package/dist/labelled-input-base-ORb1rGq6.d.ts.map +1 -0
  85. package/dist/labelled-radial-input.d.ts +2 -2
  86. package/dist/labelled-radial-input.d.ts.map +1 -1
  87. package/dist/labelled-radial-input.js +7 -6
  88. package/dist/labelled-radial-input.js.map +1 -1
  89. package/dist/labelled-range-input.d.ts +1 -1
  90. package/dist/labelled-range-input.js +2 -2
  91. package/dist/led.d.ts +1 -1
  92. package/dist/led.d.ts.map +1 -1
  93. package/dist/led.js +2 -2
  94. package/dist/led.js.map +1 -1
  95. package/dist/{menu-item-CKRfP0pN.js → menu-item-BrIuORYD.js} +20 -10
  96. package/dist/menu-item-BrIuORYD.js.map +1 -0
  97. package/dist/{menu-item-ClQF2DKN.d.ts → menu-item-COx-Azgj.d.ts} +1 -1
  98. package/dist/menu-item-COx-Azgj.d.ts.map +1 -0
  99. package/dist/{menu-DJJuCm-5.js → menu-vjoiHZ13.js} +30 -17
  100. package/dist/menu-vjoiHZ13.js.map +1 -0
  101. package/dist/menu.d.ts +2 -2
  102. package/dist/menu.js +2 -2
  103. package/dist/miller.d.ts +2 -2
  104. package/dist/miller.d.ts.map +1 -1
  105. package/dist/miller.js +18 -11
  106. package/dist/miller.js.map +1 -1
  107. package/dist/narrowed-text.d.ts.map +1 -1
  108. package/dist/narrowed-text.js +7 -5
  109. package/dist/narrowed-text.js.map +1 -1
  110. package/dist/panel.d.ts +1 -1
  111. package/dist/panel.d.ts.map +1 -1
  112. package/dist/panel.js +5 -4
  113. package/dist/panel.js.map +1 -1
  114. package/dist/plots.d.ts +2 -2
  115. package/dist/plots.js +2 -2
  116. package/dist/polar-pad.d.ts +1 -1
  117. package/dist/polar-pad.d.ts.map +1 -1
  118. package/dist/polar-pad.js +10 -4
  119. package/dist/polar-pad.js.map +1 -1
  120. package/dist/{popup-ImtS9KnS.js → popup-DiWMxsWj.js} +1 -1
  121. package/dist/{popup-ImtS9KnS.js.map → popup-DiWMxsWj.js.map} +1 -1
  122. package/dist/prominence-DlFShic3.js +104 -0
  123. package/dist/prominence-DlFShic3.js.map +1 -0
  124. package/dist/{radial-input-LUSBEycd.d.ts → radial-input-CpQPs1Mp.d.ts} +1 -1
  125. package/dist/radial-input-CpQPs1Mp.d.ts.map +1 -0
  126. package/dist/{radial-input-DZA6WaFu.js → radial-input-DZ7eQCcM.js} +11 -6
  127. package/dist/radial-input-DZ7eQCcM.js.map +1 -0
  128. package/dist/radial-input.d.ts +1 -1
  129. package/dist/radial-input.js +1 -1
  130. package/dist/range-input.d.ts +1 -1
  131. package/dist/range-input.d.ts.map +1 -1
  132. package/dist/range-input.js +9 -4
  133. package/dist/range-input.js.map +1 -1
  134. package/dist/range.d.ts +1 -1
  135. package/dist/range.d.ts.map +1 -1
  136. package/dist/range.js +5 -3
  137. package/dist/range.js.map +1 -1
  138. package/dist/{registry-Q2gYQCHM.js → registry-CkW7q09Q.js} +1 -1
  139. package/dist/{registry-Q2gYQCHM.js.map → registry-CkW7q09Q.js.map} +1 -1
  140. package/dist/selecthorizontal.d.ts +1 -1
  141. package/dist/selecthorizontal.d.ts.map +1 -1
  142. package/dist/selecthorizontal.js +4 -3
  143. package/dist/selecthorizontal.js.map +1 -1
  144. package/dist/snap-container.d.ts.map +1 -1
  145. package/dist/snap-container.js +1 -1
  146. package/dist/split-layout.d.ts +1 -1
  147. package/dist/split-layout.d.ts.map +1 -1
  148. package/dist/split-layout.js +6 -5
  149. package/dist/split-layout.js.map +1 -1
  150. package/dist/style.css +14 -4
  151. package/dist/swipe.d.ts.map +1 -1
  152. package/dist/swipe.js +9 -4
  153. package/dist/swipe.js.map +1 -1
  154. package/dist/{tab-list-BnqISNvO.d.ts → tab-list-DLzb-m-I.d.ts} +2 -2
  155. package/dist/tab-list-DLzb-m-I.d.ts.map +1 -0
  156. package/dist/tabs.d.ts +1 -1
  157. package/dist/tabs.js +10 -4
  158. package/dist/tabs.js.map +1 -1
  159. package/dist/{tickled-controller-Cq1Va0rR.d.ts → tickled-controller-BjYCzGrU.d.ts} +2 -2
  160. package/dist/tickled-controller-BjYCzGrU.d.ts.map +1 -0
  161. package/dist/{tickled-styles-Bg3QbcrD.js → tickled-styles-fNDdqf6l.js} +1 -17
  162. package/dist/{tickled-styles-Bg3QbcrD.js.map → tickled-styles-fNDdqf6l.js.map} +1 -1
  163. package/dist/{timeline-Cv75Th2U.js → timeline-2OFtN4Ta.js} +1437 -36
  164. package/dist/timeline-2OFtN4Ta.js.map +1 -0
  165. package/dist/timeline.d.ts +1 -1
  166. package/dist/timeline.js +1 -1
  167. package/dist/{tooltip-DMxmBNky.js → tooltip-CJt3WLmN.js} +1 -1
  168. package/dist/{tooltip-DMxmBNky.js.map → tooltip-CJt3WLmN.js.map} +1 -1
  169. package/dist/{tooltip-CwGfb4lp.d.ts → tooltip-RGVIwHrZ.d.ts} +1 -1
  170. package/dist/{tooltip-CwGfb4lp.d.ts.map → tooltip-RGVIwHrZ.d.ts.map} +1 -1
  171. package/dist/{tree-CvvKbO-f.js → tree-CJWpMUvD.js} +113 -103
  172. package/dist/tree-CJWpMUvD.js.map +1 -0
  173. package/dist/{tree-component-D8Bg48tt.d.ts → tree-component-BcO26Xvt.d.ts} +65 -3
  174. package/dist/tree-component-BcO26Xvt.d.ts.map +1 -0
  175. package/dist/tree.d.ts +15 -15
  176. package/dist/tree.d.ts.map +1 -1
  177. package/dist/tree.js +1 -1
  178. package/dist/{types-BKaqnpwx.d.ts → types-DSfbtHs3.d.ts} +1 -1
  179. package/dist/types-DSfbtHs3.d.ts.map +1 -0
  180. package/dist/{xy-axis-DL0tfJDr.js → xy-axis-CK64haaK.js} +1831 -359
  181. package/dist/xy-axis-CK64haaK.js.map +1 -0
  182. package/dist/{xy-axis-D9uwpGTT.d.ts → xy-axis-McdUpcYr.d.ts} +102 -3
  183. package/dist/xy-axis-McdUpcYr.d.ts.map +1 -0
  184. package/dist/xy-pad.d.ts +1 -1
  185. package/dist/xy-pad.d.ts.map +1 -1
  186. package/dist/xy-pad.js +11 -6
  187. package/dist/xy-pad.js.map +1 -1
  188. package/docs-user/README.md +45 -0
  189. package/docs-user/ac-text.md +67 -0
  190. package/docs-user/ac-token.md +187 -0
  191. package/docs-user/button.md +355 -0
  192. package/docs-user/checkbox.md +62 -0
  193. package/docs-user/colour-picker.md +237 -0
  194. package/docs-user/crumbs.md +66 -0
  195. package/docs-user/data-display.md +98 -0
  196. package/docs-user/editable-label.md +244 -0
  197. package/docs-user/grouped-item-lister.md +424 -0
  198. package/docs-user/icons.md +191 -0
  199. package/docs-user/incr-search.md +94 -0
  200. package/docs-user/labelled-radial-input.md +204 -0
  201. package/docs-user/labelled-range-input.md +194 -0
  202. package/docs-user/led.md +40 -0
  203. package/docs-user/menu.md +319 -0
  204. package/docs-user/miller.md +453 -0
  205. package/docs-user/narrowed-text.md +72 -0
  206. package/docs-user/panel.md +250 -0
  207. package/docs-user/plots.md +361 -0
  208. package/docs-user/polar-pad.md +228 -0
  209. package/docs-user/radial-input.md +559 -0
  210. package/docs-user/range-input.md +264 -0
  211. package/docs-user/range.md +12 -0
  212. package/docs-user/selecthorizontal.md +32 -0
  213. package/docs-user/snap-container.md +132 -0
  214. package/docs-user/split-layout.md +232 -0
  215. package/docs-user/swipe.md +44 -0
  216. package/docs-user/tabs.md +217 -0
  217. package/docs-user/timeline.md +116 -0
  218. package/docs-user/transitory-label.md +120 -0
  219. package/docs-user/tree.md +592 -0
  220. package/docs-user/user-catalog.md +165 -0
  221. package/docs-user/user-theming.md +379 -0
  222. package/docs-user/util.md +122 -0
  223. package/docs-user/vertical-list.md +351 -0
  224. package/docs-user/xy-pad.md +94 -0
  225. package/package.json +19 -18
  226. package/dist/colour-C3MQIjFJ-BLXg8W3U.js.map +0 -1
  227. package/dist/colour-picker-K6zu82lm.js.map +0 -1
  228. package/dist/dist-BX0OVIm7.js +0 -1290
  229. package/dist/dist-BX0OVIm7.js.map +0 -1
  230. package/dist/dist-TAGYwaju.js.map +0 -1
  231. package/dist/hex-editor-DlfoA65y.d.ts.map +0 -1
  232. package/dist/icon-9NWTXtE1.js.map +0 -1
  233. package/dist/icon-CeTDJhC7.d.ts.map +0 -1
  234. package/dist/index--hrxPBkQ.d.ts.map +0 -1
  235. package/dist/index-CnL9krsH.d.ts.map +0 -1
  236. package/dist/index-DSlPeWLK.d.ts.map +0 -1
  237. package/dist/index-DrQjA8Gs.d.ts.map +0 -1
  238. package/dist/interaction-DG7X1rhI.js +0 -1
  239. package/dist/labelled-input-base-D-Y7agky.d.ts.map +0 -1
  240. package/dist/menu-DJJuCm-5.js.map +0 -1
  241. package/dist/menu-item-CKRfP0pN.js.map +0 -1
  242. package/dist/menu-item-ClQF2DKN.d.ts.map +0 -1
  243. package/dist/prominence--ic9uWBl.js +0 -98
  244. package/dist/prominence--ic9uWBl.js.map +0 -1
  245. package/dist/radial-input-DZA6WaFu.js.map +0 -1
  246. package/dist/radial-input-LUSBEycd.d.ts.map +0 -1
  247. package/dist/tab-list-BnqISNvO.d.ts.map +0 -1
  248. package/dist/tickled-controller-Cq1Va0rR.d.ts.map +0 -1
  249. package/dist/timeline-Cv75Th2U.js.map +0 -1
  250. package/dist/tree-CvvKbO-f.js.map +0 -1
  251. package/dist/tree-component-D8Bg48tt.d.ts.map +0 -1
  252. package/dist/types-BKaqnpwx.d.ts.map +0 -1
  253. package/dist/xy-axis-D9uwpGTT.d.ts.map +0 -1
  254. package/dist/xy-axis-DL0tfJDr.js.map +0 -1
  255. /package/bundle/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
  256. /package/dist/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
@@ -0,0 +1,94 @@
1
+ # Incremental Search
2
+
3
+ Utilities for incremental (type-ahead) search over data collections or DOM-backed lists.
4
+ You can use the low-level `createIncrSearch` for data, `createElementIncrSearch` for
5
+ DOM elements, or `IncrSearchTreeController` for tree and Miller list components.
6
+
7
+ ## Plain UL list
8
+
9
+ Matches the approach in `demo/incr-search.ts` using `createElementIncrSearch`.
10
+
11
+ ```typescript
12
+ import { createElementIncrSearch } from '../src/incr-search/index.js';
13
+
14
+ const ITEMS = [
15
+ 'Afghanistan', 'Albania', 'Algeria', 'Andorra', 'Angola',
16
+ 'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan',
17
+ ];
18
+
19
+ const listEl = document.querySelector<HTMLUListElement>('#list')!;
20
+ const searchEl = document.querySelector<HTMLInputElement>('#search')!;
21
+
22
+ for (const name of ITEMS) {
23
+ const li = document.createElement('li');
24
+ li.textContent = name;
25
+ listEl.append(li);
26
+ }
27
+
28
+ const search = createElementIncrSearch({
29
+ container: listEl,
30
+ itemSelector: 'li',
31
+ highlightKey: 'demo-incr-highlight',
32
+ onChange: state => {
33
+ const count = state.query.trim() ? state.results.length : ITEMS.length;
34
+ console.log('query', `"${state.query}" → ${count} items`);
35
+ },
36
+ });
37
+
38
+ searchEl.addEventListener('input', () => {
39
+ void search.setQuery(searchEl.value);
40
+ });
41
+
42
+ searchEl.addEventListener('keydown', (e: KeyboardEvent) => {
43
+ if (e.key === 'Escape') {
44
+ searchEl.value = '';
45
+ void search.clearQuery();
46
+ }
47
+ });
48
+ ```
49
+
50
+ ## Tree list component
51
+
52
+ Use `IncrSearchTreeController` with `ixfx-tree-list` (or any `TreeSearchHost`).
53
+
54
+ ```typescript
55
+ import { IncrSearchTreeController } from '../src/incr-search/index.js';
56
+ import type { TreeListElement } from '../src/tree/tree-list.ts';
57
+
58
+ const treeEl = document.querySelector<TreeListElement>('ixfx-tree-list')!;
59
+ const searchEl = document.querySelector<HTMLInputElement>('[data-search]')!;
60
+
61
+ const searchCtrl = new IncrSearchTreeController(treeEl, {
62
+ selector: ({ node }) => node.item.label,
63
+ input: searchEl,
64
+ });
65
+
66
+ searchCtrl.connect();
67
+ ```
68
+
69
+ ## Miller tree component
70
+
71
+ `IncrSearchMillerController` is a compatibility export. Prefer `IncrSearchTreeController`
72
+ and pass the Miller label selector.
73
+
74
+ ```typescript
75
+ import { IncrSearchTreeController } from '../src/incr-search/index.js';
76
+ import type { MillerListElement } from '../src/miller/miller-list.ts';
77
+
78
+ const millerEl = document.querySelector<MillerListElement>('ixfx-miller-list')!;
79
+ const searchEl = document.querySelector<HTMLInputElement>('[data-search]')!;
80
+
81
+ const searchCtrl = new IncrSearchTreeController(millerEl, {
82
+ selector: ({ node }) => node.item.label,
83
+ labelSelector: '.label',
84
+ input: searchEl,
85
+ });
86
+
87
+ searchCtrl.connect();
88
+ ```
89
+
90
+ ## Notes
91
+
92
+ - `createElementIncrSearch` observes child mutations and refreshes automatically.
93
+ - `IncrSearchTreeController` sets `filterPredicate` and highlights matching labels.
94
+ - Call `disconnect()` when tearing down controllers to clear highlights and handlers.
@@ -0,0 +1,204 @@
1
+ # `<ixfx-labelled-radial-input>`
2
+
3
+ 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 editing the number moves the dial.
4
+
5
+ ## Basic usage
6
+
7
+ ```html
8
+ <ixfx-labelled-radial-input label="Volume" value="50" min="0" max="100">
9
+ </ixfx-labelled-radial-input>
10
+ ```
11
+
12
+ ## Layout
13
+
14
+ Layout is driven by a container query on the component's aspect ratio (width / height).
15
+
16
+ | Aspect Ratio (width/height) | Layout |
17
+ |----------------------------|--------------------------------|
18
+ | >= 1.5 (wide) | **Horizontal** — label to the left of dial |
19
+ | < 1.5 (tall/square) | **Vertical** — label above dial |
20
+
21
+ ### Force a layout with `--flow`
22
+
23
+ Override the container query entirely:
24
+
25
+ ```html
26
+ <ixfx-labelled-radial-input style="--flow: row" ...> <!-- always horizontal (label left) -->
27
+ <ixfx-labelled-radial-input style="--flow: column" ...> <!-- always vertical (label above) -->
28
+ ```
29
+
30
+ ## Mini mode
31
+
32
+ When the component is very small the label is hidden and only the dial is shown, with the tooltip forced on.
33
+
34
+ Mini mode activates when **both** of the following are true:
35
+ - `width < mini-width-threshold` (default 120px)
36
+ - `height < mini-height-threshold` (default 45px)
37
+
38
+ Override with a CSS variable:
39
+
40
+ ```html
41
+ <ixfx-labelled-radial-input style="--mini: 1" ...> <!-- always mini -->
42
+ <ixfx-labelled-radial-input style="--mini: 0" ...> <!-- never mini -->
43
+ ```
44
+
45
+ ## Formatter
46
+
47
+ Apply a function to format the displayed value. Set it as a JS property — it cannot be set as an HTML attribute.
48
+
49
+ ```js
50
+ const el = document.querySelector('ixfx-labelled-radial-input');
51
+ el.formatter = (v) => `${Math.round(Number(v))}%`;
52
+ ```
53
+
54
+ The formatter receives the raw string value and returns the string to display.
55
+
56
+ ## Properties / attributes
57
+
58
+ | Attribute | Type | Default | Description |
59
+ |--------------------------|------------------------------------------------|--------------|-----------------------------------------------------|
60
+ | `label` | `string` | `""` | Label text shown in the editable-number |
61
+ | `value` | `number` | `0` | Current value |
62
+ | `min` | `number` | `0` | Minimum value |
63
+ | `max` | `number` | `100` | Maximum value |
64
+ | `step` | `number` | `1` | Step increment |
65
+ | `drag-sensitivity` | `number` | `1` | Steps per pixel when dragging the number label |
66
+ | `precision` | `number` | `0` | Decimal places shown |
67
+ | `radius` | `number` (10-50) | `45` | Dial size |
68
+ | `flatten` | `number` (0-1) | `0.3` | How much circle to hide |
69
+ | `marker-style` | `"needle" \| "dot" \| "none"` | `"needle"` | Marker type |
70
+ | `wedge` | `boolean` | `false` | Show filled wedge sector |
71
+ | `value-mode` | `"range" \| "bipolar" \| "unipolar"` | `"range"` | Value display mode |
72
+ | `tooltip` | `boolean` | `false` | Show dial tooltip |
73
+ | `aspect-ratio-threshold` | `number` | `1.5` | Width/height ratio to trigger horizontal layout |
74
+ | `mini-width-threshold` | `number` | `120` | Width (px) below which mini mode can activate |
75
+ | `mini-height-threshold` | `number` | `45` | Height (px) below which mini mode can activate |
76
+ | `display-text-logic` | `"hidden" \| "show" \| "override" \| "transient"` | `"transient"` | Controls when label text vs value is shown |
77
+ | `formatter` | `(value: string) => string` | — | JS property only — formats value for display |
78
+
79
+ ### `display-text-logic` values
80
+
81
+ | Value | Behaviour |
82
+ |-------------|--------------------------------------------------------|
83
+ | `transient` | Shows the label when idle, value on hover/interaction |
84
+ | `show` | Always shows the value |
85
+ | `hidden` | Always shows the label text, never the value |
86
+ | `override` | Label text replaces value at all times |
87
+
88
+ ## Events
89
+
90
+ | Event | Detail | Description |
91
+ |----------|---------------------------|----------------------------------------------------------|
92
+ | `change` | `{ value: string }` | Fired when the value is committed (dial release or number edit) |
93
+ | `input` | `{ value: number }` | Fired on every dial interaction |
94
+
95
+ Both events bubble and are composed (cross shadow boundary).
96
+
97
+ ## CSS variables
98
+
99
+ ### Layout
100
+
101
+ | Variable | Default | Description |
102
+ |-------------|--------------|-----------------------------------------------------------|
103
+ | `--height` | `auto` | Component height |
104
+ | `--width` | `auto` | Component width |
105
+ | `--min-height` | `60px` | Minimum height |
106
+ | `--min-width` | `60px` | Minimum width |
107
+ | `--flow` | *(container query)* | `row` or `column` — overrides the automatic layout |
108
+ | `--mini` | *(auto)* | `1` to force mini mode, `0` to disable it |
109
+
110
+ ### Label (passed to `ixfx-editable-number`)
111
+
112
+ | Variable | Default | Description |
113
+ |-----------------------|----------------------------|--------------------------|
114
+ | `--label-font-size` | `var(--text-m)` | Font size |
115
+ | `--label-font-family` | `var(--font-family)` | Font family |
116
+ | `--label-color` | `var(--surface-text)` | Text colour |
117
+ | `--label-muted-color` | `var(--surface-muted-text)`| Muted text colour |
118
+ | `--label-padding` | `var(--space-xs) var(--space-s)` | Padding |
119
+ | `--label-radius` | `var(--radius-s)` | Border radius |
120
+ | `--label-align` | `center` | Text alignment |
121
+
122
+ ### Dial (passed to `ixfx-radial-input`)
123
+
124
+ | Variable | Default | Description |
125
+ |-----------------------------|--------------|------------------------------------|
126
+ | `--dial-border-width` | — | Dial border width |
127
+ | `--dial-border-color` | — | Dial border colour |
128
+ | `--dial-fill` | — | Dial fill colour |
129
+ | `--radius` | `45` | Dial radius |
130
+ | `--marker-color` | — | Marker colour |
131
+ | `--marker-thickness` | — | Marker thickness |
132
+ | `--marker-length` | — | Marker length |
133
+ | `--marker-offset` | — | Marker offset from dial edge |
134
+ | `--marker-border-width` | — | Marker border width |
135
+ | `--marker-border-color` | — | Marker border colour |
136
+ | `--midpoint-color` | — | Midpoint colour (bipolar mode) |
137
+ | `--midpoint-thickness` | — | Midpoint thickness |
138
+ | `--midpoint-length` | — | Midpoint length |
139
+ | `--midpoint-offset` | — | Midpoint offset |
140
+ | `--lane-width` | — | Lane width |
141
+ | `--lane-color` | — | Lane colour |
142
+ | `--lane-border-width` | — | Lane border width |
143
+ | `--lane-offset` | — | Lane offset |
144
+ | `--lane-dasharray` | — | Lane dash array |
145
+ | `--wedge-fill` | — | Wedge fill colour |
146
+ | `--wedge-fill-opacity` | — | Wedge fill opacity |
147
+ | `--wedge-border-width` | — | Wedge border width |
148
+ | `--wedge-border-color` | — | Wedge border colour |
149
+
150
+ ## Examples
151
+
152
+ ### Horizontal layout (wide container)
153
+
154
+ ```html
155
+ <ixfx-labelled-radial-input
156
+ id="my-dial"
157
+ label="Mix"
158
+ value="75"
159
+ style="width: 200px; height: 80px;"
160
+ ></ixfx-labelled-radial-input>
161
+ ```
162
+
163
+ ### Custom dial colours
164
+
165
+ ```html
166
+ <ixfx-labelled-radial-input
167
+ label="Intensity"
168
+ value="70"
169
+ style="
170
+ --dial-fill: hsl(200 80% 40%);
171
+ --marker-color: white;
172
+ "
173
+ ></ixfx-labelled-radial-input>
174
+ ```
175
+
176
+ ### Wedge mode
177
+
178
+ ```html
179
+ <ixfx-labelled-radial-input
180
+ label="Mix"
181
+ value="50"
182
+ wedge
183
+ style="--wedge-fill: hsl(260 80% 50%);"
184
+ ></ixfx-labelled-radial-input>
185
+ ```
186
+
187
+ ### Force mini mode
188
+
189
+ ```html
190
+ <ixfx-labelled-radial-input label="Vol" value="50" style="--mini: 1">
191
+ </ixfx-labelled-radial-input>
192
+ ```
193
+
194
+ ### Listen for changes
195
+
196
+ ```js
197
+ const el = document.querySelector('ixfx-labelled-radial-input');
198
+
199
+ // Committed value (dial release or number edit)
200
+ el.addEventListener('change', (e) => console.log(e.detail.value));
201
+
202
+ // Every dial interaction
203
+ el.addEventListener('input', (e) => console.log(e.detail.value));
204
+ ```
@@ -0,0 +1,194 @@
1
+ # `<ixfx-labelled-range-input>`
2
+
3
+ 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 editing the number moves the slider.
4
+
5
+ ## Basic usage
6
+
7
+ ```html
8
+ <ixfx-labelled-range-input label="Volume" value="0.5" min="0" max="1" step="0.01">
9
+ </ixfx-labelled-range-input>
10
+ ```
11
+
12
+ ## Layout
13
+
14
+ Layout is driven by a container query on the component's own height.
15
+
16
+ | Height | Layout |
17
+ |-----------------|---------------------------------|
18
+ | >= 80px (default) | **Vertical** — label above slider |
19
+ | < 80px | **Horizontal** — label beside slider |
20
+
21
+ The component defaults to `height: 100px`, so unconstrained usage shows the vertical layout. Constrain the height in your CSS to switch to horizontal.
22
+
23
+ ### Force a layout with `--flow`
24
+
25
+ Override the container query entirely:
26
+
27
+ ```html
28
+ <ixfx-labelled-range-input style="--flow: row" ...> <!-- always horizontal -->
29
+ <ixfx-labelled-range-input style="--flow: column" ...> <!-- always vertical -->
30
+ ```
31
+
32
+ ### Control label width (horizontal layout)
33
+
34
+ ```html
35
+ <ixfx-labelled-range-input style="--label-width: 8em" ...>
36
+ ```
37
+
38
+ ## Mini mode
39
+
40
+ When the component is very small the label is hidden and only the slider is shown, with the tooltip forced on.
41
+
42
+ Mini mode activates when **both** of the following are true:
43
+ - `width < mini-width-threshold` (default 120px)
44
+ - `height < mini-height-threshold` (default 40px)
45
+
46
+ Override with a CSS variable:
47
+
48
+ ```html
49
+ <ixfx-labelled-range-input style="--mini: 1" ...> <!-- always mini -->
50
+ <ixfx-labelled-range-input style="--mini: 0" ...> <!-- never mini -->
51
+ ```
52
+
53
+ ## Formatter
54
+
55
+ Apply a function to format the displayed value and tooltip. Set it as a JS property — it cannot be set as an HTML attribute.
56
+
57
+ ```js
58
+ const el = document.querySelector('ixfx-labelled-range-input');
59
+ el.formatter = (v) => `${Math.round(Number(v))}%`;
60
+ ```
61
+
62
+ The formatter receives the raw string value and returns the string to display. When set, it applies to both the editable-number label and the slider tooltip.
63
+
64
+ ## Properties / attributes
65
+
66
+ | Attribute | Type | Default | Description |
67
+ |------------------------|-----------------------------------------------|--------------|-----------------------------------------------------|
68
+ | `label` | `string` | `""` | Label text shown in the editable-number |
69
+ | `value` | `number` | `0` | Current value |
70
+ | `min` | `number` | `0` | Minimum value |
71
+ | `max` | `number` | `1` | Maximum value |
72
+ | `step` | `number` | `0.01` | Step increment |
73
+ | `drag-sensitivity` | `number` | `1` | Steps per pixel when dragging the number label |
74
+ | `precision` | `number` | `0` | Decimal places shown |
75
+ | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Slider track orientation |
76
+ | `tooltip` | `boolean` | `false` | Show slider tooltip |
77
+ | `tooltip-orientation` | `"top" \| "right"` | `"top"` | Tooltip position relative to the slider thumb |
78
+ | `switch-height` | `number` | `80` | Height (px) at which layout switches vertical↔horizontal |
79
+ | `mini-width-threshold` | `number` | `120` | Width (px) below which mini mode can activate |
80
+ | `mini-height-threshold`| `number` | `40` | Height (px) below which mini mode can activate |
81
+ | `display-text-logic` | `"hidden" \| "show" \| "override" \| "transient"` | `"transient"` | Controls when label text vs value is shown |
82
+ | `formatter` | `(value: string) => string` | — | JS property only — formats value for display and tooltip |
83
+
84
+ ### `display-text-logic` values
85
+
86
+ | Value | Behaviour |
87
+ |-------------|--------------------------------------------------------|
88
+ | `transient` | Shows the label when idle, value on hover/interaction |
89
+ | `show` | Always shows the value |
90
+ | `hidden` | Always shows the label text, never the value |
91
+ | `override` | Label text replaces value at all times |
92
+
93
+ ## Events
94
+
95
+ | Event | Detail | Description |
96
+ |----------|---------------------------|----------------------------------------------------------|
97
+ | `change` | `{ value: string }` | Fired when the value is committed (slider release or number edit) |
98
+ | `input` | `{ value: number }` | Fired on every slider move |
99
+
100
+ Both events bubble and are composed (cross shadow boundary).
101
+
102
+ ## CSS variables
103
+
104
+ ### Layout
105
+
106
+ | Variable | Default | Description |
107
+ |--------------------------|--------------|-----------------------------------------------------------|
108
+ | `--height` | `100px` | Component height. Set to `100%` to fill a constrained parent |
109
+ | `--width` | `auto` | Component width |
110
+ | `--min-height` | `20px` | Minimum height |
111
+ | `--min-width` | `50px` | Minimum width |
112
+ | `--flow` | *(container query)* | `row` or `column` — overrides the automatic layout |
113
+ | `--label-width` | `5ch` | Width of the editable-number in horizontal layout |
114
+ | `--mini` | *(auto)* | `1` to force mini mode, `0` to disable it |
115
+
116
+ ### Label (passed to `ixfx-editable-number`)
117
+
118
+ | Variable | Default | Description |
119
+ |-----------------------|----------------------------|--------------------------|
120
+ | `--label-font-size` | `var(--text-m)` | Font size |
121
+ | `--label-font-family` | `var(--font-family)` | Font family |
122
+ | `--label-color` | `var(--surface-text)` | Text colour |
123
+ | `--label-muted-color` | `var(--surface-muted-text)`| Muted text colour |
124
+ | `--label-padding` | `var(--space-xs) var(--space-s)` | Padding |
125
+ | `--label-radius` | `var(--radius-s)` | Border radius |
126
+ | `--label-align` | `center` | Text alignment |
127
+
128
+ ### Slider track (passed to `ixfx-range-input`)
129
+
130
+ | Variable | Default | Description |
131
+ |-----------------------------------|--------------------------|------------------------------------------|
132
+ | `--track-thickness` | `1.5em` | Track cross-axis size |
133
+ | `--thumb-color` | `var(--surface-text)` | Thumb colour |
134
+ | `--thumb-color-active` | `var(--accent)` | Thumb colour while active |
135
+ | `--thumb-size` | `var(--track-thickness)` | Thumb size along the track axis |
136
+ | `--thumb-thickness` | `2em` | Thumb size on the cross axis |
137
+ | `--thumb-radius` | `0` | Thumb border radius |
138
+ | `--thumb-border` | `none` | Thumb border |
139
+ | `--thumb-shadow` | `none` | Thumb box shadow |
140
+ | `--background-track` | `var(--surface-ll)` | Track background |
141
+ | `--background-track-hover` | `--background-track` | Track background on hover |
142
+ | `--background-track-value` | `var(--surface-hh)` | Filled (value) portion of the track |
143
+ | `--background-track-value-hover` | `var(--accent)` | Filled portion on hover |
144
+ | `--background-track-value-active` | `var(--accent)` | Filled portion while dragging |
145
+
146
+ ## Examples
147
+
148
+ ### Horizontal layout with percentage formatter
149
+
150
+ ```html
151
+ <ixfx-labelled-range-input
152
+ id="my-slider"
153
+ label="Mix"
154
+ value="0.75"
155
+ style="height: 50px"
156
+ ></ixfx-labelled-range-input>
157
+
158
+ <script type="module">
159
+ document.querySelector('#my-slider').formatter = (v) =>
160
+ `${Math.round(Number(v) * 100)}%`;
161
+ </script>
162
+ ```
163
+
164
+ ### Custom slider colours
165
+
166
+ ```html
167
+ <ixfx-labelled-range-input
168
+ label="Intensity"
169
+ value="0.7"
170
+ style="
171
+ --background-track-value: linear-gradient(to right, hsl(200 80% 40%), hsl(260 80% 50%));
172
+ --thumb-radius: 999px;
173
+ "
174
+ ></ixfx-labelled-range-input>
175
+ ```
176
+
177
+ ### Force mini mode
178
+
179
+ ```html
180
+ <ixfx-labelled-range-input label="Vol" value="0.5" style="--mini: 1">
181
+ </ixfx-labelled-range-input>
182
+ ```
183
+
184
+ ### Listen for changes
185
+
186
+ ```js
187
+ const el = document.querySelector('ixfx-labelled-range-input');
188
+
189
+ // Committed value (slider release or number edit)
190
+ el.addEventListener('change', (e) => console.log(e.detail.value));
191
+
192
+ // Every slider move
193
+ el.addEventListener('input', (e) => console.log(e.detail.value));
194
+ ```
@@ -0,0 +1,40 @@
1
+ # `ixfx-led`
2
+
3
+ An LED indicator component with color mixing and optional blinking animation.
4
+
5
+ ```html
6
+ <ixfx-led></ixfx-led>
7
+ <ixfx-led lit color="oklch(60% 0.2 120)"></ixfx-led>
8
+ <ixfx-led blinking rate="300"></ixfx-led>
9
+ ```
10
+
11
+ ### Properties / attributes
12
+
13
+ | Attribute / Property | Type | Default | Description |
14
+ |----------------------|----------|--------------------------|----------------------------------------|
15
+ | `lit` | `boolean`| `false` | Whether the LED is illuminated |
16
+ | `value` | `number` | `0` | Value 0-1 used for brightness calculation |
17
+ | `color` | `string` | `'oklch(50% 0.15 25)'` | LED color (oklch recommended) |
18
+ | `blinking` | `boolean`| `false` | Enable blinking animation |
19
+ | `rate` | `number` | `500` | Blinking interval in milliseconds |
20
+
21
+ ### CSS custom properties
22
+
23
+ | Property | Default | Description |
24
+ |----------|---------|-------------------|
25
+ | `--size` | `1ex` | LED diameter |
26
+ | `--colour` | `oklch(50% 0.15 25)` | LED colour (overrides `color` attribute) |
27
+
28
+ ### Usage notes
29
+
30
+ The LED brightness is calculated as `color-mix(color, white, (1 - value) * 50%)`. Set `value` to 0 for off and 1 for fully lit.
31
+
32
+ For blinking, setting `rate` below 300ms adjusts the transition duration automatically for smoother animation.
33
+
34
+ ### TypeScript types
35
+
36
+ ```ts
37
+ // LED color is a CSS color string
38
+ led.color = 'oklch(70% 0.15 200)';
39
+ led.value = 0.75; // 75% lit
40
+ ```