@ixfx/components 0.4.3 → 0.5.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 +518 -1176
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +4861 -17274
- package/bundle/index.js.map +1 -1
- package/bundle/style.css +1 -1
- package/dist/ac-text.d.ts +12 -7
- package/dist/ac-text.d.ts.map +1 -1
- package/dist/ac-text.js +2 -3
- package/dist/ac-text.js.map +1 -1
- package/dist/{button-C3fLMKUv.js → button-DxYee1ZH.js} +2 -2
- package/dist/{button-C3fLMKUv.js.map → button-DxYee1ZH.js.map} +1 -1
- package/dist/button.d.ts +1 -2
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -2
- package/dist/checkbox.d.ts +0 -1
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/{colour-picker-0sgAha1T.js → colour-picker-C3Ag4uh-.js} +15 -17
- package/dist/colour-picker-C3Ag4uh-.js.map +1 -0
- package/dist/colour-picker.d.ts +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +5 -2
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +45 -20
- package/dist/crumbs.js.map +1 -1
- package/dist/data-display.d.ts +1 -2
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +25 -23
- package/dist/data-display.js.map +1 -1
- package/dist/{data-provider-DgI909FP.js → data-provider-35TyWclX.js} +2 -2
- package/dist/data-provider-35TyWclX.js.map +1 -0
- package/dist/{decorate-DdjvVHS-.js → decorate-VRK8rslU.js} +2 -2
- package/dist/{decorate-DdjvVHS-.js.map → decorate-VRK8rslU.js.map} +1 -1
- package/dist/{defaults-C3b9OWJD.js → defaults-DcE8RQJc.js} +19 -1
- package/dist/defaults-DcE8RQJc.js.map +1 -0
- package/dist/editable-label.d.ts +1 -2
- package/dist/editable-label.d.ts.map +1 -1
- package/dist/editable-label.js +1 -1
- package/dist/editable-label.js.map +1 -1
- package/dist/{hex-editor-WZrjRtvL.js → hex-editor-Bk6rxhfg.js} +9 -11
- package/dist/hex-editor-Bk6rxhfg.js.map +1 -0
- package/dist/{hex-editor-CMIxtNgT.d.ts → hex-editor-Caq8vNQk.d.ts} +2 -3
- package/dist/hex-editor-Caq8vNQk.d.ts.map +1 -0
- package/dist/hex.d.ts +1 -1
- package/dist/hex.js +1 -1
- package/dist/highlight-B-eQrhhb.js.map +1 -1
- package/dist/{icon-Ndo40kNO.js → icon-B22XVlCt.js} +6 -4
- package/dist/{icon-Ndo40kNO.js.map → icon-B22XVlCt.js.map} +1 -1
- package/dist/{icon-Bd5BiU4b.d.ts → icon-DpA9x2Ve.d.ts} +2 -3
- package/dist/icon-DpA9x2Ve.d.ts.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +2 -2
- package/dist/{incr-search-CeERfijK.js → incr-search-2pz21xtR.js} +9 -6
- package/dist/incr-search-2pz21xtR.js.map +1 -0
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +1 -1
- package/dist/{index-CowKi2mo.d.ts → index-BeuCXtiO.d.ts} +3 -4
- package/dist/index-BeuCXtiO.d.ts.map +1 -0
- package/dist/{index-D87kRcEs.d.ts → index-BtGFYfaK.d.ts} +2 -1
- package/dist/index-BtGFYfaK.d.ts.map +1 -0
- package/dist/{index-s_IhSnwc.d.ts → index-CoqtMSd9.d.ts} +6 -4
- package/dist/index-CoqtMSd9.d.ts.map +1 -0
- package/dist/{index-9fGVAg1d.d.ts → index-Cxwxtj6g.d.ts} +3 -3
- package/dist/index-Cxwxtj6g.d.ts.map +1 -0
- package/dist/index-D_ftz_IT.d.ts +184 -0
- package/dist/index-D_ftz_IT.d.ts.map +1 -0
- package/dist/{index-_RTQSKHi.d.ts → index-DfEkTGvT.d.ts} +11 -11
- package/dist/index-DfEkTGvT.d.ts.map +1 -0
- package/dist/index.d.ts +167 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +712 -2970
- package/dist/index.js.map +1 -1
- package/dist/keyboard-B5xD7Fz-.js +36 -0
- package/dist/keyboard-B5xD7Fz-.js.map +1 -0
- package/dist/{labelled-input-base-ORb1rGq6.d.ts → labelled-input-base-DiTme1JO.d.ts} +1 -2
- package/dist/labelled-input-base-DiTme1JO.d.ts.map +1 -0
- package/dist/{labelled-input-base-9K_w3oLj.js → labelled-input-base-hoKGwyKT.js} +2 -2
- package/dist/{labelled-input-base-9K_w3oLj.js.map → labelled-input-base-hoKGwyKT.js.map} +1 -1
- package/dist/labelled-radial-input.d.ts +5 -4
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-radial-input.js +7 -3
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-range-input.js +27 -3
- package/dist/labelled-range-input.js.map +1 -1
- package/dist/led.d.ts +1 -2
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +1 -1
- package/dist/led.js.map +1 -1
- package/dist/{menu-vjoiHZ13.js → menu-g6yd7ddN.js} +8 -9
- package/dist/menu-g6yd7ddN.js.map +1 -0
- package/dist/{menu-item-COx-Azgj.d.ts → menu-item-DSyf9f_L.d.ts} +4 -4
- package/dist/menu-item-DSyf9f_L.d.ts.map +1 -0
- package/dist/{menu-item-BrIuORYD.js → menu-item-NUKwJ4s5.js} +3 -4
- package/dist/{menu-item-BrIuORYD.js.map → menu-item-NUKwJ4s5.js.map} +1 -1
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +8 -5
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +22 -24
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.d.ts +7 -3
- package/dist/narrowed-text.d.ts.map +1 -1
- package/dist/narrowed-text.js +73 -47
- 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 +2 -2
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +2 -2
- package/dist/plots.js +1 -1
- package/dist/polar-pad.d.ts +15 -3
- package/dist/polar-pad.d.ts.map +1 -1
- package/dist/polar-pad.js +64 -11
- package/dist/polar-pad.js.map +1 -1
- package/dist/{radial-input-CpQPs1Mp.d.ts → radial-input-BhZeyyP-.d.ts} +6 -4
- package/dist/radial-input-BhZeyyP-.d.ts.map +1 -0
- package/dist/{radial-input-DZ7eQCcM.js → radial-input-DxCrt_NE.js} +41 -19
- package/dist/radial-input-DxCrt_NE.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 +15 -4
- package/dist/range-input.d.ts.map +1 -1
- package/dist/range-input.js +23 -2
- package/dist/range-input.js.map +1 -1
- package/dist/range.d.ts +15 -4
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +23 -2
- package/dist/range.js.map +1 -1
- package/dist/selecthorizontal.d.ts +1 -2
- package/dist/selecthorizontal.d.ts.map +1 -1
- package/dist/selecthorizontal.js +1 -1
- package/dist/selecthorizontal.js.map +1 -1
- package/dist/slider-input-DFpG23bS.js +1592 -0
- package/dist/slider-input-DFpG23bS.js.map +1 -0
- package/dist/slider-input-TidGhvXt.d.ts +160 -0
- package/dist/slider-input-TidGhvXt.d.ts.map +1 -0
- package/dist/slider-input.d.ts +2 -0
- package/dist/slider-input.js +2 -0
- package/dist/snap-container.d.ts +1 -2
- package/dist/snap-container.d.ts.map +1 -1
- package/dist/snap-container.js +6 -5
- package/dist/snap-container.js.map +1 -1
- package/dist/split-layout.d.ts +1 -2
- package/dist/split-layout.d.ts.map +1 -1
- package/dist/split-layout.js +6 -4
- package/dist/split-layout.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/swipe.d.ts +12 -2
- package/dist/swipe.d.ts.map +1 -1
- package/dist/swipe.js +22 -3
- package/dist/swipe.js.map +1 -1
- package/dist/{tab-list-DLzb-m-I.d.ts → tab-list-BohmbvqF.d.ts} +14 -5
- package/dist/tab-list-BohmbvqF.d.ts.map +1 -0
- package/dist/tab-list-DhkVhRQc.js +597 -0
- package/dist/tab-list-DhkVhRQc.js.map +1 -0
- package/dist/tabs.d.ts +2 -2
- package/dist/tabs.js +1 -208
- package/dist/{tickled-controller-BjYCzGrU.d.ts → tickled-controller-BF2Ea0DE.d.ts} +2 -31
- package/dist/tickled-controller-BF2Ea0DE.d.ts.map +1 -0
- package/dist/tickled-styles-fNDdqf6l.js.map +1 -1
- package/dist/{timeline-2OFtN4Ta.js → timeline-B2fgDL4U.js} +37 -1438
- package/dist/timeline-B2fgDL4U.js.map +1 -0
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/{tooltip-RGVIwHrZ.d.ts → tooltip-BlvBUQxx.d.ts} +3 -3
- package/dist/tooltip-BlvBUQxx.d.ts.map +1 -0
- package/dist/{tree-D84RUgFC.js → tree-BnvzhsAw.js} +8 -9
- package/dist/tree-BnvzhsAw.js.map +1 -0
- package/dist/{tree-component-BaCSPWg1.d.ts → tree-component-DCmmKZ4j.d.ts} +8 -124
- package/dist/tree-component-DCmmKZ4j.d.ts.map +1 -0
- package/dist/tree.d.ts +10 -4
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/types-DO3gt5vg.d.ts +130 -0
- package/dist/types-DO3gt5vg.d.ts.map +1 -0
- package/dist/wheel-nudge-BlSCe_hD.js +86 -0
- package/dist/wheel-nudge-BlSCe_hD.js.map +1 -0
- package/dist/wheel-nudge-DgMTRYlh.d.ts +29 -0
- package/dist/wheel-nudge-DgMTRYlh.d.ts.map +1 -0
- package/dist/{xy-axis-BeWRMmbo.d.ts → xy-axis-5KhVTNoX.d.ts} +30 -7
- package/dist/xy-axis-5KhVTNoX.d.ts.map +1 -0
- package/dist/xy-axis-CxHC8z5E.js +1849 -0
- package/dist/xy-axis-CxHC8z5E.js.map +1 -0
- package/dist/xy-pad.d.ts +13 -2
- package/dist/xy-pad.d.ts.map +1 -1
- package/dist/xy-pad.js +47 -6
- package/dist/xy-pad.js.map +1 -1
- package/docs-user/README.md +2 -0
- package/docs-user/button.md +48 -0
- package/docs-user/narrowed-text.md +33 -1
- package/docs-user/notifier.md +131 -0
- package/docs-user/plots.md +61 -1
- package/docs-user/radial-input.md +2 -2
- package/docs-user/slider-input.md +673 -0
- package/docs-user/tabs.md +12 -14
- package/docs-user/user-catalog.md +69 -4
- package/docs-user/vertical-list.md +75 -7
- package/package.json +23 -20
- package/dist/colour-C3MQIjFJ-vRCdr8W-.js +0 -3732
- package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +0 -1
- package/dist/colour-picker-0sgAha1T.js.map +0 -1
- package/dist/data-provider-DgI909FP.js.map +0 -1
- package/dist/defaults-C3b9OWJD.js.map +0 -1
- package/dist/dist-CA13FvMn.js +0 -980
- package/dist/dist-CA13FvMn.js.map +0 -1
- package/dist/hex-editor-CMIxtNgT.d.ts.map +0 -1
- package/dist/hex-editor-WZrjRtvL.js.map +0 -1
- package/dist/icon-Bd5BiU4b.d.ts.map +0 -1
- package/dist/incr-search-CeERfijK.js.map +0 -1
- package/dist/index-9fGVAg1d.d.ts.map +0 -1
- package/dist/index-CowKi2mo.d.ts.map +0 -1
- package/dist/index-D87kRcEs.d.ts.map +0 -1
- package/dist/index-DgXpCg_G.d.ts +0 -1244
- package/dist/index-DgXpCg_G.d.ts.map +0 -1
- package/dist/index-_RTQSKHi.d.ts.map +0 -1
- package/dist/index-s_IhSnwc.d.ts.map +0 -1
- package/dist/keyboard-CimiEskD.js +0 -20
- package/dist/keyboard-CimiEskD.js.map +0 -1
- package/dist/labelled-input-base-ORb1rGq6.d.ts.map +0 -1
- package/dist/menu-item-COx-Azgj.d.ts.map +0 -1
- package/dist/menu-vjoiHZ13.js.map +0 -1
- package/dist/radial-input-CpQPs1Mp.d.ts.map +0 -1
- package/dist/radial-input-DZ7eQCcM.js.map +0 -1
- package/dist/tab-list-DLzb-m-I.d.ts.map +0 -1
- package/dist/tabs.js.map +0 -1
- package/dist/tickled-controller-BjYCzGrU.d.ts.map +0 -1
- package/dist/timeline-2OFtN4Ta.js.map +0 -1
- package/dist/tooltip-RGVIwHrZ.d.ts.map +0 -1
- package/dist/tree-D84RUgFC.js.map +0 -1
- package/dist/tree-component-BaCSPWg1.d.ts.map +0 -1
- package/dist/types-DSfbtHs3.d.ts +0 -8
- package/dist/types-DSfbtHs3.d.ts.map +0 -1
- package/dist/xy-axis-BeWRMmbo.d.ts.map +0 -1
- package/dist/xy-axis-no0OL_O1.js +0 -7893
- package/dist/xy-axis-no0OL_O1.js.map +0 -1
- /package/bundle/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
- /package/dist/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
# ixfx-slider-input
|
|
2
|
+
|
|
3
|
+
A linear SVG-based slider. Supports horizontal and vertical orientations, three value modes (single/dual/triple), three single-mode styles (range/bipolar/unipolar), triangle/needle/dot markers, and `value-drag` length-drag gestures for multi-handle modes.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Basic Usage
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<ixfx-slider-input min="0" max="100" value-a="50"></ixfx-slider-input>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The component defaults to `200px` wide and `24px` tall (the horizontal cross axis). Set width and height via CSS:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<ixfx-slider-input min="0" max="100" value-a="50" style="width: 320px;"></ixfx-slider-input>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Vertical orientation swaps the dimensions and rotates interaction to up/down:
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<ixfx-slider-input
|
|
23
|
+
orientation="vertical"
|
|
24
|
+
min="0" max="100" value-a="30"
|
|
25
|
+
style="width: 24px; height: 200px;"
|
|
26
|
+
></ixfx-slider-input>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To fill a container, set both `width: 100%` and `height: 100%`:
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<div style="width: 100%; height: 24px;">
|
|
33
|
+
<ixfx-slider-input min="0" max="100" value-a="50" style="width: 100%; height: 100%;"></ixfx-slider-input>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Attributes & Properties
|
|
40
|
+
|
|
41
|
+
| Attribute / Property | Type | Default | Description |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `value-a` | `number` | `50` | Primary handle value (single mode), or lower pole (dual/triple) |
|
|
44
|
+
| `value-b` | `number` | `0` | Upper pole (dual/triple modes only) |
|
|
45
|
+
| `value-c` | `number` | midpoint of A/B | Middle handle (triple mode only) |
|
|
46
|
+
| `min` | `number` | `0` | Minimum value |
|
|
47
|
+
| `max` | `number` | `100` | Maximum value |
|
|
48
|
+
| `step` | `number` | `undefined` | Keyboard/wheel/pointer step size. If unset, defaults to 10% of range |
|
|
49
|
+
| `stops` | `number` | `undefined` | Quantise to N discrete positions. Also sets step size |
|
|
50
|
+
| `value-mode` | `"single"` \| `"dual"` \| `"triple"` | `"single"` | Number of handles |
|
|
51
|
+
| `single-style` | `"range"` \| `"bipolar"` \| `"unipolar"` | `"range"` | How the value bar is drawn (single mode only) |
|
|
52
|
+
| `orientation` | `"horizontal"` \| `"vertical"` | `"horizontal"` | Track orientation |
|
|
53
|
+
| `marker-style` | `"needle"` \| `"dot"` \| `"triangle"` \| `"none"` | `"needle"` | Marker shape (applies to all handles) |
|
|
54
|
+
| `marker-style-c` | as `marker-style` | `undefined` | Optional per-handle marker override for the C handle in triple mode |
|
|
55
|
+
| `crossover` | `"none"` \| `"allow"` | `"allow"` | Whether handles can pass through each other (dual/triple) |
|
|
56
|
+
| `value-drag` | `"none"` \| `"stop"` \| `"flex"` | `"none"` | Whether the segment between A and B is draggable (dual/triple) |
|
|
57
|
+
| `value-min` | `number` | `1` | Minimum gap between A and B in value units (dual/triple) |
|
|
58
|
+
| `value-c-follow` | `"none"` \| `"proportional"` | `"none"` | How C follows when A/B move the band (triple mode) |
|
|
59
|
+
| `disabled` | `boolean` | `false` | Disables all interaction. Reflected as attribute |
|
|
60
|
+
|
|
61
|
+
### Sizing
|
|
62
|
+
|
|
63
|
+
Size via CSS `width`/`height`. The horizontal default is 200×24, vertical 24×200. Both can be overridden freely.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<!-- Thin (16px tall) -->
|
|
67
|
+
<ixfx-slider-input style="height: 16px;" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
68
|
+
|
|
69
|
+
<!-- Large (32px tall) -->
|
|
70
|
+
<ixfx-slider-input style="height: 32px;" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Disabled state
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<ixfx-slider-input disabled min="0" max="100" value-a="70"></ixfx-slider-input>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Interaction
|
|
82
|
+
|
|
83
|
+
**Mouse / touch:** click or drag on the track. Clicking sets the value to that position; dragging continuously updates it. In dual/triple modes, clicking a handle drags it; clicking the segment between handles (when `value-drag` is enabled) drags the whole band. Full min-to-max range spans `2×` the component's rendered long-axis size.
|
|
84
|
+
|
|
85
|
+
**Cursor feedback:**
|
|
86
|
+
- `grab` over a marker handle (a/b/c)
|
|
87
|
+
- `move` over a draggable segment (between A and B when `value-drag` is on)
|
|
88
|
+
- `pointer` over empty track where clicking sets a value (single mode; or the dead zones outside A/B in multi mode)
|
|
89
|
+
- `grabbing` while actively dragging
|
|
90
|
+
- `default` over a no-op segment (dual mode with `value-drag="none"`, or between A-C/C-B in triple with `value-drag="none"`)
|
|
91
|
+
- `not-allowed` on a `disabled` slider
|
|
92
|
+
|
|
93
|
+
**Shift + drag:** fine control — sensitivity reduced to 1/10th (`KEY_STEP_DIVISOR = 10`).
|
|
94
|
+
|
|
95
|
+
**Keyboard** (when focused):
|
|
96
|
+
|
|
97
|
+
| Key | Action |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `ArrowRight` / `ArrowUp` | Increase by step (single mode), or move last-interacted handle |
|
|
100
|
+
| `ArrowLeft` / `ArrowDown` | Decrease by step (single mode), or move last-interacted handle |
|
|
101
|
+
| `Home` | Set to `min` (or move handle to low in multi mode) |
|
|
102
|
+
| `End` | Set to `max` (or move handle to high in multi mode) |
|
|
103
|
+
| `Shift` + arrows | Fine step (÷10) |
|
|
104
|
+
|
|
105
|
+
**Wheel** (when focused or `lighttouch` is on):
|
|
106
|
+
|
|
107
|
+
Wheel forward increases the value, wheel backward decreases it (sign is platform-agnostic — both `deltaY` and `deltaX` are considered so Mac shift-scrolls are handled correctly). Shift narrows the step to 1/10th.
|
|
108
|
+
|
|
109
|
+
Step size priority: `stops` > explicit `step` > 10% of range.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Value Modes
|
|
114
|
+
|
|
115
|
+
### `single` (default)
|
|
116
|
+
|
|
117
|
+
One handle. The `value-a` property holds the current value.
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<ixfx-slider-input value-mode="single" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `dual`
|
|
124
|
+
|
|
125
|
+
Two handles. The `value-a` / `value-b` properties hold the low/high poles, and the value bar is drawn between them. They are swapped automatically so that `valueMin = min(valueA, valueB)` and `valueMax = max(valueA, valueB)`.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<ixfx-slider-input
|
|
129
|
+
value-mode="dual"
|
|
130
|
+
min="0" max="100"
|
|
131
|
+
value-a="20" value-b="80"
|
|
132
|
+
></ixfx-slider-input>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `crossover` attribute controls whether the handles can pass through each other:
|
|
136
|
+
- `"allow"` (default) — handles can cross; the bar will flip and the high/low swap.
|
|
137
|
+
- `"none"` — handles are pinned to their side and can't cross. `value-min` enforces a minimum gap.
|
|
138
|
+
|
|
139
|
+
The `value-drag` attribute controls whether the segment between A and B is itself draggable:
|
|
140
|
+
- `"none"` (default) — clicking the segment does nothing.
|
|
141
|
+
- `"stop"` — dragging the segment translates it, but length stays fixed at the bounds.
|
|
142
|
+
- `"flex"` — dragging the segment translates it, length shortens at the bounds.
|
|
143
|
+
|
|
144
|
+
### `triple`
|
|
145
|
+
|
|
146
|
+
Three handles. `value-a` / `value-b` are the outer poles, `value-c` is the middle handle. The `value-c-follow` attribute controls how C behaves when the A/B band changes:
|
|
147
|
+
- `"none"` (default) — C keeps its value unless pushed outside the new band.
|
|
148
|
+
- `"proportional"` — C is re-anchored at the same fractional position it had within the old band, so it moves with A/B.
|
|
149
|
+
|
|
150
|
+
```html
|
|
151
|
+
<ixfx-slider-input
|
|
152
|
+
value-mode="triple"
|
|
153
|
+
min="0" max="100"
|
|
154
|
+
value-a="20" value-b="80"
|
|
155
|
+
></ixfx-slider-input>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
A/B/C can be styled independently via `--marker-a-color`, `--marker-b-color`, `--marker-c-color`, and the C handle can take a different marker shape via `marker-style-c="dot"`.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Single Styles
|
|
163
|
+
|
|
164
|
+
The `single-style` attribute (single mode only) controls how the value bar is drawn.
|
|
165
|
+
|
|
166
|
+
### `range` (default)
|
|
167
|
+
|
|
168
|
+
The value bar grows from the start of the track to the value. Classic slider behaviour.
|
|
169
|
+
|
|
170
|
+
```html
|
|
171
|
+
<ixfx-slider-input single-style="range" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `bipolar`
|
|
175
|
+
|
|
176
|
+
The value bar is centred on the midpoint of `min`…`max`. Values below the midpoint grow leftward, values above grow rightward. A small tick is drawn at the centre. Suited for pan, balance, or any centred parameter.
|
|
177
|
+
|
|
178
|
+
```html
|
|
179
|
+
<ixfx-slider-input single-style="bipolar" min="-1" max="1" value-a="0.5"></ixfx-slider-input>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### `unipolar`
|
|
183
|
+
|
|
184
|
+
The value bar is symmetric around the centre, growing in both directions as the value increases. A marker is shown at *both* ends of the bar (low and high). A small tick is drawn at the centre. Click-to-set follows a V-shape: 0 at the centre, 1 at either end (clicking 25% in from the left or right sets value 0.5).
|
|
185
|
+
|
|
186
|
+
```html
|
|
187
|
+
<ixfx-slider-input single-style="unipolar" min="0" max="1" value-a="0.6"></ixfx-slider-input>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Stepping & Quantisation
|
|
193
|
+
|
|
194
|
+
`step` controls keyboard, wheel, and pointer step size:
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<ixfx-slider-input min="0" max="1" value-a="0.5" step="0.2"></ixfx-slider-input>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`stops` quantises the value to N equally-spaced positions. Drag, click, keyboard, and wheel input all snap:
|
|
201
|
+
|
|
202
|
+
```html
|
|
203
|
+
<!-- 5 discrete stops: 0, 0.2, 0.4, 0.6, 0.8, 1.0 -->
|
|
204
|
+
<ixfx-slider-input min="0" max="1" value-a="0.5" stops="5"></ixfx-slider-input>
|
|
205
|
+
|
|
206
|
+
<!-- 10 stops across 0-100: snaps to 0, 10, 20 … 100 -->
|
|
207
|
+
<ixfx-slider-input min="0" max="100" value-a="50" stops="10"></ixfx-slider-input>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
When `stops` is set it also governs the keyboard and wheel step (one stop per keypress).
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Events
|
|
215
|
+
|
|
216
|
+
Both events bubble and are `composed` (cross shadow-DOM boundaries).
|
|
217
|
+
|
|
218
|
+
### `input`
|
|
219
|
+
|
|
220
|
+
Fired continuously during user interaction (every pointer/key event that changes the value). Use for live updates.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import type { SliderInputInputEvent } from 'ixfx-slider-input/types';
|
|
224
|
+
|
|
225
|
+
slider.addEventListener('input', (e: SliderInputInputEvent) => {
|
|
226
|
+
const d = e.detail;
|
|
227
|
+
// d.value: number in min..max (single mode), or = valueMax in multi modes
|
|
228
|
+
// d.valueRelative: number 0..1
|
|
229
|
+
// d.valueMin, d.valueMax, d.valueMinRelative, d.valueMaxRelative: dual/triple
|
|
230
|
+
// d.valueC, d.valueCRelative: triple only
|
|
231
|
+
// d.changed: 'min' | 'max' | 'c' | 'both' (which pole(s) moved)
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### `change`
|
|
236
|
+
|
|
237
|
+
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.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import type { SliderInputChangeEvent } from 'ixfx-slider-input/types';
|
|
241
|
+
|
|
242
|
+
slider.addEventListener('change', (e: SliderInputChangeEvent) => {
|
|
243
|
+
const d = e.detail;
|
|
244
|
+
// Same shape as `input` plus previousValue / previousValueMin / etc.
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Note:** `input` fires only for user-initiated changes. Programmatic `slider.valueA = x` triggers only `change`.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## CSS Custom Properties
|
|
253
|
+
|
|
254
|
+
### Track
|
|
255
|
+
|
|
256
|
+
| Variable | Default | Description |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `--track-color` | `var(--surface-4)` | Stroke colour of the central track rect |
|
|
259
|
+
| `--track-width` | `2` | Stroke width of the track (SVG units) |
|
|
260
|
+
| `--track-fill` | `transparent` | Fill colour of the track rect |
|
|
261
|
+
|
|
262
|
+
```html
|
|
263
|
+
<ixfx-slider-input
|
|
264
|
+
min="0" max="100" value-a="60"
|
|
265
|
+
style="--track-width: 6; --track-color: hsl(280, 30%, 30%);"
|
|
266
|
+
></ixfx-slider-input>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Lanes
|
|
270
|
+
|
|
271
|
+
Lanes are line segments drawn over the track. The built-in lanes are `track` (the grey background) and `value` (the accent-coloured progress bar). Custom lanes can be added at runtime (see [Data Lanes](#data-lanes-programmatic-api)).
|
|
272
|
+
|
|
273
|
+
| Variable | Default | Description |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| `--lane-track-color` | `var(--surface-4)` | Colour of the `track` lane |
|
|
276
|
+
| `--lane-value-color` | `var(--accent)` | Colour of the `value` lane |
|
|
277
|
+
| `--lane-color` | `var(--accent)` | Fallback colour for custom lanes |
|
|
278
|
+
| `--lane-<key>-color` | — | Per-lane colour override (e.g. `--lane-sweep-color`) |
|
|
279
|
+
| `--lane-<key>-width` | — | Per-lane width override (e.g. `--lane-sweep-width`) |
|
|
280
|
+
| `--lane-dasharray` | `none` | SVG `stroke-dasharray` applied to all lanes |
|
|
281
|
+
| `--lane-dashoffset` | `0` | SVG `stroke-dashoffset` — animate for marching-ants effect |
|
|
282
|
+
|
|
283
|
+
Dashed lanes:
|
|
284
|
+
|
|
285
|
+
```html
|
|
286
|
+
<!-- 8px dashes, 4px gaps -->
|
|
287
|
+
<ixfx-slider-input style="--lane-dasharray: 8 4;" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
288
|
+
|
|
289
|
+
<!-- Dotted -->
|
|
290
|
+
<ixfx-slider-input style="--lane-dasharray: 2 4;" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Track + lanes styled together (independent of host dimensions)
|
|
294
|
+
|
|
295
|
+
```html
|
|
296
|
+
<ixfx-slider-input
|
|
297
|
+
min="0" max="100" value-a="60"
|
|
298
|
+
style="--track-width: 6; --lane-track-width: 0.4; --lane-value-width: 0.4;"
|
|
299
|
+
></ixfx-slider-input>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Marker (value indicator)
|
|
303
|
+
|
|
304
|
+
| Variable | Default | Description |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| `--marker-color` | `var(--accent)` | Colour of the needle line, dot fill, or triangle fill |
|
|
307
|
+
| `--marker-thickness` | `0.12` | Relative thickness (fraction of cross axis) |
|
|
308
|
+
| `--marker-length` | `0.8` | Needle length as fraction of cross axis (needle) |
|
|
309
|
+
| `--marker-offset` | `0` | Perpendicular offset from track centreline (needle/dot/triangle) |
|
|
310
|
+
| `--marker-border-width` | `0` | Border halo thickness around marker |
|
|
311
|
+
| `--marker-border-color` | `transparent` | Border halo colour |
|
|
312
|
+
| `--marker-dasharray` | `none` | Dashed needle pattern |
|
|
313
|
+
| `--marker-dashoffset` | `0` | Dashed needle offset |
|
|
314
|
+
|
|
315
|
+
Per-handle overrides in dual/triple modes follow the same convention: `--marker-a-color`, `--marker-b-color`, `--marker-c-color`.
|
|
316
|
+
|
|
317
|
+
### Midpoint marker (bipolar / unipolar modes only)
|
|
318
|
+
|
|
319
|
+
| Variable | Default | Description |
|
|
320
|
+
|---|---|---|
|
|
321
|
+
| `--midpoint-color` | `var(--surface-muted-text)` | Colour of the centre tick / dot |
|
|
322
|
+
| `--midpoint-thickness` | `1px` (needle) / `0.08` (dot) | Tick line width or dot radius fraction |
|
|
323
|
+
| `--midpoint-length` | `0.15` | Tick length as fraction of cross axis (needle) |
|
|
324
|
+
| `--midpoint-border-color` | `transparent` | Dot border colour (dot only) |
|
|
325
|
+
| `--midpoint-border-width` | `0` | Dot border width (dot only) |
|
|
326
|
+
|
|
327
|
+
### Dynamic value variables
|
|
328
|
+
|
|
329
|
+
The component sets `--value-relative` (a number 0…1) on the host as the value changes. In dual/triple modes it also sets `--value-min-relative`, `--value-max-relative`, and `--value-c-relative`. Use them inside `calc()` to create value-reactive styles entirely in CSS:
|
|
330
|
+
|
|
331
|
+
```html
|
|
332
|
+
<!-- Value bar hue shifts from purple to pink as value increases -->
|
|
333
|
+
<ixfx-slider-input
|
|
334
|
+
min="0" max="100" value-a="60"
|
|
335
|
+
style="--lane-value-color: hsl(calc(var(--value-relative) * 280), 80%, 55%);"
|
|
336
|
+
></ixfx-slider-input>
|
|
337
|
+
|
|
338
|
+
<!-- Marker hue follows value -->
|
|
339
|
+
<ixfx-slider-input
|
|
340
|
+
min="0" max="100" value-a="60"
|
|
341
|
+
style="--marker-color: hsl(calc(var(--value-relative) * 280), 80%, 55%);"
|
|
342
|
+
></ixfx-slider-input>
|
|
343
|
+
|
|
344
|
+
<!-- Marker thickness scales with value -->
|
|
345
|
+
<ixfx-slider-input
|
|
346
|
+
min="0" max="100" value-a="60"
|
|
347
|
+
style="--marker-thickness: calc(0.08 + var(--value-relative) * 0.25);"
|
|
348
|
+
></ixfx-slider-input>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Focus outline
|
|
352
|
+
|
|
353
|
+
| Variable | Default | Description |
|
|
354
|
+
|---|---|---|
|
|
355
|
+
| `--slider-input-svg-focus-outline` | `2px solid var(--accent)` | Outline colour and style applied to the SVG when focused via keyboard |
|
|
356
|
+
| `--slider-input-svg-focus-outline-offset` | `2px` | Outline offset |
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Marker Styles
|
|
361
|
+
|
|
362
|
+
### `needle` (default)
|
|
363
|
+
|
|
364
|
+
A line drawn perpendicular to the track. Spans `--marker-offset` (offset from centreline) to `--marker-length` (length, both as fractions of cross axis).
|
|
365
|
+
|
|
366
|
+
```html
|
|
367
|
+
<ixfx-slider-input marker-style="needle" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Thin minimalist look:
|
|
371
|
+
|
|
372
|
+
```html
|
|
373
|
+
<ixfx-slider-input
|
|
374
|
+
marker-style="needle" min="0" max="100" value-a="60"
|
|
375
|
+
style="--marker-thickness: 0.05; --marker-color: var(--surface-muted-text); --marker-length: 0.9; --marker-offset: 0.5;"
|
|
376
|
+
></ixfx-slider-input>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
With a border halo (useful for contrast against coloured lanes):
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
<ixfx-slider-input
|
|
383
|
+
marker-style="needle" min="0" max="100" value-a="60"
|
|
384
|
+
style="--marker-thickness: 0.1; --marker-border-width: 0.02; --marker-border-color: white;"
|
|
385
|
+
></ixfx-slider-input>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### `dot`
|
|
389
|
+
|
|
390
|
+
A filled circle placed at the value's track position. `--marker-offset` shifts it perpendicular to the track (0 = on the centreline, positive = away from the centreline in the track's perpendicular direction).
|
|
391
|
+
|
|
392
|
+
```html
|
|
393
|
+
<!-- Dot on the track -->
|
|
394
|
+
<ixfx-slider-input marker-style="dot" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
395
|
+
|
|
396
|
+
<!-- Dot offset above the track -->
|
|
397
|
+
<ixfx-slider-input
|
|
398
|
+
marker-style="dot" min="0" max="100" value-a="60"
|
|
399
|
+
style="--marker-offset: 0.5; --marker-thickness: 0.15;"
|
|
400
|
+
></ixfx-slider-input>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### `triangle`
|
|
404
|
+
|
|
405
|
+
A downward-pointing triangle. The apex sits on the value-track centreline at the value's track position; the base is offset *outward* by `--marker-length` (away from the centreline), so the shape hangs outside the value bar with its point facing in. Useful as a "data is here" indicator on a stacked-lane layout.
|
|
406
|
+
|
|
407
|
+
```html
|
|
408
|
+
<ixfx-slider-input
|
|
409
|
+
marker-style="triangle" min="0" max="100" value-a="60"
|
|
410
|
+
style="--marker-offset: 0.7; --marker-thickness: 0.3;"
|
|
411
|
+
></ixfx-slider-input>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### `none`
|
|
415
|
+
|
|
416
|
+
No marker rendered. Use when the value bar or lanes carry the visual.
|
|
417
|
+
|
|
418
|
+
```html
|
|
419
|
+
<ixfx-slider-input marker-style="none" min="0" max="100" value-a="60"></ixfx-slider-input>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Data Lanes (programmatic API)
|
|
425
|
+
|
|
426
|
+
Lanes are additional line segments drawn over (or offset from) the track. The two built-in lanes are `track` (grey background) and `value` (accent progress bar). You can add custom lanes at runtime.
|
|
427
|
+
|
|
428
|
+
### Lane types
|
|
429
|
+
|
|
430
|
+
| Type | Behaviour |
|
|
431
|
+
|---|---|
|
|
432
|
+
| `value` | Draws from `lane.valueMin` to `lane.valueMax` along the track. Update with `setLaneRange()` for ranges or `setLaneValue()` for a single position (single mode) |
|
|
433
|
+
| `fixed` | Always shows the full track — useful for static reference lines |
|
|
434
|
+
| `anchored` | Centred on the current value, with `lane.value` controlling its half-width (0..1 of total track) |
|
|
435
|
+
|
|
436
|
+
### API
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
import type { SliderInputElement } from 'ixfx-slider-input';
|
|
440
|
+
|
|
441
|
+
const slider = document.querySelector('ixfx-slider-input') as SliderInputElement;
|
|
442
|
+
|
|
443
|
+
await customElements.whenDefined('ixfx-slider-input');
|
|
444
|
+
await slider.updateComplete;
|
|
445
|
+
|
|
446
|
+
// Add a fixed reference line above the track
|
|
447
|
+
slider.addLane('target', {
|
|
448
|
+
type: 'fixed',
|
|
449
|
+
offset: 0.2, // 20% above the track centreline
|
|
450
|
+
width: 0.05, // thin line
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Add a value-type lane (will default to current value span)
|
|
454
|
+
slider.addLane('average', {
|
|
455
|
+
type: 'value',
|
|
456
|
+
value: 0.4, // initial 0..1 position
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// Add an anchored tolerance band centred on the current value
|
|
460
|
+
slider.addLane('tolerance', {
|
|
461
|
+
type: 'anchored',
|
|
462
|
+
value: 0.15, // band spans ±15% of total track
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Update a value-type lane
|
|
466
|
+
slider.setLaneValue('average', 0.6);
|
|
467
|
+
|
|
468
|
+
// Update a value-type lane's range (dual/triple band)
|
|
469
|
+
slider.setLaneRange('average', 0.2, 0.8);
|
|
470
|
+
|
|
471
|
+
// Remove a lane
|
|
472
|
+
slider.removeLane('target');
|
|
473
|
+
|
|
474
|
+
// Read lane state
|
|
475
|
+
const lane = slider.getLane('average');
|
|
476
|
+
const allLanes = slider.getLanes(); // ReadonlyMap<string, LaneData>
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Style lanes via CSS variables using the lane key:
|
|
480
|
+
|
|
481
|
+
```css
|
|
482
|
+
ixfx-slider-input {
|
|
483
|
+
--lane-average-color: hsl(120, 70%, 50%);
|
|
484
|
+
--lane-tolerance-color: hsl(180, 80%, 50%);
|
|
485
|
+
--lane-sweep-width: 0.06;
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Live lane animation example
|
|
490
|
+
|
|
491
|
+
From the demo — a sweep lane that oscillates between 0 and the current slider value:
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
let direction = 1;
|
|
495
|
+
setInterval(() => {
|
|
496
|
+
const sweep = slider.getLane('sweep');
|
|
497
|
+
if (!sweep) return;
|
|
498
|
+
const lo = sweep.valueMin ?? 0;
|
|
499
|
+
let nextLo = lo + direction * 0.02;
|
|
500
|
+
if (nextLo >= 0.7 || nextLo <= 0.05) {
|
|
501
|
+
direction *= -1;
|
|
502
|
+
nextLo = Math.max(0.05, Math.min(0.7, nextLo));
|
|
503
|
+
}
|
|
504
|
+
slider.setLaneRange('sweep', nextLo, nextLo + 0.3);
|
|
505
|
+
}, 100);
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
The lane's `offset` value (in `addLane({ offset })`) positions it perpendicular to the track. Positive values push the lane *away* from the centreline (above for horizontal, right of for vertical).
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Tooltip Integration
|
|
513
|
+
|
|
514
|
+
Set `data-tooltip` and `data-tooltip-live` to wire up a live tooltip that updates as the user drags:
|
|
515
|
+
|
|
516
|
+
```html
|
|
517
|
+
<ixfx-slider-input
|
|
518
|
+
min="0" max="100" value-a="50"
|
|
519
|
+
data-tooltip="50%"
|
|
520
|
+
data-tooltip-anchor="top"
|
|
521
|
+
data-tooltip-live="input"
|
|
522
|
+
></ixfx-slider-input>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
When `data-tooltip-live` is present the component automatically updates `data-tooltip` to the current percentage (e.g. `"63%"`) on every `input` event. In dual/triple modes the tooltip reflects the value at the max pole (parity with native `<input type="range">`).
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## CSS Parts
|
|
530
|
+
|
|
531
|
+
Lane paths and marker groups are exposed as CSS shadow parts for styling from outside the shadow DOM:
|
|
532
|
+
|
|
533
|
+
```css
|
|
534
|
+
ixfx-slider-input::part(lane) { … } /* all lanes */
|
|
535
|
+
ixfx-slider-input::part(lane-track) { … } /* track lane */
|
|
536
|
+
ixfx-slider-input::part(lane-value) { … } /* value lane */
|
|
537
|
+
ixfx-slider-input::part(lane-sweep) { … } /* a custom lane */
|
|
538
|
+
ixfx-slider-input::part(value-marker) { … } /* all handles' marker group */
|
|
539
|
+
ixfx-slider-input::part(value-marker-a) { … } /* dual/triple: A handle */
|
|
540
|
+
ixfx-slider-input::part(value-marker-b) { … } /* dual/triple: B handle */
|
|
541
|
+
ixfx-slider-input::part(value-marker-c) { … } /* triple: C handle */
|
|
542
|
+
ixfx-slider-input::part(midpoint-marker) { … } /* bipolar/unipolar centre tick */
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Vertical Orientation
|
|
548
|
+
|
|
549
|
+
Set `orientation="vertical"`. The component swaps width and height (24×200 default) and inverts the click-to-set / drag direction so the **top of the track = high value** and the **bottom = low value**. The host's CSS dimensions need to match the new layout (typically swap width ↔ height and tweak one to match the visual thickness you want):
|
|
550
|
+
|
|
551
|
+
```html
|
|
552
|
+
<ixfx-slider-input
|
|
553
|
+
orientation="vertical" min="0" max="100" value-a="50"
|
|
554
|
+
style="width: 24px; height: 200px;"
|
|
555
|
+
></ixfx-slider-input>
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The track, value bar, and markers all use the same `CROSS` axis for their perpendicular dimension, so all CSS variables (e.g. `--marker-thickness`, `--lane-track-width`) work identically in both orientations.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Styling Recipes
|
|
563
|
+
|
|
564
|
+
### Purple theme
|
|
565
|
+
|
|
566
|
+
```css
|
|
567
|
+
#my-slider {
|
|
568
|
+
--lane-value-color: hsl(280, 100%, 60%);
|
|
569
|
+
--lane-track-color: hsl(280, 30%, 30%);
|
|
570
|
+
--marker-color: hsl(280, 100%, 70%);
|
|
571
|
+
--marker-thickness: 0.3;
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Animated marching-ants track
|
|
576
|
+
|
|
577
|
+
```css
|
|
578
|
+
@keyframes march {
|
|
579
|
+
to { --lane-dashoffset: -30; }
|
|
580
|
+
}
|
|
581
|
+
#my-slider {
|
|
582
|
+
--lane-dasharray: 20 10;
|
|
583
|
+
animation: march 1s linear infinite;
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Value-reactive hue (single mode)
|
|
588
|
+
|
|
589
|
+
```html
|
|
590
|
+
<ixfx-slider-input
|
|
591
|
+
min="0" max="100" value-a="60"
|
|
592
|
+
style="--marker-color: hsl(calc((1 - var(--value-relative)) * 280), 80%, 55%);"
|
|
593
|
+
></ixfx-slider-input>
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### SVG gradient on the value bar
|
|
597
|
+
|
|
598
|
+
Define gradients in a hidden `<svg>` at the top of the page using `gradientUnits="userSpaceOnUse"` anchored to the slider's viewBox, then reference them via `--lane-value-color`:
|
|
599
|
+
|
|
600
|
+
```html
|
|
601
|
+
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
|
602
|
+
<defs>
|
|
603
|
+
<linearGradient id="slider-grad-h" gradientUnits="userSpaceOnUse" x1="5" y1="0" x2="95" y2="0">
|
|
604
|
+
<stop offset="0%" stop-color="oklch(70% 0.22 25)" />
|
|
605
|
+
<stop offset="100%" stop-color="oklch(75% 0.22 145)" />
|
|
606
|
+
</linearGradient>
|
|
607
|
+
</defs>
|
|
608
|
+
</svg>
|
|
609
|
+
<ixfx-slider-input
|
|
610
|
+
min="0" max="100" value-a="60"
|
|
611
|
+
style="--lane-value-color: url(#slider-grad-h);"
|
|
612
|
+
></ixfx-slider-input>
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Implementation Notes
|
|
618
|
+
|
|
619
|
+
These are lessons learned from building the component — useful context when building a similar thing from scratch.
|
|
620
|
+
|
|
621
|
+
### SVG in a fixed `100×24` viewBox
|
|
622
|
+
|
|
623
|
+
All geometry is computed in a fixed `100×24` viewBox coordinate space for horizontal, or `24×100` for vertical, with the track running along the long axis. The `CROSS = 24` constant sets the perpendicular dimension, and `LONG = 100` sets the long axis. The SVG `viewBox` attribute handles scaling to actual DOM pixels, so every calculation stays coordinate-system-agnostic. The path is always `(viewBox-relative) × (host size / viewBox size)` regardless of the host's rendered size.
|
|
624
|
+
|
|
625
|
+
### Lane offset needs a CSS unit
|
|
626
|
+
|
|
627
|
+
`transform: translateY(${offset})` (unitless) is **invalid** CSS — a `<length>` is required, otherwise the whole `transform` declaration is dropped and the lane renders on the centreline. The code uses `translateY(${offset * CROSS / 2}px)` so the offset is expressed in viewBox units (with `px` suffix for CSS), making the offset stable across host sizes.
|
|
628
|
+
|
|
629
|
+
### Marker rendering uses `part` + per-handle CSS variable patterns
|
|
630
|
+
|
|
631
|
+
Each marker group has `part="value-marker"` (or `value-marker-<handle>` in multi modes) and reads its colour from a `var(--marker-<handle>-color, var(--marker-color, var(--accent)))` chain. The two-tier fallback means `--marker-color` styles all markers globally, while `--marker-a-color` / `--marker-b-color` / `--marker-c-color` can override per handle. The same pattern is used for the marker-style override (`--marker-style-c`) and for lane widths (`--lane-<key>-width`).
|
|
632
|
+
|
|
633
|
+
The `<defs>` + `<use>` pattern with `markerBorderWidth > 0` produces a halo: the shape is pushed into a single root `<defs>` (so it's not rendered twice) and referenced twice via `<use>` — once as `class="marker-border"` (slightly thicker stroke) and once as `class="marker-fill"` (the actual colour). The same approach handles both needle (`<path>`) and dot/triangle (`<circle>` / `<polygon>`) shapes.
|
|
634
|
+
|
|
635
|
+
### `#quantise` runs at the set-value boundary
|
|
636
|
+
|
|
637
|
+
`#quantise(value)` is the single internal entry point for snapping: it applies `stops` first (priority), then `step` (if `stops` not set), and returns the raw value otherwise. Because every value-setting path (`#setSingleValueFromRelative`, `#setDualOrTripleValueFromRelative`, `#stepHandleByRelativeDelta`, `#applyLengthDragFromRelative`) funnels through it, snapping is universal — drag, click, keyboard, wheel, and length-drag all quantise identically.
|
|
638
|
+
|
|
639
|
+
### WillUpdate initialises the value lane before the first render
|
|
640
|
+
|
|
641
|
+
In multi-handle modes, the internal `#poleA` / `#poleB` / `#poleC` 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. The triple-mode `valueC` defaults to the midpoint of A and B, matching `radial-input`'s deferred lane-correction pattern.
|
|
642
|
+
|
|
643
|
+
### `value-c-follow="proportional"` keeps C anchored to the band
|
|
644
|
+
|
|
645
|
+
When A or B moves, the C handle's fractional position within the *old* band is recomputed against the *new* band, so C tracks the band automatically across continuous drags. Because `#resolvePoleChange` and `#applyLengthDragFromRelative` both use the same `oldLo/oldHi → newLo/newHi` formula, the behaviour is consistent across pointer-drag, keyboard nudges, wheel, and length-drag.
|
|
646
|
+
|
|
647
|
+
### Length-drag preserves the grab offset
|
|
648
|
+
|
|
649
|
+
The bar between A and B in dual/triple modes is draggable when `value-drag !== "none"`. `#applyLengthDragFromRelative` computes the delta against `#lengthDragStartRelative` (the host-relative position at `pointerdown`), not against the low edge. Without this, the low edge would jump under the cursor on grab. The same `#lengthDragStartRelative` is captured in `#onPointerDown` when `chosenHandle === 'length'`.
|
|
650
|
+
|
|
651
|
+
### Dead-zone clicks are no-ops when `value-drag` is enabled
|
|
652
|
+
|
|
653
|
+
In `#onPointerDown`'s multi-handle hit-testing, the "click outside the band jumps the nearest handle" branches are gated on `valueDrag === "none"`. When `value-drag` is on, the only interactions that change values are grabbing a handle directly or grabbing the bar itself. This makes the click target clear: outside the band does nothing.
|
|
654
|
+
|
|
655
|
+
### Midpoint marker only renders in bipolar / unipolar single mode
|
|
656
|
+
|
|
657
|
+
`#renderMidpointMarker()` is a no-op unless `valueMode === "single"` and `singleStyle` is `"bipolar"` or `"unipolar"`. In those modes a small tick is drawn at the track centre to anchor the symmetric value bar.
|
|
658
|
+
|
|
659
|
+
### Unipolar shows two markers (low + high end)
|
|
660
|
+
|
|
661
|
+
For unipolar single mode, `#renderValueMarker` computes a `lowCenter` reflection of the high end across `bounds.centre` and emits a second marker shape at that position. The low-end marker uses the same `--marker-color` / `--marker-thickness` / `--marker-length` as the high end, so the existing CSS customisation patterns work unchanged.
|
|
662
|
+
|
|
663
|
+
### Unipolar click-to-set uses a V-shape mapping
|
|
664
|
+
|
|
665
|
+
`#onPointerDown` and `#onPointerMove` remap the host-relative click position to the value via `Math.abs(2 * relativeValue - 1)` when in unipolar single mode: 0 at the centre, 1 at either end. So clicking 25% in from the left or right sets value 0.5, and clicking at the centre sets value 0. The mapping is *not* applied to cursor hit-testing (the cursor logic uses the raw host-relative position so it can find the low/high markers at `0.5 ± value/2`).
|
|
666
|
+
|
|
667
|
+
### `disconnectedCallback` cleans up timers and global listeners
|
|
668
|
+
|
|
669
|
+
The debounce timer and the `pointermove` / `pointerup` / `pointercancel` window listeners are both cleaned up in `disconnectedCallback`. The pointer cleanup is achieved by calling `#onPointerUp()` directly, which resets `#dragging`, releases pointer capture, and removes the window listeners. Forgetting this causes a memory leak and a stuck-dragging bug if an element is removed mid-drag.
|
|
670
|
+
|
|
671
|
+
### Wheel handling is platform-agnostic
|
|
672
|
+
|
|
673
|
+
The `WheelNudgeMixin` consults `event.deltaY` first, falling back to `event.deltaX` when `deltaY` is 0. This matters on Mac, where shift+scroll is interpreted as horizontal scrolling and the browser sends `deltaX` with `deltaY=0`; without the fallback, shift+wheel on Mac would always read direction as `-1` (because `0 < 0` is false). The slider's `_applyWheelDelta` then inverts the sign so that "wheel forward = value up" works on both Windows and Mac.
|