@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.
- package/bundle/index.d.ts +1086 -603
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +16527 -14258
- package/bundle/index.js.map +1 -1
- package/bundle/style.css +14 -4
- package/dist/ac-text.d.ts.map +1 -1
- package/dist/ac-text.js +11 -6
- package/dist/ac-text.js.map +1 -1
- package/dist/{button-DMt5XWUK.js → button-C3fLMKUv.js} +4 -4
- package/dist/{button-DMt5XWUK.js.map → button-C3fLMKUv.js.map} +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/checkbox.d.ts +2 -2
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +14 -8
- package/dist/checkbox.js.map +1 -1
- package/dist/{colour-C3MQIjFJ-BLXg8W3U.js → colour-C3MQIjFJ-vRCdr8W-.js} +146 -552
- package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +1 -0
- package/dist/{colour-picker-K6zu82lm.js → colour-picker-0sgAha1T.js} +14 -9
- package/dist/colour-picker-0sgAha1T.js.map +1 -0
- package/dist/colour-picker.d.ts +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +11 -6
- package/dist/crumbs.js.map +1 -1
- package/dist/data-display.d.ts +9 -4
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +270 -43
- package/dist/data-display.js.map +1 -1
- package/dist/data-provider-DgI909FP.js +23 -0
- package/dist/data-provider-DgI909FP.js.map +1 -0
- package/dist/{decorate-D7rC1gLP.js → decorate-DdjvVHS-.js} +2 -2
- package/dist/{decorate-D7rC1gLP.js.map → decorate-DdjvVHS-.js.map} +1 -1
- package/dist/{defaults-C7elhuyJ.js → defaults-C3b9OWJD.js} +2 -2
- package/dist/{defaults-C7elhuyJ.js.map → defaults-C3b9OWJD.js.map} +1 -1
- package/dist/{dist-TAGYwaju.js → dist-CA13FvMn.js} +7 -66
- package/dist/dist-CA13FvMn.js.map +1 -0
- package/dist/editable-label.d.ts.map +1 -1
- package/dist/editable-label.js +3 -2
- package/dist/editable-label.js.map +1 -1
- package/dist/fallbacks-pNMOMx38.js +168 -0
- package/dist/fallbacks-pNMOMx38.js.map +1 -0
- package/dist/{hex-editor-DlfoA65y.d.ts → hex-editor-CMIxtNgT.d.ts} +1 -1
- package/dist/hex-editor-CMIxtNgT.d.ts.map +1 -0
- package/dist/{hex-editor-CsN_ySkH.js → hex-editor-WZrjRtvL.js} +3 -3
- package/dist/{hex-editor-CsN_ySkH.js.map → hex-editor-WZrjRtvL.js.map} +1 -1
- package/dist/hex.d.ts +1 -1
- package/dist/hex.js +1 -1
- package/dist/{highlight-TPbSHruI.js → highlight-B-eQrhhb.js} +1 -1
- package/dist/{highlight-TPbSHruI.js.map → highlight-B-eQrhhb.js.map} +1 -1
- package/dist/{icon-CeTDJhC7.d.ts → icon-Bd5BiU4b.d.ts} +2 -2
- package/dist/icon-Bd5BiU4b.d.ts.map +1 -0
- package/dist/{icon-9NWTXtE1.js → icon-Ndo40kNO.js} +6 -5
- package/dist/icon-Ndo40kNO.js.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +3 -3
- package/dist/{incr-search-DSKKsk6w.js → incr-search-CeERfijK.js} +2 -2
- package/dist/{incr-search-DSKKsk6w.js.map → incr-search-CeERfijK.js.map} +1 -1
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +2 -2
- package/dist/{index-hXFn14tM.d.ts → index-BMjri2-S.d.ts} +2 -2
- package/dist/{index-hXFn14tM.d.ts.map → index-BMjri2-S.d.ts.map} +1 -1
- package/dist/{index-DSlPeWLK.d.ts → index-CWpNcQKE.d.ts} +5 -5
- package/dist/index-CWpNcQKE.d.ts.map +1 -0
- package/dist/{index--hrxPBkQ.d.ts → index-CowKi2mo.d.ts} +2 -1
- package/dist/index-CowKi2mo.d.ts.map +1 -0
- package/dist/{index-B1hNR7Z9.d.ts → index-D87kRcEs.d.ts} +1 -1
- package/dist/{index-B1hNR7Z9.d.ts.map → index-D87kRcEs.d.ts.map} +1 -1
- package/dist/{index-CnL9krsH.d.ts → index-DgXpCg_G.d.ts} +32 -26
- package/dist/index-DgXpCg_G.d.ts.map +1 -0
- package/dist/{index-DrQjA8Gs.d.ts → index-s_IhSnwc.d.ts} +2 -2
- package/dist/index-s_IhSnwc.d.ts.map +1 -0
- package/dist/index.d.ts +432 -121
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2370 -1707
- package/dist/index.js.map +1 -1
- package/dist/interaction-D6efrBLo.js +1 -0
- package/dist/{keyboard-De_vOHLg.js → keyboard-CimiEskD.js} +1 -1
- package/dist/{keyboard-De_vOHLg.js.map → keyboard-CimiEskD.js.map} +1 -1
- package/dist/{labelled-input-base-CqPL5rKm.js → labelled-input-base-9K_w3oLj.js} +2 -2
- package/dist/{labelled-input-base-CqPL5rKm.js.map → labelled-input-base-9K_w3oLj.js.map} +1 -1
- package/dist/{labelled-input-base-D-Y7agky.d.ts → labelled-input-base-ORb1rGq6.d.ts} +1 -1
- package/dist/labelled-input-base-ORb1rGq6.d.ts.map +1 -0
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-radial-input.js +7 -6
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-range-input.js +2 -2
- package/dist/led.d.ts +1 -1
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +2 -2
- package/dist/led.js.map +1 -1
- package/dist/{menu-item-CKRfP0pN.js → menu-item-BrIuORYD.js} +20 -10
- package/dist/menu-item-BrIuORYD.js.map +1 -0
- package/dist/{menu-item-ClQF2DKN.d.ts → menu-item-COx-Azgj.d.ts} +1 -1
- package/dist/menu-item-COx-Azgj.d.ts.map +1 -0
- package/dist/{menu-DJJuCm-5.js → menu-vjoiHZ13.js} +30 -17
- package/dist/menu-vjoiHZ13.js.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +2 -2
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +18 -11
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.d.ts.map +1 -1
- package/dist/narrowed-text.js +7 -5
- package/dist/narrowed-text.js.map +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +5 -4
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +2 -2
- package/dist/plots.js +2 -2
- package/dist/polar-pad.d.ts +1 -1
- package/dist/polar-pad.d.ts.map +1 -1
- package/dist/polar-pad.js +10 -4
- package/dist/polar-pad.js.map +1 -1
- package/dist/{popup-ImtS9KnS.js → popup-DiWMxsWj.js} +1 -1
- package/dist/{popup-ImtS9KnS.js.map → popup-DiWMxsWj.js.map} +1 -1
- package/dist/prominence-DlFShic3.js +104 -0
- package/dist/prominence-DlFShic3.js.map +1 -0
- package/dist/{radial-input-LUSBEycd.d.ts → radial-input-CpQPs1Mp.d.ts} +1 -1
- package/dist/radial-input-CpQPs1Mp.d.ts.map +1 -0
- package/dist/{radial-input-DZA6WaFu.js → radial-input-DZ7eQCcM.js} +11 -6
- package/dist/radial-input-DZ7eQCcM.js.map +1 -0
- package/dist/radial-input.d.ts +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.d.ts +1 -1
- package/dist/range-input.d.ts.map +1 -1
- package/dist/range-input.js +9 -4
- package/dist/range-input.js.map +1 -1
- package/dist/range.d.ts +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +5 -3
- package/dist/range.js.map +1 -1
- package/dist/{registry-Q2gYQCHM.js → registry-CkW7q09Q.js} +1 -1
- package/dist/{registry-Q2gYQCHM.js.map → registry-CkW7q09Q.js.map} +1 -1
- package/dist/selecthorizontal.d.ts +1 -1
- package/dist/selecthorizontal.d.ts.map +1 -1
- package/dist/selecthorizontal.js +4 -3
- package/dist/selecthorizontal.js.map +1 -1
- package/dist/snap-container.d.ts.map +1 -1
- package/dist/snap-container.js +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/split-layout.d.ts.map +1 -1
- package/dist/split-layout.js +6 -5
- package/dist/split-layout.js.map +1 -1
- package/dist/style.css +14 -4
- package/dist/swipe.d.ts.map +1 -1
- package/dist/swipe.js +9 -4
- package/dist/swipe.js.map +1 -1
- package/dist/{tab-list-BnqISNvO.d.ts → tab-list-DLzb-m-I.d.ts} +2 -2
- package/dist/tab-list-DLzb-m-I.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +10 -4
- package/dist/tabs.js.map +1 -1
- package/dist/{tickled-controller-Cq1Va0rR.d.ts → tickled-controller-BjYCzGrU.d.ts} +2 -2
- package/dist/tickled-controller-BjYCzGrU.d.ts.map +1 -0
- package/dist/{tickled-styles-Bg3QbcrD.js → tickled-styles-fNDdqf6l.js} +1 -17
- package/dist/{tickled-styles-Bg3QbcrD.js.map → tickled-styles-fNDdqf6l.js.map} +1 -1
- package/dist/{timeline-Cv75Th2U.js → timeline-2OFtN4Ta.js} +1437 -36
- package/dist/timeline-2OFtN4Ta.js.map +1 -0
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/{tooltip-DMxmBNky.js → tooltip-CJt3WLmN.js} +1 -1
- package/dist/{tooltip-DMxmBNky.js.map → tooltip-CJt3WLmN.js.map} +1 -1
- package/dist/{tooltip-CwGfb4lp.d.ts → tooltip-RGVIwHrZ.d.ts} +1 -1
- package/dist/{tooltip-CwGfb4lp.d.ts.map → tooltip-RGVIwHrZ.d.ts.map} +1 -1
- package/dist/{tree-CvvKbO-f.js → tree-CJWpMUvD.js} +113 -103
- package/dist/tree-CJWpMUvD.js.map +1 -0
- package/dist/{tree-component-D8Bg48tt.d.ts → tree-component-BcO26Xvt.d.ts} +65 -3
- package/dist/tree-component-BcO26Xvt.d.ts.map +1 -0
- package/dist/tree.d.ts +15 -15
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/{types-BKaqnpwx.d.ts → types-DSfbtHs3.d.ts} +1 -1
- package/dist/types-DSfbtHs3.d.ts.map +1 -0
- package/dist/{xy-axis-DL0tfJDr.js → xy-axis-CK64haaK.js} +1831 -359
- package/dist/xy-axis-CK64haaK.js.map +1 -0
- package/dist/{xy-axis-D9uwpGTT.d.ts → xy-axis-McdUpcYr.d.ts} +102 -3
- package/dist/xy-axis-McdUpcYr.d.ts.map +1 -0
- package/dist/xy-pad.d.ts +1 -1
- package/dist/xy-pad.d.ts.map +1 -1
- package/dist/xy-pad.js +11 -6
- package/dist/xy-pad.js.map +1 -1
- package/docs-user/README.md +45 -0
- package/docs-user/ac-text.md +67 -0
- package/docs-user/ac-token.md +187 -0
- package/docs-user/button.md +355 -0
- package/docs-user/checkbox.md +62 -0
- package/docs-user/colour-picker.md +237 -0
- package/docs-user/crumbs.md +66 -0
- package/docs-user/data-display.md +98 -0
- package/docs-user/editable-label.md +244 -0
- package/docs-user/grouped-item-lister.md +424 -0
- package/docs-user/icons.md +191 -0
- package/docs-user/incr-search.md +94 -0
- package/docs-user/labelled-radial-input.md +204 -0
- package/docs-user/labelled-range-input.md +194 -0
- package/docs-user/led.md +40 -0
- package/docs-user/menu.md +319 -0
- package/docs-user/miller.md +453 -0
- package/docs-user/narrowed-text.md +72 -0
- package/docs-user/panel.md +250 -0
- package/docs-user/plots.md +361 -0
- package/docs-user/polar-pad.md +228 -0
- package/docs-user/radial-input.md +559 -0
- package/docs-user/range-input.md +264 -0
- package/docs-user/range.md +12 -0
- package/docs-user/selecthorizontal.md +32 -0
- package/docs-user/snap-container.md +132 -0
- package/docs-user/split-layout.md +232 -0
- package/docs-user/swipe.md +44 -0
- package/docs-user/tabs.md +217 -0
- package/docs-user/timeline.md +116 -0
- package/docs-user/transitory-label.md +120 -0
- package/docs-user/tree.md +592 -0
- package/docs-user/user-catalog.md +165 -0
- package/docs-user/user-theming.md +379 -0
- package/docs-user/util.md +122 -0
- package/docs-user/vertical-list.md +351 -0
- package/docs-user/xy-pad.md +94 -0
- package/package.json +19 -18
- package/dist/colour-C3MQIjFJ-BLXg8W3U.js.map +0 -1
- package/dist/colour-picker-K6zu82lm.js.map +0 -1
- package/dist/dist-BX0OVIm7.js +0 -1290
- package/dist/dist-BX0OVIm7.js.map +0 -1
- package/dist/dist-TAGYwaju.js.map +0 -1
- package/dist/hex-editor-DlfoA65y.d.ts.map +0 -1
- package/dist/icon-9NWTXtE1.js.map +0 -1
- package/dist/icon-CeTDJhC7.d.ts.map +0 -1
- package/dist/index--hrxPBkQ.d.ts.map +0 -1
- package/dist/index-CnL9krsH.d.ts.map +0 -1
- package/dist/index-DSlPeWLK.d.ts.map +0 -1
- package/dist/index-DrQjA8Gs.d.ts.map +0 -1
- package/dist/interaction-DG7X1rhI.js +0 -1
- package/dist/labelled-input-base-D-Y7agky.d.ts.map +0 -1
- package/dist/menu-DJJuCm-5.js.map +0 -1
- package/dist/menu-item-CKRfP0pN.js.map +0 -1
- package/dist/menu-item-ClQF2DKN.d.ts.map +0 -1
- package/dist/prominence--ic9uWBl.js +0 -98
- package/dist/prominence--ic9uWBl.js.map +0 -1
- package/dist/radial-input-DZA6WaFu.js.map +0 -1
- package/dist/radial-input-LUSBEycd.d.ts.map +0 -1
- package/dist/tab-list-BnqISNvO.d.ts.map +0 -1
- package/dist/tickled-controller-Cq1Va0rR.d.ts.map +0 -1
- package/dist/timeline-Cv75Th2U.js.map +0 -1
- package/dist/tree-CvvKbO-f.js.map +0 -1
- package/dist/tree-component-D8Bg48tt.d.ts.map +0 -1
- package/dist/types-BKaqnpwx.d.ts.map +0 -1
- package/dist/xy-axis-D9uwpGTT.d.ts.map +0 -1
- package/dist/xy-axis-DL0tfJDr.js.map +0 -1
- /package/bundle/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
- /package/dist/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
# ixfx-radial-input
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<ixfx-radial-input min="0" max="1" value="0.5"></ixfx-radial-input>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The component defaults to `100px` wide. Set width (and optionally height) via CSS:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<ixfx-radial-input min="0" max="100" value="50" style="width: 120px;"></ixfx-radial-input>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
To fill a container:
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<div style="width: 80px; height: 80px;">
|
|
23
|
+
<ixfx-radial-input min="0" max="1" value="0.5" style="width: 100%; height: 100%;"></ixfx-radial-input>
|
|
24
|
+
</div>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Attributes & Properties
|
|
30
|
+
|
|
31
|
+
| Attribute / Property | Type | Default | Description |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `value` | `number` | `0` | Current value, within `min`…`max` |
|
|
34
|
+
| `min` | `number` | `0` | Minimum value |
|
|
35
|
+
| `max` | `number` | `100` | Maximum value |
|
|
36
|
+
| `step` | `number` | `undefined` | Keyboard step size. If unset, defaults to 20% of range |
|
|
37
|
+
| `stops` | `number` | `undefined` | Quantise to N discrete positions. Also sets keyboard step size |
|
|
38
|
+
| `value-mode` | `"range"` \| `"bipolar"` \| `"unipolar"` | `"range"` | Controls how the value arc is drawn (see [Value Modes](#value-modes)) |
|
|
39
|
+
| `marker-style` | `"needle"` \| `"dot"` \| `"none"` | `"needle"` | Visual style of the value indicator |
|
|
40
|
+
| `flatten` | `number` | `0.3` | Proportion of circle to hide at the top (0 = full circle, 0.5 = semicircle, 1 = nothing). Controls vertical footprint |
|
|
41
|
+
| `radius` | `number` | `45` | Dial radius in viewBox units (10–50). Clamped automatically |
|
|
42
|
+
| `wedge` | `boolean` | `false` | Enable a filled pie-slice behind the arc |
|
|
43
|
+
| `disabled` | `boolean` | `false` | Disables all interaction. Reflected as attribute |
|
|
44
|
+
|
|
45
|
+
### Sizing
|
|
46
|
+
|
|
47
|
+
Size via CSS `width`/`height`. The component's height adapts automatically to the `flatten` value to minimise whitespace — it tracks how much of the circle is visible and subtracts the hidden portion. You can override the height directly.
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<!-- Small -->
|
|
51
|
+
<ixfx-radial-input style="width: 60px;" min="0" max="1" value="0.4"></ixfx-radial-input>
|
|
52
|
+
|
|
53
|
+
<!-- Large -->
|
|
54
|
+
<ixfx-radial-input style="width: 200px;" min="0" max="1" value="0.4"></ixfx-radial-input>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Disabled state
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<ixfx-radial-input disabled min="0" max="1" value="0.7"></ixfx-radial-input>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Interaction
|
|
66
|
+
|
|
67
|
+
**Mouse / touch:** drag vertically. Dragging upward increases the value. Full min-to-max range spans `2×` the component's rendered height.
|
|
68
|
+
|
|
69
|
+
**Shift + drag:** fine control — sensitivity reduced to 1/10th (`KEY_STEP_DIVISOR = 10`).
|
|
70
|
+
|
|
71
|
+
**Keyboard** (when focused):
|
|
72
|
+
|
|
73
|
+
| Key | Action |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `ArrowUp` / `ArrowRight` | Increase by step |
|
|
76
|
+
| `ArrowDown` / `ArrowLeft` | Decrease by step |
|
|
77
|
+
| `Home` | Set to minimum |
|
|
78
|
+
| `End` | Set to maximum |
|
|
79
|
+
| `Shift` + arrows | Fine step (÷10) |
|
|
80
|
+
|
|
81
|
+
Step size priority: `stops` > explicit `step` > 20% of range.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Value Modes
|
|
86
|
+
|
|
87
|
+
### `range` (default)
|
|
88
|
+
|
|
89
|
+
Arc sweeps from the minimum position clockwise to the current value. Classic knob/potentiometer behaviour.
|
|
90
|
+
|
|
91
|
+
```html
|
|
92
|
+
<ixfx-radial-input value-mode="range" min="0" max="1" value="0.7"></ixfx-radial-input>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `bipolar`
|
|
96
|
+
|
|
97
|
+
Arc pivots from the top-centre (midpoint). Values below the midpoint of `min`…`max` sweep left; values above sweep right. A midpoint marker is drawn at the top. Suited for pan, pitch, or any centred parameter.
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<ixfx-radial-input value-mode="bipolar" min="-1" max="1" value="0.5"></ixfx-radial-input>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `unipolar`
|
|
104
|
+
|
|
105
|
+
Arc spreads symmetrically from the top-centre in both directions. Suited for width or intensity parameters where the visual weight is centred. A midpoint marker is drawn at the top.
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<ixfx-radial-input value-mode="unipolar" min="0" max="1" value="0.6"></ixfx-radial-input>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Stepping & Quantisation
|
|
114
|
+
|
|
115
|
+
`step` controls keyboard navigation step size:
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<ixfx-radial-input min="0" max="1" value="0.5" step="0.2"></ixfx-radial-input>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`stops` quantises the value to N equally-spaced positions. Drag and keyboard input both snap:
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<!-- 5 discrete stops: 0, 0.25, 0.5, 0.75, 1.0 -->
|
|
125
|
+
<ixfx-radial-input min="0" max="1" value="0.5" stops="5"></ixfx-radial-input>
|
|
126
|
+
|
|
127
|
+
<!-- 10 stops across 0-100: snaps to 0, 10, 20 … 100 -->
|
|
128
|
+
<ixfx-radial-input min="0" max="100" value="50" stops="10"></ixfx-radial-input>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When `stops` is set it also governs the keyboard step (one stop per keypress).
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Events
|
|
136
|
+
|
|
137
|
+
Both events bubble and are `composed` (cross shadow-DOM boundaries).
|
|
138
|
+
|
|
139
|
+
### `input`
|
|
140
|
+
|
|
141
|
+
Fired continuously during user interaction (every pointer/key event that changes the value). Use for live updates.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import type { RadialInputInputEvent } from 'ixfx-radial-input/types';
|
|
145
|
+
|
|
146
|
+
dial.addEventListener('input', (e: RadialInputInputEvent) => {
|
|
147
|
+
const { value, valueRelative } = e.detail;
|
|
148
|
+
// value: number in min..max range
|
|
149
|
+
// valueRelative: number 0..1
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `change`
|
|
154
|
+
|
|
155
|
+
Fired after the user finishes interacting (debounced 150 ms). Equivalent to the native `<input type="range">` change semantics. Also fires immediately for programmatic value changes.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import type { RadialInputChangeEvent } from 'ixfx-radial-input/types';
|
|
159
|
+
|
|
160
|
+
dial.addEventListener('change', (e: RadialInputChangeEvent) => {
|
|
161
|
+
const { value, valueRelative, previousValue } = e.detail;
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Note:** `input` fires only for user-initiated changes. Programmatic `dial.value = x` triggers only `change`.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## CSS Custom Properties
|
|
170
|
+
|
|
171
|
+
### Dial outline
|
|
172
|
+
|
|
173
|
+
| Variable | Default | Description |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `--dial-border-color` | `var(--surface-4)` | Colour of the track border arc |
|
|
176
|
+
| `--dial-border-width` | `2` | Stroke width of the border arc (SVG units) |
|
|
177
|
+
| `--dial-fill` | `transparent` | Fill colour of the dial face |
|
|
178
|
+
|
|
179
|
+
### Lanes
|
|
180
|
+
|
|
181
|
+
Lanes are arcs drawn around the dial. The built-in lanes are `track` (the grey background arc) and `value` (the accent-coloured progress arc).
|
|
182
|
+
|
|
183
|
+
| Variable | Default | Description |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `--lane-track-color` | `var(--surface-4)` | Colour of the `track` lane |
|
|
186
|
+
| `--lane-value-color` | `var(--accent)` | Colour of the `value` lane |
|
|
187
|
+
| `--lane-color` | `var(--accent)` | Fallback colour for custom lanes |
|
|
188
|
+
| `--lane-<key>-color` | — | Per-lane colour override (e.g. `--lane-sweep-color`) |
|
|
189
|
+
| `--lane-<key>-width` | — | Per-lane width override |
|
|
190
|
+
| `--lane-dasharray` | `none` | SVG `stroke-dasharray` applied to all lanes |
|
|
191
|
+
| `--lane-dashoffset` | `0` | SVG `stroke-dashoffset` — animate for a marching-ants effect |
|
|
192
|
+
|
|
193
|
+
Dashed lanes:
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<!-- 8px dashes, 4px gaps -->
|
|
197
|
+
<ixfx-radial-input style="--lane-dasharray: 8 4;" min="0" max="1" value="0.6"></ixfx-radial-input>
|
|
198
|
+
|
|
199
|
+
<!-- Dotted -->
|
|
200
|
+
<ixfx-radial-input style="--lane-dasharray: 2 4;" min="0" max="1" value="0.6"></ixfx-radial-input>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Marker (value indicator)
|
|
204
|
+
|
|
205
|
+
| Variable | Default | Description |
|
|
206
|
+
|---|---|---|
|
|
207
|
+
| `--marker-color` | `var(--accent)` | Colour of the needle line or dot fill |
|
|
208
|
+
| `--marker-thickness` | `0.15` | Relative thickness (fraction of radius) |
|
|
209
|
+
| `--marker-length` | `0.8` | Needle length as fraction of radius |
|
|
210
|
+
| `--marker-offset` | `0.2` | Needle start offset from centre (needle), or radial position multiplier (dot) |
|
|
211
|
+
| `--marker-border-width` | `0` | Border halo thickness around needle/dot |
|
|
212
|
+
| `--marker-border-color` | `transparent` | Border halo colour |
|
|
213
|
+
| `--marker-dasharray` | `none` | Dashed needle pattern |
|
|
214
|
+
| `--marker-dashoffset` | `0` | Dashed needle offset |
|
|
215
|
+
|
|
216
|
+
### Midpoint marker (bipolar/unipolar modes)
|
|
217
|
+
|
|
218
|
+
| Variable | Default | Description |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| `--midpoint-color` | `var(--surface-muted-text)` | Colour of the top-centre tick/dot |
|
|
221
|
+
| `--midpoint-thickness` | `1px` (needle) / `0.08` (dot) | Tick line width or dot size |
|
|
222
|
+
| `--midpoint-border-color` | `transparent` | Dot border colour (dot style only) |
|
|
223
|
+
| `--midpoint-border-width` | `0` | Dot border width (dot style only) |
|
|
224
|
+
| `--midpoint-length` | `0.15` | Tick line length as fraction of radius (needle style) |
|
|
225
|
+
| `--midpoint-offset` | `0.85` | Tick start offset from centre (needle style) |
|
|
226
|
+
|
|
227
|
+
### Wedge
|
|
228
|
+
|
|
229
|
+
| Variable | Default | Description |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `--wedge-fill` | `var(--accent)` | Wedge fill colour |
|
|
232
|
+
| `--wedge-fill-opacity` | `0.25` | Wedge fill opacity |
|
|
233
|
+
| `--wedge-border-color` | `var(--accent)` | Wedge border/stroke colour |
|
|
234
|
+
| `--wedge-border-width` | `0` | Wedge border stroke width |
|
|
235
|
+
| `--wedge-border-opacity` | `1` | Wedge border opacity |
|
|
236
|
+
|
|
237
|
+
### Dynamic value variable
|
|
238
|
+
|
|
239
|
+
The component sets `--value-relative` (a number 0…1) on the host element as the value changes. Use it inside `calc()` to create value-reactive styles entirely in CSS:
|
|
240
|
+
|
|
241
|
+
```html
|
|
242
|
+
<!-- Needle colour shifts from purple to pink as value increases -->
|
|
243
|
+
<ixfx-radial-input
|
|
244
|
+
min="0" max="1" value="0.6"
|
|
245
|
+
style="--marker-color: hsl(calc((1 - var(--value-relative)) * 280), 80%, 55%);"
|
|
246
|
+
></ixfx-radial-input>
|
|
247
|
+
|
|
248
|
+
<!-- Wedge hue follows value -->
|
|
249
|
+
<ixfx-radial-input
|
|
250
|
+
wedge min="0" max="1" value="0.5"
|
|
251
|
+
style="--wedge-fill: hsl(calc(var(--value-relative) * 280), 80%, 50%); --wedge-fill-opacity: 0.4;"
|
|
252
|
+
></ixfx-radial-input>
|
|
253
|
+
|
|
254
|
+
<!-- Border thickens and changes colour -->
|
|
255
|
+
<ixfx-radial-input
|
|
256
|
+
min="0" max="1" value="0.5"
|
|
257
|
+
style="
|
|
258
|
+
--dial-border-width: calc(1 + var(--value-relative) * 5);
|
|
259
|
+
--dial-border-color: hsl(calc(var(--value-relative) * 280), 80%, 55%);
|
|
260
|
+
"
|
|
261
|
+
></ixfx-radial-input>
|
|
262
|
+
|
|
263
|
+
<!-- Needle width grows with value (1px thin → 9px thick) -->
|
|
264
|
+
<ixfx-radial-input
|
|
265
|
+
marker-style="needle" min="0" max="1" value="0.5"
|
|
266
|
+
style="--marker-thickness: calc(0.01 + var(--value-relative) * 0.2);"
|
|
267
|
+
></ixfx-radial-input>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Marker Styles
|
|
273
|
+
|
|
274
|
+
### `needle` (default)
|
|
275
|
+
|
|
276
|
+
A line from `--marker-offset` outward to `--marker-length` (both as fractions of radius).
|
|
277
|
+
|
|
278
|
+
```html
|
|
279
|
+
<ixfx-radial-input marker-style="needle" min="0" max="1" value="0.6"></ixfx-radial-input>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Thin minimalist look:
|
|
283
|
+
|
|
284
|
+
```html
|
|
285
|
+
<ixfx-radial-input
|
|
286
|
+
marker-style="needle" min="0" max="1" value="0.6"
|
|
287
|
+
style="--marker-thickness: 0.05; --marker-color: var(--surface-muted-text); --marker-length: 0.9; --marker-offset: 0.5;"
|
|
288
|
+
></ixfx-radial-input>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
With a border halo (useful for contrast on filled dials):
|
|
292
|
+
|
|
293
|
+
```html
|
|
294
|
+
<ixfx-radial-input
|
|
295
|
+
marker-style="needle" min="0" max="1" value="0.6"
|
|
296
|
+
style="--marker-thickness: 0.1; --marker-border-width: 0.02; --marker-border-color: white;"
|
|
297
|
+
></ixfx-radial-input>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### `dot`
|
|
301
|
+
|
|
302
|
+
A filled circle placed at the perimeter at the value's angular position. `--marker-offset` moves it radially (1.0 = on the arc, <1.0 = toward centre, >1.0 = outside arc).
|
|
303
|
+
|
|
304
|
+
```html
|
|
305
|
+
<!-- Dot on the arc -->
|
|
306
|
+
<ixfx-radial-input marker-style="dot" min="0" max="1" value="0.6" style="--marker-offset: 1;"></ixfx-radial-input>
|
|
307
|
+
|
|
308
|
+
<!-- Dot inside perimeter -->
|
|
309
|
+
<ixfx-radial-input marker-style="dot" min="0" max="1" value="0.6" style="--marker-offset: 0.5;"></ixfx-radial-input>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Dot with dynamic border:
|
|
313
|
+
|
|
314
|
+
```html
|
|
315
|
+
<ixfx-radial-input
|
|
316
|
+
marker-style="dot" min="0" max="1" value="0.6"
|
|
317
|
+
style="
|
|
318
|
+
--marker-offset: 0.8;
|
|
319
|
+
--marker-thickness: 0.12;
|
|
320
|
+
--marker-color: var(--surface-1);
|
|
321
|
+
--marker-border-width: 0.05;
|
|
322
|
+
--marker-border-color: hsl(calc(var(--value-relative) * 280), 80%, 60%);
|
|
323
|
+
"
|
|
324
|
+
></ixfx-radial-input>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### `none`
|
|
328
|
+
|
|
329
|
+
No marker rendered. Useful when the wedge or lanes carry the visual.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Wedge Visualisation
|
|
334
|
+
|
|
335
|
+
The `wedge` attribute renders a filled pie-slice sector behind the lane arcs. It follows the active `value-mode`.
|
|
336
|
+
|
|
337
|
+
```html
|
|
338
|
+
<!-- Basic -->
|
|
339
|
+
<ixfx-radial-input wedge min="0" max="1" value="0.5"></ixfx-radial-input>
|
|
340
|
+
|
|
341
|
+
<!-- With border -->
|
|
342
|
+
<ixfx-radial-input wedge min="0" max="1" value="0.5"
|
|
343
|
+
style="--wedge-fill-opacity: 0.2; --wedge-border-width: 1;">
|
|
344
|
+
</ixfx-radial-input>
|
|
345
|
+
|
|
346
|
+
<!-- Temperature gauge (blue→red) -->
|
|
347
|
+
<ixfx-radial-input wedge min="0" max="1" value="0.3"
|
|
348
|
+
style="--wedge-fill: hsl(calc((1 - var(--value-relative)) * 240), 80%, 50%); --wedge-fill-opacity: 0.4;">
|
|
349
|
+
</ixfx-radial-input>
|
|
350
|
+
|
|
351
|
+
<!-- Bipolar wedge -->
|
|
352
|
+
<ixfx-radial-input wedge value-mode="bipolar" min="-1" max="1" value="0.7"
|
|
353
|
+
style="--wedge-fill: hsl(120, 70%, 50%); --wedge-fill-opacity: 0.35;">
|
|
354
|
+
</ixfx-radial-input>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Flatten
|
|
360
|
+
|
|
361
|
+
`flatten` hides a fraction of the top of the circle, converting the full-circle dial into a horseshoe shape. The component height adjusts automatically to remove the dead space.
|
|
362
|
+
|
|
363
|
+
| Value | Arc visible | Degrees |
|
|
364
|
+
|---|---|---|
|
|
365
|
+
| `0` | 100% | 360° full circle |
|
|
366
|
+
| `0.3` (default) | 70% | 252° |
|
|
367
|
+
| `0.5` | 50% | 180° semicircle |
|
|
368
|
+
| `0.7` | 30% | 108° |
|
|
369
|
+
|
|
370
|
+
```html
|
|
371
|
+
<ixfx-radial-input flatten="0.5" min="0" max="1" value="0.75"></ixfx-radial-input>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Data Lanes (programmatic API)
|
|
377
|
+
|
|
378
|
+
Lanes are additional arcs drawn around the dial. The two built-in lanes are `track` (grey background) and `value` (accent progress arc). You can add custom lanes at runtime.
|
|
379
|
+
|
|
380
|
+
### Lane types
|
|
381
|
+
|
|
382
|
+
| Type | Behaviour |
|
|
383
|
+
|---|---|
|
|
384
|
+
| `value` | Arc sweeps from origin to `lane.value` (same logic as the main value arc) |
|
|
385
|
+
| `fixed` | Always shows the full track arc — useful for static reference rings |
|
|
386
|
+
| `anchored` | Arc is centred on the current dial value; `lane.value` controls its width (0..1 of total arc) |
|
|
387
|
+
|
|
388
|
+
### API
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import type { RadialInputElement } from 'ixfx-radial-input';
|
|
392
|
+
|
|
393
|
+
const dial = document.querySelector('ixfx-radial-input') as RadialInputElement;
|
|
394
|
+
|
|
395
|
+
// Wait for the element to be ready
|
|
396
|
+
await customElements.whenDefined('ixfx-radial-input');
|
|
397
|
+
await dial.updateComplete;
|
|
398
|
+
|
|
399
|
+
// Add a fixed reference ring at a different radial offset
|
|
400
|
+
dial.addLane('target', {
|
|
401
|
+
type: 'fixed',
|
|
402
|
+
offset: 0.2, // 20% closer to centre
|
|
403
|
+
width: 0.05, // thin line
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Add an animated value lane (e.g. a running average)
|
|
407
|
+
dial.addLane('average', {
|
|
408
|
+
type: 'value',
|
|
409
|
+
value: 0.4, // initial 0..1 position
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Add an anchored tolerance band centred on the current value
|
|
413
|
+
dial.addLane('tolerance', {
|
|
414
|
+
type: 'anchored',
|
|
415
|
+
value: 0.15, // band spans ±15% of total arc width
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Update a lane's value (0..1)
|
|
419
|
+
dial.setLaneValue('average', 0.6);
|
|
420
|
+
|
|
421
|
+
// Remove a lane
|
|
422
|
+
dial.removeLane('target');
|
|
423
|
+
|
|
424
|
+
// Read lane state
|
|
425
|
+
const lane = dial.getLane('average');
|
|
426
|
+
const allLanes = dial.getLanes(); // ReadonlyMap<string, LaneData>
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Style lanes via CSS variables using the lane key:
|
|
430
|
+
|
|
431
|
+
```css
|
|
432
|
+
ixfx-radial-input {
|
|
433
|
+
--lane-average-color: hsl(120, 70%, 50%);
|
|
434
|
+
--lane-tolerance-color: hsl(180, 80%, 50%);
|
|
435
|
+
--lane-sweep-width: 0.06;
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Live lane animation example
|
|
440
|
+
|
|
441
|
+
From the demo — a sweep lane that oscillates between 0 and the current dial value:
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
let sweepPhase = 0;
|
|
445
|
+
setInterval(() => {
|
|
446
|
+
sweepPhase += 0.08;
|
|
447
|
+
const sineValue = (Math.sin(sweepPhase) + 1) / 2;
|
|
448
|
+
const currentValue = (dial.value - dial.min) / (dial.max - dial.min);
|
|
449
|
+
dial.setLaneValue('sweep', sineValue * currentValue);
|
|
450
|
+
}, 50);
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Tooltip Integration
|
|
456
|
+
|
|
457
|
+
Set `data-tooltip` and `data-tooltip-live` to wire up a live tooltip that updates as the user drags:
|
|
458
|
+
|
|
459
|
+
```html
|
|
460
|
+
<ixfx-radial-input
|
|
461
|
+
min="0" max="1" value="0.5"
|
|
462
|
+
data-tooltip="50%"
|
|
463
|
+
data-tooltip-anchor="top"
|
|
464
|
+
data-tooltip-live="input"
|
|
465
|
+
></ixfx-radial-input>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
When `data-tooltip-live` is present the component automatically updates `data-tooltip` to the current percentage (e.g. `"63%"`) on every `input` event.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## CSS Parts
|
|
473
|
+
|
|
474
|
+
Lane arcs are exposed as CSS shadow parts for styling from outside the shadow DOM:
|
|
475
|
+
|
|
476
|
+
```css
|
|
477
|
+
ixfx-radial-input::part(lane) { … } /* all lanes */
|
|
478
|
+
ixfx-radial-input::part(lane-track) { … } /* track lane specifically */
|
|
479
|
+
ixfx-radial-input::part(lane-value) { … } /* value lane specifically */
|
|
480
|
+
ixfx-radial-input::part(lane-sweep) { … } /* custom "sweep" lane */
|
|
481
|
+
ixfx-radial-input::part(value-marker) { … } /* needle or dot group */
|
|
482
|
+
ixfx-radial-input::part(midpoint-marker) { … } /* bipolar/unipolar centre tick */
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Styling Recipes
|
|
488
|
+
|
|
489
|
+
### Purple theme
|
|
490
|
+
|
|
491
|
+
```css
|
|
492
|
+
#my-dial {
|
|
493
|
+
--lane-value-color: hsl(280, 100%, 60%);
|
|
494
|
+
--lane-track-color: hsl(280, 30%, 30%);
|
|
495
|
+
--marker-thickness: 0.3;
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Animated marching-ants track
|
|
500
|
+
|
|
501
|
+
```css
|
|
502
|
+
@keyframes march {
|
|
503
|
+
to { --lane-dashoffset: -30; }
|
|
504
|
+
}
|
|
505
|
+
#my-dial {
|
|
506
|
+
--lane-dasharray: 20 10;
|
|
507
|
+
animation: march 1s linear infinite;
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Implementation Notes
|
|
514
|
+
|
|
515
|
+
These are lessons learned from building the component — useful context when building a similar thing from scratch.
|
|
516
|
+
|
|
517
|
+
### SVG in a fixed 100×100 viewBox
|
|
518
|
+
|
|
519
|
+
All geometry is computed in a fixed `100×100` viewBox coordinate space with the circle centred at `{x: 50, y: 50}`. `radius` is a property clamped to 10–50 to prevent the dial from clipping the viewBox edges. This makes every calculation coordinate-system-agnostic — the SVG `viewBox` attribute takes care of scaling to the actual DOM pixels.
|
|
520
|
+
|
|
521
|
+
### Flatten changes the viewBox height
|
|
522
|
+
|
|
523
|
+
A flat-bottomed dial is achieved by cutting off the top portion of the arc. The full circle sits in a `100×100` box, but showing only the bottom `(1 - flatten) × 100` rows means the top of the box is empty space. Rather than cropping with `overflow: hidden`, the component dynamically narrows the SVG `viewBox` height (and sets a matching `--dial-height` CSS variable on the host) after first render and whenever `flatten` changes. This removes the dead space and makes the component's physical footprint match its visual content without requiring the consumer to manually set a height.
|
|
524
|
+
|
|
525
|
+
### Pointer capture, not window listeners… except for edge cases
|
|
526
|
+
|
|
527
|
+
The intended approach is `svg.setPointerCapture(event.pointerId)` on `pointerdown` so that `pointermove` events continue to reach the element even when the pointer leaves it. However, `pointermove` and `pointerup` are also attached to `window` as a belt-and-suspenders fallback, and removed again in `pointerup`/`pointercancel`. This handles edge cases where pointer capture is not available or fails silently (e.g. in some sandboxed environments).
|
|
528
|
+
|
|
529
|
+
### Drag sensitivity tied to rendered height
|
|
530
|
+
|
|
531
|
+
Full min-to-max range = `2 × clientHeight` of drag distance. This means a 100 px dial needs 200 px of vertical drag to sweep its full range — comfortable but precise. The sensitivity scales automatically as the component is resized. `Shift` divides sensitivity by 10 (`KEY_STEP_DIVISOR`), matching the fine-control convention used across other components in this library.
|
|
532
|
+
|
|
533
|
+
### Value stored in domain units; relative value (0..1) computed on demand
|
|
534
|
+
|
|
535
|
+
`this.value` is always in `min`…`max` domain units. Relative values are computed when needed with `scale(value, min, max, 0, 1)`. This avoids double-state and keeps the public API matching `<input type="range">` conventions.
|
|
536
|
+
|
|
537
|
+
### `input` vs `change` event semantics mirror native inputs
|
|
538
|
+
|
|
539
|
+
`input` fires on every user-driven value change (live, high frequency). `change` is debounced (150 ms) and fires once after the user stops interacting — identical to native `<input>` semantics. Programmatic writes bypass the `input` event entirely and fire `change` immediately (not debounced). This distinction matters to consumers: bind live UI feedback to `input`, persist/process to `change`.
|
|
540
|
+
|
|
541
|
+
### Lane rendering uses `part` + per-lane CSS variable patterns
|
|
542
|
+
|
|
543
|
+
Each lane path has `part="lane lane-<key>"` and reads its colour from `var(--lane-<key>-color, var(--lane-color))`. This two-tier fallback means a single `--lane-color` styles all custom lanes, while individual lanes can still be overridden by name. The same pattern is used for widths. The SVG `stroke-width` is expressed as a `calc()` over `--lane-<key>-width * radius%` so lane thickness scales proportionally with dial size.
|
|
544
|
+
|
|
545
|
+
### `--value-relative` as a CSS custom property bridge
|
|
546
|
+
|
|
547
|
+
Because CSS custom properties are live (they cascade and recompute continuously), updating `--value-relative` on the host element during every render cycle lets any CSS expression that references `var(--value-relative)` update purely in the browser's style engine — no JS event listener needed by the consumer. The component also has a `#getCssVar()` helper that evaluates `calc(…var(--value-relative)…)` expressions when reading back CSS variables (e.g. for `--marker-thickness`), so CSS-driven geometry works even for SVG attributes that must be set as numbers.
|
|
548
|
+
|
|
549
|
+
### Quantisation happens at the set-value boundary
|
|
550
|
+
|
|
551
|
+
`#quantise()` is called in `#setValueFromRelative()`, the single internal entry point for all value changes. Snapping to stops is therefore universal — drag, keyboard, and programmatic writes all go through the same path.
|
|
552
|
+
|
|
553
|
+
### `willUpdate` initialises the value lane before the first render
|
|
554
|
+
|
|
555
|
+
The `value` lane's internal `value` field is a 0..1 relative value, not a domain value. It must be in sync before the first paint. This is done in `willUpdate()` (not the constructor) because `@property` values set from HTML attributes are not yet parsed at construction time.
|
|
556
|
+
|
|
557
|
+
### `disconnectedCallback` cleans up timers and global listeners
|
|
558
|
+
|
|
559
|
+
The debounce timer and window pointer listeners are both cleaned up in `disconnectedCallback`. The pointer cleanup is achieved by calling `#onPointerUp()` directly, which also resets `#dragging`. Forgetting this causes a memory leak and a stuck-dragging bug if an element is removed mid-drag.
|