@ixfx/components 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/bundle/index.d.ts +1086 -603
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +16153 -15296
  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-9NWTXtE1.js → icon-Ndo40kNO.js} +6 -5
  53. package/dist/icon-Ndo40kNO.js.map +1 -0
  54. package/dist/{icon-Dm_DHDz6.d.ts → icon-epZd-JIO.d.ts} +2 -2
  55. package/dist/icon-epZd-JIO.d.ts.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-DLkSKL4T.d.ts → index-CowKi2mo.d.ts} +1 -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-iZLld8Ca.d.ts → index-JuEVfmr-.d.ts} +2 -2
  73. package/dist/index-JuEVfmr-.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 +2369 -1706
  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-BVvNeb5R.d.ts → labelled-input-base-Cz6qE6Dk.d.ts} +1 -1
  84. package/dist/labelled-input-base-Cz6qE6Dk.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-Bh0PhlKW.js → timeline-2OFtN4Ta.js} +2 -2
  164. package/dist/{timeline-Bh0PhlKW.js.map → timeline-2OFtN4Ta.js.map} +1 -1
  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-Dm_DHDz6.d.ts.map +0 -1
  234. package/dist/index-CnL9krsH.d.ts.map +0 -1
  235. package/dist/index-DLkSKL4T.d.ts.map +0 -1
  236. package/dist/index-DSlPeWLK.d.ts.map +0 -1
  237. package/dist/index-iZLld8Ca.d.ts.map +0 -1
  238. package/dist/interaction-DG7X1rhI.js +0 -1
  239. package/dist/labelled-input-base-BVvNeb5R.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/tree-CvvKbO-f.js.map +0 -1
  250. package/dist/tree-component-D8Bg48tt.d.ts.map +0 -1
  251. package/dist/types-BKaqnpwx.d.ts.map +0 -1
  252. package/dist/xy-axis-D9uwpGTT.d.ts.map +0 -1
  253. package/dist/xy-axis-DL0tfJDr.js.map +0 -1
  254. /package/bundle/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
  255. /package/dist/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
@@ -0,0 +1,351 @@
1
+ # `ixfx-vertical-list`
2
+
3
+ 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.
4
+
5
+ ---
6
+
7
+ ## Contents
8
+
9
+ 1. [Quick start](#quick-start)
10
+ 2. [Item types](#item-types)
11
+ 3. [Properties](#properties)
12
+ 4. [Selection](#selection)
13
+ 5. [Events](#events)
14
+ 6. [Keyboard navigation](#keyboard-navigation)
15
+ 7. [Incremental search](#incremental-search)
16
+ 8. [CSS variables](#css-variables)
17
+ 9. [CSS parts](#css-parts)
18
+
19
+ ---
20
+
21
+ ## Quick start
22
+
23
+ ### Declarative HTML items
24
+
25
+ ```html
26
+ <ixfx-vertical-list id="my-list" selection-mode="single">
27
+ <li>Apple</li>
28
+ <li>Banana</li>
29
+ <li>Cherry</li>
30
+ </ixfx-vertical-list>
31
+
32
+ <script type="module">
33
+ import '@ixfx/components/vertical-list';
34
+
35
+ const list = document.querySelector('#my-list');
36
+ list.addEventListener('list-select', ({ detail }) => {
37
+ const [item] = detail.selected;
38
+ console.log('selected:', item?.textContent);
39
+ });
40
+ </script>
41
+ ```
42
+
43
+ ### Programmatic items
44
+
45
+ ```typescript
46
+ import type { VerticalListElement } from '@ixfx/components';
47
+
48
+ const list = document.querySelector<VerticalListElement>('ixfx-vertical-list')!;
49
+
50
+ list.addItem('Document A');
51
+ list.addItem('Image 01.png');
52
+
53
+ // Or pass an element directly
54
+ const li = document.createElement('li');
55
+ li.textContent = 'Custom item';
56
+ list.addItem(li);
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Item types
62
+
63
+ ### Plain `<li>` elements
64
+
65
+ The simplest item type. Styled directly via `::slotted(li)` CSS from the component's shadow DOM.
66
+
67
+ ```html
68
+ <ixfx-vertical-list>
69
+ <li>Item one</li>
70
+ <li>Item two</li>
71
+ </ixfx-vertical-list>
72
+ ```
73
+
74
+ Searchable text is the element's `textContent`. Override with `data-search-label` for custom search terms:
75
+
76
+ ```html
77
+ <li data-search-label="document a alpha">Document A</li>
78
+ ```
79
+
80
+ ### Custom shadow-DOM components
81
+
82
+ Any element with a `data-list-item` attribute is recognised as a list item. This enables rich card-style rows built with Lit or any other framework:
83
+
84
+ ```html
85
+ <ixfx-vertical-list>
86
+ <my-track-card data-list-item track-title="Bohemian Rhapsody" artist="Queen"></my-track-card>
87
+ </ixfx-vertical-list>
88
+ ```
89
+
90
+ Because the component cannot reach inside a custom element's shadow root, you must expose searchable text via a `data-search-label` attribute on the host element. The recommended pattern is to set this in the custom element's `willUpdate` lifecycle:
91
+
92
+ ```typescript
93
+ override willUpdate(changed: PropertyValues) {
94
+ super.willUpdate(changed);
95
+ if (changed.has('trackTitle') || changed.has('artist')) {
96
+ this.setAttribute('data-search-label', `${this.trackTitle} ${this.artist}`);
97
+ }
98
+ }
99
+ ```
100
+
101
+ To reflect selection state inside the custom element's shadow DOM, target the `:host([data-selected])` CSS selector:
102
+
103
+ ```css
104
+ :host([data-selected]) .title {
105
+ color: var(--item-text-selected, #fff);
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Properties
112
+
113
+ | Property / Attribute | Type | Default | Description |
114
+ |---|---|---|---|
115
+ | `selectionMode` / `selection-mode` | `'none' \| 'single' \| 'multiple'` | `'single'` | Which items can be selected simultaneously |
116
+ | `interactionMode` / `interaction-mode` | `ListInteractionMode` | `'standard'` | How gestures map to selection changes (see table below) |
117
+ | `filterPredicate` | `((el: Element) => boolean) \| undefined` | `undefined` | External filter; only items returning `true` are shown |
118
+
119
+ Switching `interactionMode` always clears the current selection.
120
+
121
+ ### `interactionMode`
122
+
123
+ | Value | Behaviour |
124
+ |---|---|
125
+ | `'implicit'` | Click always replaces selection. No modifier support. |
126
+ | `'standard'` | Plain click replaces selection; Ctrl/Cmd+click toggles; Shift+click range-selects (multiple mode only); Cmd+A selects all |
127
+ | `'vscode'` | Like `standard` for clicks; Shift+Arrow extends the range from the keyboard anchor |
128
+ | `'checked'` | Checkboxes on every `<li>` row; checkbox click (or Space) toggles; body clicks do nothing. Custom elements show a left-border indicator instead of a checkbox |
129
+ | `'manual'` | No automatic selection; drive state entirely via `select()` / `deselect()` / `clearSelection()` |
130
+
131
+ ---
132
+
133
+ ## Selection
134
+
135
+ ### Programmatic API
136
+
137
+ ```typescript
138
+ // Replace selection with a single item
139
+ list.select(element);
140
+
141
+ // Replace selection with multiple items — requires selectionMode="multiple", throws otherwise
142
+ list.selectMany([elementA, elementB, elementC]);
143
+
144
+ // Remove one item from the selection
145
+ list.deselect(element);
146
+
147
+ // Clear entire selection
148
+ list.clearSelection();
149
+
150
+ // Select all visible items (only works when multiple selection is effective)
151
+ list.selectAll();
152
+ ```
153
+
154
+ ### Adding and removing items
155
+
156
+ ```typescript
157
+ // Append a string (wrapped in <li>) or an Element
158
+ list.addItem('New item');
159
+ list.addItem(myElement);
160
+
161
+ // Remove a specific element
162
+ list.removeItem(element);
163
+
164
+ // Remove all items and clear selection
165
+ list.clearItems();
166
+ ```
167
+
168
+ ### Reading the selection
169
+
170
+ ```typescript
171
+ // Most recently selected item (primary selection)
172
+ const item = list.selectedItem;
173
+ if (item) console.log(item.textContent?.trim());
174
+
175
+ // Full selection set
176
+ for (const el of list.selectedItems) {
177
+ console.log(el.textContent?.trim());
178
+ }
179
+
180
+ // Via event
181
+ list.addEventListener('list-select', ({ detail }) => {
182
+ const { selected, previous } = detail;
183
+ for (const el of selected) {
184
+ console.log(el.textContent?.trim());
185
+ }
186
+ });
187
+ ```
188
+
189
+ ### Attribute hooks on items
190
+
191
+ The component reflects state onto list items as data attributes. Use these in CSS or to read state from JavaScript:
192
+
193
+ | Attribute | Set when |
194
+ |---|---|
195
+ | `data-tickled` | The keyboard/pointer cursor is on this item |
196
+ | `data-selected` | The item is in the current selection |
197
+ | `data-checked` | Set in `checked` mode; mirrors `data-selected` |
198
+
199
+ ---
200
+
201
+ ## Events
202
+
203
+ All events **bubble** and are **composed** (they cross shadow-DOM boundaries).
204
+
205
+ | Event | Detail type | When fired |
206
+ |---|---|---|
207
+ | `list-select` | `ListSelectDetail` | Selection changes by any means |
208
+ | `list-tickle` | `ListTickleDetail` | An item enters cursor focus (hover or keyboard navigation) |
209
+ | `list-activate` | `ListActivateDetail` | User explicitly activates an item: Enter key or double-click |
210
+ | `list-item-click` | `ListItemClickDetail` | Raw click or Enter on any item |
211
+
212
+ ### Detail types
213
+
214
+ ```typescript
215
+ type ListSelectDetail = {
216
+ readonly selected: ReadonlySet<Element>; // Current full selection
217
+ readonly previous: ReadonlySet<Element>; // Selection before this change
218
+ };
219
+
220
+ type ListTickleDetail = { readonly item: Element };
221
+ type ListActivateDetail = { readonly item: Element };
222
+ type ListItemClickDetail = { readonly item: Element };
223
+ ```
224
+
225
+ ### Usage example
226
+
227
+ ```typescript
228
+ list.addEventListener('list-select', ({ detail }) => {
229
+ const labels = [...detail.selected]
230
+ .map(el => el.getAttribute('data-search-label') ?? el.textContent?.trim())
231
+ .join(', ');
232
+ console.log('selection:', labels);
233
+ });
234
+
235
+ list.addEventListener('list-tickle', ({ detail }) => {
236
+ previewPane.show(detail.item);
237
+ });
238
+
239
+ list.addEventListener('list-activate', ({ detail }) => {
240
+ router.navigate(detail.item.dataset.key);
241
+ });
242
+
243
+ list.addEventListener('list-item-click', ({ detail }) => {
244
+ analytics.track('list-click', detail.item.dataset.key);
245
+ });
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Keyboard navigation
251
+
252
+ The element must be focused (click it or tab to it) before keyboard navigation works. A visible focus ring appears on focus.
253
+
254
+ | Key | Action |
255
+ |---|---|
256
+ | `Arrow Down` | Move cursor to next visible item |
257
+ | `Arrow Up` | Move cursor to previous visible item |
258
+ | `Enter` | Activate the cursor item — fires `list-item-click` then `list-activate`; selects the item (except in `manual` mode) |
259
+ | `Space` | Toggle selection on the cursor item |
260
+ | `Shift+Arrow Down/Up` | Extend range from anchor (`vscode` mode only) |
261
+ | `Ctrl/Cmd+A` | Select all visible items (when effective multiple selection is on) |
262
+ | `Ctrl/Cmd+F` | Open the incremental search overlay |
263
+ | `Escape` | Clear the current selection (or close the search overlay if open) |
264
+
265
+ ### While the search overlay is open
266
+
267
+ | Key | Action |
268
+ |---|---|
269
+ | `Arrow Down / Up` | Move cursor through the filtered list |
270
+ | `Enter` | Activate the cursor item (or first visible item) and close search |
271
+ | `Escape` | Close search overlay |
272
+
273
+ ---
274
+
275
+ ## Incremental search
276
+
277
+ Press **Ctrl+F** (or **Cmd+F**) to open the search overlay. Typing filters items in real time using fuzzy matching. Matched text in plain `<li>` items is highlighted via the [CSS Highlight API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API).
278
+
279
+ ### Adding the page-level highlight rule
280
+
281
+ The CSS Highlight API requires a `::highlight()` rule in the page stylesheet to make matches visible. Add this to your page:
282
+
283
+ ```css
284
+ ::highlight(vertical-list-search) {
285
+ background: var(--accent);
286
+ color: var(--accent-text, #fff);
287
+ }
288
+ ```
289
+
290
+ Without this rule the filtering still works (non-matching items are hidden) but matched text inside `<li>` items will not be highlighted.
291
+
292
+ ### Searchable text
293
+
294
+ | Item type | Default search text | Override |
295
+ |---|---|---|
296
+ | `<li>` | `textContent` | `data-search-label` attribute |
297
+ | `[data-list-item]` custom element | `data-search-label` attribute | — |
298
+
299
+ Custom elements must always set `data-search-label` since the component cannot read their shadow DOM content.
300
+
301
+ ### External `filterPredicate`
302
+
303
+ Supply a custom filter function to hide items independently of the search overlay:
304
+
305
+ ```typescript
306
+ // Show only items tagged as 'document'
307
+ list.filterPredicate = (el) => el.dataset.type === 'document';
308
+
309
+ // Remove the filter
310
+ list.filterPredicate = undefined;
311
+ ```
312
+
313
+ ---
314
+
315
+ ## CSS variables
316
+
317
+ | Variable | Default | Description |
318
+ |---|---|---|
319
+ | `--list-bg` | `var(--surface-3)` | List background |
320
+ | `--list-text` | `var(--surface-3-text)` | Default text colour |
321
+ | `--list-border` | `var(--border)` | Border colour |
322
+ | `--list-radius` | `var(--radius-s)` | Border radius |
323
+ | `--list-item-height` | `28px` | Minimum row height for `<li>` items |
324
+ | `--list-item-padding` | `4px var(--space-m, 8px)` | Padding for `<li>` items |
325
+ | `--item-bg-tickled` | `rgba(0,0,0,0.06)` | Row background when cursor is on it |
326
+ | `--item-bg-selected` | `rgba(25,118,210,1)` | Selected row background |
327
+ | `--item-text-selected` | `#fff` | Selected row text colour |
328
+ | `--item-bg-selected-tickled` | `rgba(21,101,192,1)` | Selected + cursor row background |
329
+ | `--accent` | — | Accent colour (focus ring, search highlight, checkbox) |
330
+ | `--transition` | `0.1s ease-out` | Transition timing for hover/selection changes |
331
+
332
+ ---
333
+
334
+ ## CSS parts
335
+
336
+ | Part | Element |
337
+ |---|---|
338
+ | `list` | The `<ul>` scroll container |
339
+ | `search-overlay` | The floating search input overlay |
340
+
341
+ Use `::part()` to style these from outside the component's shadow DOM:
342
+
343
+ ```css
344
+ ixfx-vertical-list::part(list) {
345
+ padding: 4px 0;
346
+ }
347
+
348
+ ixfx-vertical-list::part(search-overlay) {
349
+ border-radius: 8px;
350
+ }
351
+ ```
@@ -0,0 +1,94 @@
1
+ # `ixfx-xy-pad`
2
+
3
+ A 2D touch-friendly XY pad for controlling two parameters simultaneously. Each axis supports independent linear/logarithmic scaling.
4
+
5
+ ```html
6
+ <ixfx-xy-pad></ixfx-xy-pad>
7
+ <ixfx-xy-pad x-min="0" x-max="1" y-min="0" y-max="1" x-label="Volume" y-label="Pan"></ixfx-xy-pad>
8
+ ```
9
+
10
+ ```js
11
+ const pad = document.querySelector('ixfx-xy-pad');
12
+ pad.xMin = 0;
13
+ pad.xMax = 100;
14
+ pad.yMin = 0;
15
+ pad.yMax = 100;
16
+ pad.xScale = 'linear';
17
+ pad.yScale = 'log'; // logarithmic scaling for y axis
18
+
19
+ pad.addEventListener('input', e => {
20
+ console.log(`x: ${e.detail.x.toFixed(2)}, y: ${e.detail.y.toFixed(2)}`);
21
+ });
22
+
23
+ pad.addEventListener('change', e => {
24
+ console.log('drag ended:', e.detail);
25
+ });
26
+ ```
27
+
28
+ ### Properties / attributes
29
+
30
+ | Attribute / Property | Type | Default | Description |
31
+ |-----------------------|------------------------|------------|----------------------------------------------|
32
+ | `xMin` | `number` | `1` | Minimum X value |
33
+ | `xMax` | `number` | `100` | Maximum X value |
34
+ | `x` | `number` | `50` | Current X value |
35
+ | `yMin` | `number` | `1` | Minimum Y value |
36
+ | `yMax` | `number` | `100` | Maximum Y value |
37
+ | `y` | `number` | `50` | Current Y value |
38
+ | `xScale` | `'linear' \| 'log'` | `'linear'` | X axis scaling |
39
+ | `yScale` | `'linear' \| 'log'` | `'linear'` | Y axis scaling |
40
+ | `xSnapStep` | `number \| undefined` | `undefined`| X axis snap granularity (`x-snap-step`) |
41
+ | `ySnapStep` | `number \| undefined` | `undefined`| Y axis snap granularity (`y-snap-step`) |
42
+ | `xLabel` | `string` | `'X'` | X axis label text |
43
+ | `yLabel` | `string` | `'Y'` | Y axis label text |
44
+ | `showCrosshairs` | `boolean` | `false` | Show crosshair lines |
45
+ | `showGrid` | `boolean` | `false` | Show background grid |
46
+ | `hideCursor` | `boolean` | `false` | Hide cursor during drag |
47
+ | `disabled` | `boolean` | `false` | Disable all interaction |
48
+ | `stops` | `number \| undefined` | `undefined`| Quantized stops for both axes |
49
+ | `stopsX` | `number \| undefined` | `undefined`| Quantized stops for X axis only |
50
+ | `stopsY` | `number \| undefined` | `undefined`| Quantized stops for Y axis only |
51
+
52
+ ### Events
53
+
54
+ | Event | Detail | Description |
55
+ |----------|---------------------------|----------------------------------------|
56
+ | `input` | `{ x: number, y: number }` | Continuously during drag |
57
+ | `change` | `{ x: number, y: number }` | When drag ends (pointer release) |
58
+
59
+ ### Methods
60
+
61
+ | Method | Returns | Description |
62
+ |------------------|------------------------------|--------------------------------------------------|
63
+ | `getRelativeX()` | `number` (0–1) | X position as fraction |
64
+ | `getRelativeY()` | `number` (0–1) | Y position as fraction |
65
+ | `getPolar()` | `{ radius: number, angle: number }` | Polar coordinates (radius 0–1, angle 0–2π radians, 0 = right, clockwise) |
66
+
67
+ ### Keyboard navigation
68
+
69
+ Arrow keys move the position. Hold Shift for finer control (step divisor).
70
+
71
+ ### CSS custom properties
72
+
73
+ | Property | Default | Description |
74
+ |-----------------------|----------------------|---------------------------------------|
75
+ | `--marker-size` | `10cqb` | Position marker diameter |
76
+ | `--crosshair-color` | `var(--surface-6)` | Crosshair line color |
77
+ | `--crosshair-width` | `2px` | Crosshair line thickness |
78
+ | `--crosshair-x-color` | `var(--crosshair-color)` | Vertical crosshair color |
79
+ | `--crosshair-x-width` | `var(--crosshair-width)` | Vertical crosshair thickness |
80
+ | `--crosshair-y-color` | `var(--crosshair-color)` | Horizontal crosshair color |
81
+ | `--crosshair-y-width` | `var(--crosshair-width)` | Horizontal crosshair thickness |
82
+ | `--grid-color` | `var(--surface-6)` | Grid line color |
83
+ | `--grid-width` | `1px` | Grid line thickness |
84
+ | `--xy-pad-bg` | `var(--surface-3)` | Pad background colour |
85
+
86
+ ### CSS properties set on host (for external use)
87
+
88
+ The component sets these custom properties on its host element during drag:
89
+
90
+ | Property | Description |
91
+ |------------|----------------------------------------------|
92
+ | `--x-pct` | X position as 0–100 percentage |
93
+ | `--y-pct` | Y position as 0–100 percentage |
94
+ | `--radius-pct` | Distance from centre as 0–100 percentage |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
3
  "type": "module",
4
- "version": "0.3.2",
4
+ "version": "0.4.1",
5
5
  "description": "",
6
6
  "author": "",
7
7
  "license": "ISC",
@@ -19,7 +19,7 @@
19
19
  "types": "./dist/incr-search.d.ts",
20
20
  "import": "./dist/incr-search.js"
21
21
  },
22
- "./*": {
22
+ "./*.js": {
23
23
  "types": "./dist/*.d.ts",
24
24
  "import": "./dist/*.js"
25
25
  }
@@ -27,7 +27,8 @@
27
27
  "main": "dist/index.js",
28
28
  "files": [
29
29
  "bundle",
30
- "dist"
30
+ "dist",
31
+ "docs-user"
31
32
  ],
32
33
  "scripts": {
33
34
  "typecheck": "tsc --noEmit",
@@ -48,23 +49,23 @@
48
49
  "devDependencies": {
49
50
  "@antfu/eslint-config": "^9.0.0",
50
51
  "@clinth/vcanvas": "0.0.3",
51
- "@ixfx/arrays": "0.56.12",
52
- "@ixfx/collections": "0.56.12",
53
- "@ixfx/core": "0.56.12",
54
- "@ixfx/dom": "0.56.12",
55
- "@ixfx/flow": "0.56.12",
56
- "@ixfx/geometry": "0.56.12",
57
- "@ixfx/modulation": "0.56.12",
58
- "@ixfx/numbers": "0.56.12",
59
- "@ixfx/visual": "0.56.12",
60
- "@tsdown/css": "0.22.0",
61
- "eslint": "10.4.0",
52
+ "@ixfx/arrays": "0.56.13",
53
+ "@ixfx/collections": "0.56.13",
54
+ "@ixfx/core": "0.56.13",
55
+ "@ixfx/dom": "0.56.13",
56
+ "@ixfx/flow": "0.56.13",
57
+ "@ixfx/geometry": "0.56.13",
58
+ "@ixfx/modulation": "0.56.13",
59
+ "@ixfx/numbers": "0.56.13",
60
+ "@ixfx/visual": "0.56.13",
61
+ "@tsdown/css": "0.22.1",
62
+ "eslint": "10.4.1",
62
63
  "eslint-plugin-format": "2.0.1",
63
64
  "globals": "17.6.0",
64
- "knip": "6.14.1",
65
- "tsdown": "0.22.0",
65
+ "knip": "6.14.2",
66
+ "tsdown": "0.22.1",
66
67
  "typescript": "6.0.3",
67
- "typescript-eslint": "8.59.3",
68
- "vite": "8.0.13"
68
+ "typescript-eslint": "8.60.0",
69
+ "vite": "8.0.14"
69
70
  }
70
71
  }