@ixfx/components 0.4.3 → 0.4.4

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 (225) hide show
  1. package/bundle/index.d.ts +395 -1176
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +7699 -20648
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.d.ts +12 -7
  6. package/dist/ac-text.d.ts.map +1 -1
  7. package/dist/ac-text.js +2 -3
  8. package/dist/ac-text.js.map +1 -1
  9. package/dist/{button-C3fLMKUv.js → button-DxYee1ZH.js} +2 -2
  10. package/dist/{button-C3fLMKUv.js.map → button-DxYee1ZH.js.map} +1 -1
  11. package/dist/button.d.ts +1 -2
  12. package/dist/button.d.ts.map +1 -1
  13. package/dist/button.js +1 -2
  14. package/dist/checkbox.d.ts +0 -1
  15. package/dist/checkbox.d.ts.map +1 -1
  16. package/dist/checkbox.js +1 -1
  17. package/dist/checkbox.js.map +1 -1
  18. package/dist/{colour-picker-0sgAha1T.js → colour-picker-BacNRabo.js} +14 -16
  19. package/dist/colour-picker-BacNRabo.js.map +1 -0
  20. package/dist/colour-picker.d.ts +1 -1
  21. package/dist/colour-picker.js +1 -1
  22. package/dist/crumbs.d.ts +5 -2
  23. package/dist/crumbs.d.ts.map +1 -1
  24. package/dist/crumbs.js +45 -20
  25. package/dist/crumbs.js.map +1 -1
  26. package/dist/data-display.d.ts +1 -2
  27. package/dist/data-display.d.ts.map +1 -1
  28. package/dist/data-display.js +25 -23
  29. package/dist/data-display.js.map +1 -1
  30. package/dist/{data-provider-DgI909FP.js → data-provider-35TyWclX.js} +2 -2
  31. package/dist/data-provider-35TyWclX.js.map +1 -0
  32. package/dist/{decorate-DdjvVHS-.js → decorate-VRK8rslU.js} +2 -2
  33. package/dist/{decorate-DdjvVHS-.js.map → decorate-VRK8rslU.js.map} +1 -1
  34. package/dist/editable-label.d.ts +1 -2
  35. package/dist/editable-label.d.ts.map +1 -1
  36. package/dist/editable-label.js +1 -1
  37. package/dist/editable-label.js.map +1 -1
  38. package/dist/{hex-editor-WZrjRtvL.js → hex-editor-Bk6rxhfg.js} +9 -11
  39. package/dist/hex-editor-Bk6rxhfg.js.map +1 -0
  40. package/dist/{hex-editor-CMIxtNgT.d.ts → hex-editor-Caq8vNQk.d.ts} +2 -3
  41. package/dist/hex-editor-Caq8vNQk.d.ts.map +1 -0
  42. package/dist/hex.d.ts +1 -1
  43. package/dist/hex.js +1 -1
  44. package/dist/highlight-B-eQrhhb.js.map +1 -1
  45. package/dist/{icon-Ndo40kNO.js → icon-B22XVlCt.js} +6 -4
  46. package/dist/{icon-Ndo40kNO.js.map → icon-B22XVlCt.js.map} +1 -1
  47. package/dist/{icon-Bd5BiU4b.d.ts → icon-DpA9x2Ve.d.ts} +2 -3
  48. package/dist/icon-DpA9x2Ve.d.ts.map +1 -0
  49. package/dist/icons.d.ts +2 -2
  50. package/dist/icons.js +1 -1
  51. package/dist/{incr-search-CeERfijK.js → incr-search-2pz21xtR.js} +9 -6
  52. package/dist/incr-search-2pz21xtR.js.map +1 -0
  53. package/dist/incr-search.d.ts +1 -1
  54. package/dist/incr-search.js +1 -1
  55. package/dist/{index-CowKi2mo.d.ts → index-BeuCXtiO.d.ts} +3 -4
  56. package/dist/index-BeuCXtiO.d.ts.map +1 -0
  57. package/dist/{index-D87kRcEs.d.ts → index-BtGFYfaK.d.ts} +2 -1
  58. package/dist/index-BtGFYfaK.d.ts.map +1 -0
  59. package/dist/{index-s_IhSnwc.d.ts → index-CoqtMSd9.d.ts} +6 -4
  60. package/dist/index-CoqtMSd9.d.ts.map +1 -0
  61. package/dist/{index-9fGVAg1d.d.ts → index-Cxwxtj6g.d.ts} +3 -3
  62. package/dist/index-Cxwxtj6g.d.ts.map +1 -0
  63. package/dist/index-D_ftz_IT.d.ts +184 -0
  64. package/dist/index-D_ftz_IT.d.ts.map +1 -0
  65. package/dist/{index-_RTQSKHi.d.ts → index-DfEkTGvT.d.ts} +11 -11
  66. package/dist/index-DfEkTGvT.d.ts.map +1 -0
  67. package/dist/index.d.ts +44 -29
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +304 -3075
  70. package/dist/index.js.map +1 -1
  71. package/dist/keyboard-B5xD7Fz-.js +36 -0
  72. package/dist/keyboard-B5xD7Fz-.js.map +1 -0
  73. package/dist/{labelled-input-base-ORb1rGq6.d.ts → labelled-input-base-DiTme1JO.d.ts} +1 -2
  74. package/dist/labelled-input-base-DiTme1JO.d.ts.map +1 -0
  75. package/dist/{labelled-input-base-9K_w3oLj.js → labelled-input-base-hoKGwyKT.js} +2 -2
  76. package/dist/{labelled-input-base-9K_w3oLj.js.map → labelled-input-base-hoKGwyKT.js.map} +1 -1
  77. package/dist/labelled-radial-input.d.ts +5 -4
  78. package/dist/labelled-radial-input.d.ts.map +1 -1
  79. package/dist/labelled-radial-input.js +7 -3
  80. package/dist/labelled-radial-input.js.map +1 -1
  81. package/dist/labelled-range-input.d.ts +1 -1
  82. package/dist/labelled-range-input.js +27 -3
  83. package/dist/labelled-range-input.js.map +1 -1
  84. package/dist/led.d.ts +1 -2
  85. package/dist/led.d.ts.map +1 -1
  86. package/dist/led.js +1 -1
  87. package/dist/led.js.map +1 -1
  88. package/dist/{menu-vjoiHZ13.js → menu-g6yd7ddN.js} +8 -9
  89. package/dist/menu-g6yd7ddN.js.map +1 -0
  90. package/dist/{menu-item-COx-Azgj.d.ts → menu-item-DSyf9f_L.d.ts} +4 -4
  91. package/dist/menu-item-DSyf9f_L.d.ts.map +1 -0
  92. package/dist/{menu-item-BrIuORYD.js → menu-item-NUKwJ4s5.js} +3 -4
  93. package/dist/{menu-item-BrIuORYD.js.map → menu-item-NUKwJ4s5.js.map} +1 -1
  94. package/dist/menu.d.ts +2 -2
  95. package/dist/menu.js +2 -2
  96. package/dist/miller.d.ts +8 -5
  97. package/dist/miller.d.ts.map +1 -1
  98. package/dist/miller.js +22 -24
  99. package/dist/miller.js.map +1 -1
  100. package/dist/narrowed-text.d.ts +7 -3
  101. package/dist/narrowed-text.d.ts.map +1 -1
  102. package/dist/narrowed-text.js +73 -47
  103. package/dist/narrowed-text.js.map +1 -1
  104. package/dist/panel.d.ts +1 -1
  105. package/dist/panel.d.ts.map +1 -1
  106. package/dist/panel.js +2 -2
  107. package/dist/panel.js.map +1 -1
  108. package/dist/plots.d.ts +2 -2
  109. package/dist/plots.js +1 -1
  110. package/dist/polar-pad.d.ts +15 -3
  111. package/dist/polar-pad.d.ts.map +1 -1
  112. package/dist/polar-pad.js +64 -11
  113. package/dist/polar-pad.js.map +1 -1
  114. package/dist/{radial-input-CpQPs1Mp.d.ts → radial-input-DXMN55Nz.d.ts} +6 -4
  115. package/dist/radial-input-DXMN55Nz.d.ts.map +1 -0
  116. package/dist/{radial-input-DZ7eQCcM.js → radial-input-DxCrt_NE.js} +41 -19
  117. package/dist/radial-input-DxCrt_NE.js.map +1 -0
  118. package/dist/radial-input.d.ts +1 -1
  119. package/dist/radial-input.js +1 -1
  120. package/dist/range-input.d.ts +15 -4
  121. package/dist/range-input.d.ts.map +1 -1
  122. package/dist/range-input.js +23 -2
  123. package/dist/range-input.js.map +1 -1
  124. package/dist/range.d.ts +15 -4
  125. package/dist/range.d.ts.map +1 -1
  126. package/dist/range.js +23 -2
  127. package/dist/range.js.map +1 -1
  128. package/dist/selecthorizontal.d.ts +1 -2
  129. package/dist/selecthorizontal.d.ts.map +1 -1
  130. package/dist/selecthorizontal.js +1 -1
  131. package/dist/selecthorizontal.js.map +1 -1
  132. package/dist/slider-input-DFpG23bS.js +1592 -0
  133. package/dist/slider-input-DFpG23bS.js.map +1 -0
  134. package/dist/slider-input-XppH3vcy.d.ts +160 -0
  135. package/dist/slider-input-XppH3vcy.d.ts.map +1 -0
  136. package/dist/slider-input.d.ts +2 -0
  137. package/dist/slider-input.js +2 -0
  138. package/dist/snap-container.d.ts +1 -2
  139. package/dist/snap-container.d.ts.map +1 -1
  140. package/dist/snap-container.js +6 -5
  141. package/dist/snap-container.js.map +1 -1
  142. package/dist/split-layout.d.ts +1 -2
  143. package/dist/split-layout.d.ts.map +1 -1
  144. package/dist/split-layout.js +6 -4
  145. package/dist/split-layout.js.map +1 -1
  146. package/dist/swipe.d.ts +12 -2
  147. package/dist/swipe.d.ts.map +1 -1
  148. package/dist/swipe.js +22 -3
  149. package/dist/swipe.js.map +1 -1
  150. package/dist/{tab-list-DLzb-m-I.d.ts → tab-list-BohmbvqF.d.ts} +14 -5
  151. package/dist/tab-list-BohmbvqF.d.ts.map +1 -0
  152. package/dist/tab-list-DzknpdSo.js +592 -0
  153. package/dist/tab-list-DzknpdSo.js.map +1 -0
  154. package/dist/tabs.d.ts +2 -2
  155. package/dist/tabs.js +1 -208
  156. package/dist/{tickled-controller-BjYCzGrU.d.ts → tickled-controller-BF2Ea0DE.d.ts} +2 -31
  157. package/dist/tickled-controller-BF2Ea0DE.d.ts.map +1 -0
  158. package/dist/tickled-styles-fNDdqf6l.js.map +1 -1
  159. package/dist/{timeline-2OFtN4Ta.js → timeline-B2fgDL4U.js} +37 -1438
  160. package/dist/timeline-B2fgDL4U.js.map +1 -0
  161. package/dist/timeline.d.ts +1 -1
  162. package/dist/timeline.js +1 -1
  163. package/dist/{tooltip-RGVIwHrZ.d.ts → tooltip-BlvBUQxx.d.ts} +3 -3
  164. package/dist/tooltip-BlvBUQxx.d.ts.map +1 -0
  165. package/dist/{tree-D84RUgFC.js → tree-BnvzhsAw.js} +8 -9
  166. package/dist/tree-BnvzhsAw.js.map +1 -0
  167. package/dist/{tree-component-BaCSPWg1.d.ts → tree-component-DCmmKZ4j.d.ts} +8 -124
  168. package/dist/tree-component-DCmmKZ4j.d.ts.map +1 -0
  169. package/dist/tree.d.ts +10 -4
  170. package/dist/tree.d.ts.map +1 -1
  171. package/dist/tree.js +1 -1
  172. package/dist/types-DO3gt5vg.d.ts +130 -0
  173. package/dist/types-DO3gt5vg.d.ts.map +1 -0
  174. package/dist/wheel-nudge-BlSCe_hD.js +86 -0
  175. package/dist/wheel-nudge-BlSCe_hD.js.map +1 -0
  176. package/dist/wheel-nudge-DGrNzWuE.d.ts +29 -0
  177. package/dist/wheel-nudge-DGrNzWuE.d.ts.map +1 -0
  178. package/dist/{xy-axis-BeWRMmbo.d.ts → xy-axis-5KhVTNoX.d.ts} +30 -7
  179. package/dist/xy-axis-5KhVTNoX.d.ts.map +1 -0
  180. package/dist/xy-axis-CxHC8z5E.js +1849 -0
  181. package/dist/xy-axis-CxHC8z5E.js.map +1 -0
  182. package/dist/xy-pad.d.ts +13 -2
  183. package/dist/xy-pad.d.ts.map +1 -1
  184. package/dist/xy-pad.js +47 -6
  185. package/dist/xy-pad.js.map +1 -1
  186. package/docs-user/radial-input.md +2 -2
  187. package/package.json +21 -20
  188. package/dist/colour-C3MQIjFJ-vRCdr8W-.js +0 -3732
  189. package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +0 -1
  190. package/dist/colour-picker-0sgAha1T.js.map +0 -1
  191. package/dist/data-provider-DgI909FP.js.map +0 -1
  192. package/dist/dist-CA13FvMn.js +0 -980
  193. package/dist/dist-CA13FvMn.js.map +0 -1
  194. package/dist/hex-editor-CMIxtNgT.d.ts.map +0 -1
  195. package/dist/hex-editor-WZrjRtvL.js.map +0 -1
  196. package/dist/icon-Bd5BiU4b.d.ts.map +0 -1
  197. package/dist/incr-search-CeERfijK.js.map +0 -1
  198. package/dist/index-9fGVAg1d.d.ts.map +0 -1
  199. package/dist/index-CowKi2mo.d.ts.map +0 -1
  200. package/dist/index-D87kRcEs.d.ts.map +0 -1
  201. package/dist/index-DgXpCg_G.d.ts +0 -1244
  202. package/dist/index-DgXpCg_G.d.ts.map +0 -1
  203. package/dist/index-_RTQSKHi.d.ts.map +0 -1
  204. package/dist/index-s_IhSnwc.d.ts.map +0 -1
  205. package/dist/keyboard-CimiEskD.js +0 -20
  206. package/dist/keyboard-CimiEskD.js.map +0 -1
  207. package/dist/labelled-input-base-ORb1rGq6.d.ts.map +0 -1
  208. package/dist/menu-item-COx-Azgj.d.ts.map +0 -1
  209. package/dist/menu-vjoiHZ13.js.map +0 -1
  210. package/dist/radial-input-CpQPs1Mp.d.ts.map +0 -1
  211. package/dist/radial-input-DZ7eQCcM.js.map +0 -1
  212. package/dist/tab-list-DLzb-m-I.d.ts.map +0 -1
  213. package/dist/tabs.js.map +0 -1
  214. package/dist/tickled-controller-BjYCzGrU.d.ts.map +0 -1
  215. package/dist/timeline-2OFtN4Ta.js.map +0 -1
  216. package/dist/tooltip-RGVIwHrZ.d.ts.map +0 -1
  217. package/dist/tree-D84RUgFC.js.map +0 -1
  218. package/dist/tree-component-BaCSPWg1.d.ts.map +0 -1
  219. package/dist/types-DSfbtHs3.d.ts +0 -8
  220. package/dist/types-DSfbtHs3.d.ts.map +0 -1
  221. package/dist/xy-axis-BeWRMmbo.d.ts.map +0 -1
  222. package/dist/xy-axis-no0OL_O1.js +0 -7893
  223. package/dist/xy-axis-no0OL_O1.js.map +0 -1
  224. /package/bundle/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
  225. /package/dist/{chunk-D7D4PA-g.js → rolldown-runtime-D7D4PA-g.js} +0 -0
@@ -0,0 +1,1592 @@
1
+ import { t as themeFallbacks } from "./fallbacks-pNMOMx38.js";
2
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
3
+ import { t as prominenceStyles } from "./prominence-DlFShic3.js";
4
+ import { t as resolveAxisStep } from "./keyboard-B5xD7Fz-.js";
5
+ import { t as WheelNudgeMixin } from "./wheel-nudge-BlSCe_hD.js";
6
+ import { LitElement, css, html, svg } from "lit";
7
+ import { property } from "lit/decorators.js";
8
+ /**
9
+ * Get the track bounds in viewBox units for a given orientation.
10
+ */
11
+ function getTrackBounds(orientation) {
12
+ if (orientation === `horizontal`) return {
13
+ trackStart: 5,
14
+ trackEnd: 95,
15
+ centre: 50,
16
+ crossCentre: 12
17
+ };
18
+ return {
19
+ trackStart: 5,
20
+ trackEnd: 95,
21
+ centre: 50,
22
+ crossCentre: 12
23
+ };
24
+ }
25
+ /**
26
+ * Map a relative value (0..1) to a leader point on the track for the given
27
+ * orientation and single-style.
28
+ *
29
+ * Range: grows from trackStart to trackEnd.
30
+ * Bipolar: track centre is 0.5; values spread from centre outward.
31
+ * Unipolar: spread symmetrically around centre.
32
+ *
33
+ * For vertical orientation, higher values are toward the top of the track,
34
+ * matching the spec ("higher values → top of track").
35
+ */
36
+ function valueToPosition(relativeValue, singleStyle, trackBounds, orientation) {
37
+ const span = trackBounds.trackEnd - trackBounds.trackStart;
38
+ const centre = trackBounds.centre;
39
+ const crossCentre = trackBounds.crossCentre;
40
+ let pos;
41
+ if (singleStyle === `range`) pos = trackBounds.trackStart + relativeValue * span;
42
+ else if (singleStyle === `bipolar`) {
43
+ if (relativeValue <= .5) {
44
+ const t = relativeValue / .5;
45
+ pos = trackBounds.trackStart + t * (centre - trackBounds.trackStart);
46
+ } else pos = centre + (relativeValue - .5) / .5 * (trackBounds.trackEnd - centre);
47
+ } else pos = centre + relativeValue * span / 2;
48
+ if (orientation === `horizontal`) return {
49
+ x: pos,
50
+ y: crossCentre
51
+ };
52
+ return {
53
+ x: crossCentre,
54
+ y: 100 - pos
55
+ };
56
+ }
57
+ /**
58
+ * Get the segment endpoints for a 'value' lane. The lane is drawn from
59
+ * the lane's lower relative value to its upper relative value.
60
+ *
61
+ * For 'range' and 'bipolar' styles, the endpoints are obtained by mapping
62
+ * each value through `valueToPosition`. For 'unipolar', the bar is centred
63
+ * on the track centre with length proportional to valueMax (valueMin is
64
+ * ignored), matching the spec: "spreads symmetrically around centre".
65
+ */
66
+ function getValueSegment(valueMin, valueMax, singleStyle, trackBounds, orientation) {
67
+ if (singleStyle === `unipolar`) {
68
+ const span = trackBounds.trackEnd - trackBounds.trackStart;
69
+ const centre = trackBounds.centre;
70
+ const crossCentre = trackBounds.crossCentre;
71
+ const halfLen = valueMax * span / 2;
72
+ if (orientation === `horizontal`) return {
73
+ start: {
74
+ x: centre - halfLen,
75
+ y: crossCentre
76
+ },
77
+ end: {
78
+ x: centre + halfLen,
79
+ y: crossCentre
80
+ }
81
+ };
82
+ return {
83
+ start: {
84
+ x: crossCentre,
85
+ y: centre - halfLen
86
+ },
87
+ end: {
88
+ x: crossCentre,
89
+ y: centre + halfLen
90
+ }
91
+ };
92
+ }
93
+ return {
94
+ start: valueToPosition(valueMin, singleStyle, trackBounds, orientation),
95
+ end: valueToPosition(valueMax, singleStyle, trackBounds, orientation)
96
+ };
97
+ }
98
+ /**
99
+ * Get the segment endpoints for an 'anchored' lane. The lane is centered
100
+ * on the current value, with length controlled by laneValue (0..1).
101
+ */
102
+ function getAnchoredSegment(relativeValue, laneValue, singleStyle, trackBounds, orientation) {
103
+ const halfLen = laneValue * (trackBounds.trackEnd - trackBounds.trackStart) / 2;
104
+ const centre = singleStyle === `unipolar` ? orientation === `horizontal` ? {
105
+ x: trackBounds.centre,
106
+ y: trackBounds.crossCentre
107
+ } : {
108
+ x: trackBounds.crossCentre,
109
+ y: trackBounds.centre
110
+ } : valueToPosition(relativeValue, singleStyle, trackBounds, orientation);
111
+ let startPos;
112
+ let endPos;
113
+ if (orientation === `horizontal`) {
114
+ startPos = centre.x - halfLen;
115
+ endPos = centre.x + halfLen;
116
+ } else {
117
+ startPos = centre.y - halfLen;
118
+ endPos = centre.y + halfLen;
119
+ }
120
+ return {
121
+ start: orientation === `horizontal` ? {
122
+ x: startPos,
123
+ y: centre.y
124
+ } : {
125
+ x: centre.x,
126
+ y: startPos
127
+ },
128
+ end: orientation === `horizontal` ? {
129
+ x: endPos,
130
+ y: centre.y
131
+ } : {
132
+ x: centre.x,
133
+ y: endPos
134
+ }
135
+ };
136
+ }
137
+ /**
138
+ * Generate an SVG path string for a straight line. Returns '' for a
139
+ * zero-length segment (start ≈ end), mirroring the radial-input arc
140
+ * helper's edge case.
141
+ */
142
+ function generateLinePath(start, end) {
143
+ if (Math.abs(start.x - end.x) < .001 && Math.abs(start.y - end.y) < .001) return ``;
144
+ return `M ${start.x} ${start.y} L ${end.x} ${end.y}`;
145
+ }
146
+ /**
147
+ * Clamp `value` to the [min, max] range.
148
+ */
149
+ function clamp(value, min, max) {
150
+ return Math.min(Math.max(value, min), max);
151
+ }
152
+ /**
153
+ * Linear-reinterpolate `value` from [fromMin, fromMax] to [toMin, toMax].
154
+ */
155
+ function scale(value, fromMin, fromMax, toMin, toMax) {
156
+ if (fromMax === fromMin) return toMin;
157
+ return toMin + (value - fromMin) / (fromMax - fromMin) * (toMax - toMin);
158
+ }
159
+ /**
160
+ * Convert an absolute value to a relative (0..1) position.
161
+ */
162
+ function valueToRelative(value, min, max) {
163
+ return scale(value, min, max, 0, 1);
164
+ }
165
+ /**
166
+ * Convert a pointer client position (clientX or clientY depending on
167
+ * orientation) to a relative value (0..1) given the host element's
168
+ * bounding rect on the long axis.
169
+ */
170
+ function clientPosToRelative(clientPos, hostStart, hostSize) {
171
+ if (hostSize <= 0) return 0;
172
+ return clamp((clientPos - hostStart) / hostSize, 0, 1);
173
+ }
174
+ //#endregion
175
+ //#region src/slider-input/slider-input.ts
176
+ const HANDLE_HIT_RADIUS = .06;
177
+ const DEFAULT_VALUE_MIN_FRACTION = .01;
178
+ const CHANGE_DEBOUNCE_MS = 150;
179
+ let SliderInputElement = class SliderInputElement extends WheelNudgeMixin(LitElement) {
180
+ #lanes;
181
+ /** Raw pole values: independent of which is "low" or "high". */
182
+ #poleA;
183
+ #poleB;
184
+ #poleC;
185
+ #dragging;
186
+ /** Pointer ID being tracked (fixes radial-input's hardcoded pointerId). */
187
+ #activePointerId;
188
+ #dragStartClientPos;
189
+ #poleValueAtDragStart;
190
+ /** Which handle is currently being dragged, or 'length' for segment drag. */
191
+ #dragHandle;
192
+ /** Snapshotted poles at length-drag start. */
193
+ #lengthDragStart;
194
+ /** Track the low/high identity at length-drag start (so we know which raw pole gets which end). */
195
+ #lengthDragLowKey;
196
+ #lengthDragHighKey;
197
+ #lengthDragLength;
198
+ #lengthDragStartLow;
199
+ /**
200
+ * Where the pointer was when the length drag began (in host-relative
201
+ * 0..1 terms). Preserves the grab offset so dragging the bar doesn't snap
202
+ * its low edge under the cursor.
203
+ */
204
+ #lengthDragStartRelative;
205
+ /** The handle that last received direct interaction (pointerdown/keydown/wheel/focus). */
206
+ #lastInteractedHandle;
207
+ /**
208
+ * Last pointer client position seen by #onPointerMoveCursor, used to
209
+ * recompute the cursor after a drag ends (without waiting for a fresh
210
+ * pointermove).
211
+ */
212
+ #lastPointerClient;
213
+ #changeDebounceTimer;
214
+ #isUserInteraction;
215
+ #hasInitialisedValueC;
216
+ /**
217
+ * Holds `<linearGradient>`/`<radialGradient>` elements authored as light-DOM
218
+ * children of this element, once relocated into the shadow root (see
219
+ * `#syncGradientDefs`). Lets consumers reference `url(#id)` paint servers
220
+ * from `--lane-*-color` / `--track-color` etc. — CSS custom properties
221
+ * pierce the shadow boundary, but `url(#id)` paint-server *resolution* does
222
+ * not, so the gradient definitions themselves must physically live inside
223
+ * this element's shadow root to be resolvable by lanes/track painted there.
224
+ */
225
+ #userGradientDefs;
226
+ #gradientObserver;
227
+ constructor() {
228
+ super();
229
+ this.valueA = 50;
230
+ this.valueB = 0;
231
+ this.valueC = void 0;
232
+ this.min = 0;
233
+ this.max = 100;
234
+ this.step = void 0;
235
+ this.stops = void 0;
236
+ this.valueMode = `single`;
237
+ this.singleStyle = `range`;
238
+ this.orientation = `horizontal`;
239
+ this.markerStyle = `needle`;
240
+ this.markerStyleC = void 0;
241
+ this.crossover = `allow`;
242
+ this.valueDrag = `none`;
243
+ this.valueCFollow = `none`;
244
+ this.valueMin = 1;
245
+ this.disabled = false;
246
+ this.#lanes = /* @__PURE__ */ new Map();
247
+ this.#poleA = 50;
248
+ this.#poleB = 0;
249
+ this.#poleC = void 0;
250
+ this.#dragging = false;
251
+ this.#activePointerId = void 0;
252
+ this.#dragStartClientPos = 0;
253
+ this.#poleValueAtDragStart = 0;
254
+ this.#dragHandle = void 0;
255
+ this.#lengthDragStart = void 0;
256
+ this.#lengthDragLowKey = void 0;
257
+ this.#lengthDragHighKey = void 0;
258
+ this.#lengthDragLength = 0;
259
+ this.#lengthDragStartLow = 0;
260
+ this.#lengthDragStartRelative = 0;
261
+ this.#lastInteractedHandle = `a`;
262
+ this.#lastPointerClient = {
263
+ x: 0,
264
+ y: 0
265
+ };
266
+ this.#changeDebounceTimer = void 0;
267
+ this.#isUserInteraction = false;
268
+ this.#hasInitialisedValueC = false;
269
+ this.#onHostKeyDown = (event) => {
270
+ this.#onKeyDown(event);
271
+ };
272
+ this.#syncGradientDefs = () => {
273
+ const found = this.querySelectorAll(`linearGradient, radialGradient`);
274
+ if (found.length === 0) return;
275
+ const defs = this.#ensureUserGradientDefs();
276
+ for (const node of found) defs.append(node);
277
+ };
278
+ this.#onPointerDown = (event) => {
279
+ if (this.disabled) return;
280
+ const svg = this.renderRoot.querySelector(`svg.slider-svg`);
281
+ if (!svg) return;
282
+ event.preventDefault();
283
+ this.focus();
284
+ svg.style.cursor = `grabbing`;
285
+ const rect = this.getBoundingClientRect();
286
+ const isVertical = this.orientation === `vertical`;
287
+ const clientPos = isVertical ? event.clientY : event.clientX;
288
+ const rawRelative = clientPosToRelative(clientPos, isVertical ? rect.top : rect.left, isVertical ? rect.height : rect.width);
289
+ let relativeValue = isVertical ? 1 - rawRelative : rawRelative;
290
+ if (this.valueMode === `single` && this.singleStyle === `unipolar`) relativeValue = Math.abs(2 * relativeValue - 1);
291
+ if (this.valueMode === `single`) {
292
+ this.#lastInteractedHandle = `a`;
293
+ this.#dragHandle = `a`;
294
+ this.#setSingleValueFromRelative(relativeValue, true);
295
+ this.#dragging = true;
296
+ this.toggleAttribute(`dragging`, true);
297
+ this.#activePointerId = event.pointerId;
298
+ try {
299
+ svg.setPointerCapture(event.pointerId);
300
+ } catch {}
301
+ this.#dragStartClientPos = clientPos;
302
+ this.#poleValueAtDragStart = this.valueA;
303
+ window.addEventListener(`pointermove`, this.#onPointerMove);
304
+ window.addEventListener(`pointerup`, this.#onPointerUp);
305
+ window.addEventListener(`pointercancel`, this.#onPointerUp);
306
+ return;
307
+ }
308
+ const aRel = this.#getRelativeValueA();
309
+ const bRel = this.#getRelativeValueB();
310
+ const lo = Math.min(aRel, bRel);
311
+ const hi = Math.max(aRel, bRel);
312
+ const aHandle = this.#poleA <= this.#poleB ? `a` : `b`;
313
+ const bHandle = this.#poleA > this.#poleB ? `a` : `b`;
314
+ const posA = aRel;
315
+ const posB = bRel;
316
+ const isTriple = this.valueMode === `triple`;
317
+ const posC = isTriple ? this.#getRelativeValueC() : 0;
318
+ const nearA = Math.abs(relativeValue - posA) < HANDLE_HIT_RADIUS;
319
+ const nearB = Math.abs(relativeValue - posB) < HANDLE_HIT_RADIUS;
320
+ const nearC = isTriple && Math.abs(relativeValue - posC) < HANDLE_HIT_RADIUS;
321
+ let chosenHandle;
322
+ const pickNonLast = (candidates) => {
323
+ return candidates.find((h) => h !== this.#lastInteractedHandle) ?? candidates[0];
324
+ };
325
+ if (nearA && nearB && nearC) chosenHandle = pickNonLast([
326
+ aHandle,
327
+ bHandle,
328
+ `c`
329
+ ]);
330
+ else if (nearA && nearB) chosenHandle = pickNonLast([aHandle, bHandle]);
331
+ else if (nearA && nearC) chosenHandle = pickNonLast([aHandle, `c`]);
332
+ else if (nearB && nearC) chosenHandle = pickNonLast([bHandle, `c`]);
333
+ else if (nearA) chosenHandle = aHandle;
334
+ else if (nearB) chosenHandle = bHandle;
335
+ else if (nearC) chosenHandle = `c`;
336
+ else if (this.valueDrag !== `none` && relativeValue > lo && relativeValue < hi) chosenHandle = `length`;
337
+ else if (this.valueDrag === `none` && relativeValue <= lo) chosenHandle = this.#lowKey();
338
+ else if (this.valueDrag === `none` && relativeValue >= hi) chosenHandle = this.#highKey();
339
+ else chosenHandle = void 0;
340
+ this.#dragging = true;
341
+ this.toggleAttribute(`dragging`, true);
342
+ this.#activePointerId = event.pointerId;
343
+ try {
344
+ svg.setPointerCapture(event.pointerId);
345
+ } catch {}
346
+ this.#dragStartClientPos = clientPos;
347
+ if (chosenHandle === void 0) {
348
+ window.addEventListener(`pointermove`, this.#onPointerMove);
349
+ window.addEventListener(`pointerup`, this.#onPointerUp);
350
+ window.addEventListener(`pointercancel`, this.#onPointerUp);
351
+ return;
352
+ }
353
+ this.#dragHandle = chosenHandle;
354
+ if (chosenHandle === `length`) {
355
+ this.#lengthDragStart = {
356
+ poleA: this.#poleA,
357
+ poleB: this.#poleB
358
+ };
359
+ this.#lengthDragLowKey = this.#lowKey();
360
+ this.#lengthDragHighKey = this.#highKey();
361
+ this.#lengthDragLength = this.#poleB - this.#poleA < 0 ? this.#poleA - this.#poleB : this.#poleB - this.#poleA;
362
+ this.#lengthDragStartLow = Math.min(this.#poleA, this.#poleB);
363
+ this.#lengthDragStartRelative = relativeValue;
364
+ } else {
365
+ this.#lastInteractedHandle = chosenHandle;
366
+ this.#poleValueAtDragStart = chosenHandle === `a` ? this.#poleA : this.#poleB;
367
+ this.#setDualOrTripleValueFromRelative(relativeValue, chosenHandle, true);
368
+ }
369
+ window.addEventListener(`pointermove`, this.#onPointerMove);
370
+ window.addEventListener(`pointerup`, this.#onPointerUp);
371
+ window.addEventListener(`pointercancel`, this.#onPointerUp);
372
+ };
373
+ this.#onPointerMove = (event) => {
374
+ if (!this.#dragging) return;
375
+ event.preventDefault();
376
+ const rect = this.getBoundingClientRect();
377
+ const isVertical = this.orientation === `vertical`;
378
+ const rawRelative = clientPosToRelative(isVertical ? event.clientY : event.clientX, isVertical ? rect.top : rect.left, isVertical ? rect.height : rect.width);
379
+ let relativeValue = isVertical ? 1 - rawRelative : rawRelative;
380
+ if (this.valueMode === `single` && this.singleStyle === `unipolar`) relativeValue = Math.abs(2 * relativeValue - 1);
381
+ if (this.valueMode === `single`) {
382
+ this.#setSingleValueFromRelative(relativeValue, true);
383
+ return;
384
+ }
385
+ if (this.#dragHandle === `length`) this.#applyLengthDragFromRelative(relativeValue);
386
+ else if (this.#dragHandle) this.#setDualOrTripleValueFromRelative(relativeValue, this.#dragHandle, true);
387
+ };
388
+ this.#onPointerUp = () => {
389
+ this.#dragging = false;
390
+ this.#dragHandle = void 0;
391
+ this.#lengthDragStart = void 0;
392
+ this.#lengthDragLowKey = void 0;
393
+ this.#lengthDragHighKey = void 0;
394
+ this.toggleAttribute(`dragging`, false);
395
+ this.#onPointerMoveCursor(new PointerEvent(`pointermove`, {
396
+ clientX: this.#lastPointerClient.x,
397
+ clientY: this.#lastPointerClient.y
398
+ }));
399
+ const svg = this.renderRoot.querySelector(`svg.slider-svg`);
400
+ if (svg && this.#activePointerId !== void 0 && `hasPointerCapture` in svg && typeof svg.hasPointerCapture === `function`) {
401
+ if (svg.hasPointerCapture.call(svg, this.#activePointerId)) try {
402
+ svg.releasePointerCapture(this.#activePointerId);
403
+ } catch {}
404
+ }
405
+ this.#activePointerId = void 0;
406
+ window.removeEventListener(`pointermove`, this.#onPointerMove);
407
+ window.removeEventListener(`pointerup`, this.#onPointerUp);
408
+ window.removeEventListener(`pointercancel`, this.#onPointerUp);
409
+ };
410
+ this.#onKeyDown = (event) => {
411
+ if (this.disabled) return;
412
+ const range = this.max - this.min;
413
+ const effectiveStep = resolveAxisStep(range, this.stops, this.step, event.shiftKey);
414
+ let handle = `a`;
415
+ const target = event.target;
416
+ if (target && typeof target.closest === `function`) {
417
+ const handleEl = target.closest(`[data-handle]`);
418
+ if (handleEl) {
419
+ const h = handleEl.dataset?.handle;
420
+ if (h === `a` || h === `b` || h === `c`) handle = h;
421
+ }
422
+ }
423
+ if (this.valueMode === `single`) handle = `a`;
424
+ this.#lastInteractedHandle = handle;
425
+ let handled = false;
426
+ switch (event.key) {
427
+ case `ArrowUp`:
428
+ case `ArrowRight`:
429
+ if (this.valueMode === `single`) {
430
+ const r = this.#getRelativeValueA() + effectiveStep / range;
431
+ this.#setSingleValueFromRelative(clamp(r, 0, 1), true);
432
+ } else {
433
+ const r = valueToRelative(handle === `a` ? this.#poleA : handle === `b` ? this.#poleB : this.#poleC ?? this.#low(), this.min, this.max) + effectiveStep / range;
434
+ this.#stepHandleByRelativeDelta(handle, r);
435
+ }
436
+ handled = true;
437
+ break;
438
+ case `ArrowDown`:
439
+ case `ArrowLeft`:
440
+ if (this.valueMode === `single`) {
441
+ const r = this.#getRelativeValueA() - effectiveStep / range;
442
+ this.#setSingleValueFromRelative(clamp(r, 0, 1), true);
443
+ } else {
444
+ const r = valueToRelative(handle === `a` ? this.#poleA : handle === `b` ? this.#poleB : this.#poleC ?? this.#low(), this.min, this.max) - effectiveStep / range;
445
+ this.#stepHandleByRelativeDelta(handle, r);
446
+ }
447
+ handled = true;
448
+ break;
449
+ case `Home`:
450
+ if (this.valueMode === `single`) this.#setSingleValueFromRelative(0, true);
451
+ else this.#stepHandleByRelativeDelta(handle, 0);
452
+ handled = true;
453
+ break;
454
+ case `End`:
455
+ if (this.valueMode === `single`) this.#setSingleValueFromRelative(1, true);
456
+ else this.#stepHandleByRelativeDelta(handle, 1);
457
+ handled = true;
458
+ }
459
+ if (handled) event.preventDefault();
460
+ };
461
+ this.#onFocusIn = (event) => {
462
+ const target = event.target;
463
+ if (target && typeof target.closest === `function`) {
464
+ const handleEl = target.closest(`[data-handle]`);
465
+ if (handleEl) {
466
+ const h = handleEl.dataset?.handle;
467
+ if (h === `a` || h === `b` || h === `c`) this.#lastInteractedHandle = h;
468
+ }
469
+ }
470
+ };
471
+ this.#onPointerMoveCursor = (event) => {
472
+ const svg = this.renderRoot.querySelector(`svg.slider-svg`);
473
+ if (!svg) return;
474
+ this.#lastPointerClient = {
475
+ x: event.clientX,
476
+ y: event.clientY
477
+ };
478
+ const rect = this.getBoundingClientRect();
479
+ const isVertical = this.orientation === `vertical`;
480
+ const rawRelative = clientPosToRelative(isVertical ? event.clientY : event.clientX, isVertical ? rect.top : rect.left, isVertical ? rect.height : rect.width);
481
+ const relativeValue = isVertical ? 1 - rawRelative : rawRelative;
482
+ const cursor = this.#cursorAtRelative(relativeValue);
483
+ if (svg.style.cursor !== cursor) svg.style.cursor = cursor;
484
+ };
485
+ this.#onPointerLeaveCursor = () => {
486
+ const svg = this.renderRoot.querySelector(`svg.slider-svg`);
487
+ if (!svg) return;
488
+ const cursor = this.disabled ? `not-allowed` : `default`;
489
+ if (svg.style.cursor !== cursor) svg.style.cursor = cursor;
490
+ };
491
+ if (!this.disabled) this.setAttribute(`tabindex`, `0`);
492
+ this.#lanes.set(`track`, {
493
+ key: `track`,
494
+ value: 1,
495
+ type: `fixed`,
496
+ offset: 0,
497
+ width: .16
498
+ });
499
+ this.#lanes.set(`value`, {
500
+ key: `value`,
501
+ valueMin: 0,
502
+ valueMax: 0,
503
+ type: `value`,
504
+ offset: 0,
505
+ width: .24
506
+ });
507
+ }
508
+ connectedCallback() {
509
+ super.connectedCallback();
510
+ this.setAttribute(`tabindex`, this.disabled ? `-1` : `0`);
511
+ this.addEventListener(`keydown`, this.#onHostKeyDown);
512
+ this.#syncGradientDefs();
513
+ this.#gradientObserver = new MutationObserver(this.#syncGradientDefs);
514
+ this.#gradientObserver.observe(this, {
515
+ childList: true,
516
+ subtree: true
517
+ });
518
+ }
519
+ #onHostKeyDown;
520
+ /**
521
+ * Lazily creates the inert `<svg><defs>` carrier that user-authored
522
+ * gradients get moved into. Kept entirely outside Lit's managed template —
523
+ * it's a plain sibling of the interactive `<svg class="slider-svg">` inside
524
+ * the shadow root, so `url(#id)` references from lane/track paint
525
+ * properties resolve against it regardless of render() re-runs.
526
+ */
527
+ #ensureUserGradientDefs() {
528
+ if (this.#userGradientDefs) return this.#userGradientDefs;
529
+ const svgNs = `http://www.w3.org/2000/svg`;
530
+ const carrier = document.createElementNS(svgNs, `svg`);
531
+ carrier.setAttribute(`width`, `0`);
532
+ carrier.setAttribute(`height`, `0`);
533
+ carrier.setAttribute(`aria-hidden`, `true`);
534
+ carrier.style.cssText = `position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none;`;
535
+ const defs = document.createElementNS(svgNs, `defs`);
536
+ carrier.append(defs);
537
+ this.renderRoot.append(carrier);
538
+ this.#userGradientDefs = defs;
539
+ return defs;
540
+ }
541
+ /**
542
+ * Moves any `<linearGradient>`/`<radialGradient>` authored as light-DOM
543
+ * descendants of this element into the shadow root (see
544
+ * `#ensureUserGradientDefs`). This *reparents* rather than clones the
545
+ * nodes, so consumers get normal, live SVG paint-server semantics — e.g.
546
+ * animating a `<stop>`'s `stop-color` "just works" — with no gradient
547
+ * management on the component's part. Idempotent: once moved, a node is no
548
+ * longer a light-DOM descendant of `this`, so it won't be found again.
549
+ */
550
+ #syncGradientDefs;
551
+ addLane(key, options) {
552
+ const rel = this.#getRelativeValueA();
553
+ this.#lanes.set(key, {
554
+ key,
555
+ value: options?.value ?? rel,
556
+ valueMin: options?.valueMin ?? 0,
557
+ valueMax: options?.valueMax ?? rel,
558
+ type: options?.type ?? `value`,
559
+ offset: options?.offset ?? 0,
560
+ width: options?.width ?? .1
561
+ });
562
+ this.requestUpdate();
563
+ }
564
+ setLaneValue(key, value) {
565
+ const lane = this.#lanes.get(key);
566
+ if (lane) {
567
+ lane.value = clamp(value, 0, 1);
568
+ this.requestUpdate();
569
+ }
570
+ }
571
+ setLaneRange(key, valueMin, valueMax) {
572
+ const lane = this.#lanes.get(key);
573
+ if (!lane) return;
574
+ const lo = clamp(Math.min(valueMin, valueMax), 0, 1);
575
+ const hi = clamp(Math.max(valueMin, valueMax), 0, 1);
576
+ lane.valueMin = lo;
577
+ lane.valueMax = hi;
578
+ this.requestUpdate();
579
+ }
580
+ removeLane(key) {
581
+ this.#lanes.delete(key);
582
+ this.requestUpdate();
583
+ }
584
+ getLanes() {
585
+ return new Map(this.#lanes);
586
+ }
587
+ getLane(key) {
588
+ return this.#lanes.get(key);
589
+ }
590
+ #getRelativeValueA() {
591
+ return valueToRelative(this.valueA, this.min, this.max);
592
+ }
593
+ #getRelativeValueB() {
594
+ return valueToRelative(this.valueB, this.min, this.max);
595
+ }
596
+ #getRelativeValueC() {
597
+ return valueToRelative(this.valueC ?? (this.#poleA + this.#poleB) / 2, this.min, this.max);
598
+ }
599
+ #low() {
600
+ return Math.min(this.#poleA, this.#poleB);
601
+ }
602
+ #high() {
603
+ return Math.max(this.#poleA, this.#poleB);
604
+ }
605
+ #lowKey() {
606
+ return this.#poleA <= this.#poleB ? `a` : `b`;
607
+ }
608
+ #highKey() {
609
+ return this.#poleA > this.#poleB ? `a` : `b`;
610
+ }
611
+ /**
612
+ * Effective `valueMin` in value units between A and B. Mirrors radial-input's
613
+ * default of 1% of max-min when the property holds the literal minimum.
614
+ */
615
+ #getEffectiveValueMin() {
616
+ const range = this.max - this.min;
617
+ if (this.valueMin && this.valueMin > 0) return this.valueMin;
618
+ return range * DEFAULT_VALUE_MIN_FRACTION;
619
+ }
620
+ #quantise(value) {
621
+ if (this.stops && this.stops > 1) {
622
+ const step = (this.max - this.min) / this.stops;
623
+ return clamp(Math.round((value - this.min) / step) * step + this.min, this.min, this.max);
624
+ }
625
+ if (this.step !== void 0 && this.step > 0) return clamp(Math.round((value - this.min) / this.step) * this.step + this.min, this.min, this.max);
626
+ return value;
627
+ }
628
+ #setSingleValueFromRelative(relativeValue, isUserInteraction = false) {
629
+ const newValue = scale(relativeValue, 0, 1, this.min, this.max);
630
+ const clampedValue = this.#quantise(clamp(newValue, this.min, this.max));
631
+ if (clampedValue === this.valueA) return;
632
+ const previousValue = this.valueA;
633
+ this.valueA = clampedValue;
634
+ this.#poleA = clampedValue;
635
+ this.#isUserInteraction = isUserInteraction;
636
+ const valueLane = this.#lanes.get(`value`);
637
+ if (valueLane && valueLane.type === `value`) this.#applySingleStyleValueLane(valueLane, this.#getRelativeValueA());
638
+ if (this.hasAttribute(`data-tooltip-live`)) {
639
+ const percentage = Math.round(this.#getRelativeValueA() * 100);
640
+ this.setAttribute(`data-tooltip`, `${percentage}%`);
641
+ }
642
+ this.#emitInputAndChange({ previousValue }, isUserInteraction);
643
+ }
644
+ #setDualOrTripleValueFromRelative(relativeValue, handle, isUserInteraction = false) {
645
+ const clamped = clamp(scale(relativeValue, 0, 1, this.min, this.max), this.min, this.max);
646
+ const result = this.#resolvePoleChange(handle, clamped, this.#lastInteractedHandle);
647
+ if (!result) return;
648
+ const previous = { ...this.#poles() };
649
+ this.#poleA = result.poleA;
650
+ this.#poleB = result.poleB;
651
+ if (result.poleC !== void 0) this.#poleC = result.poleC;
652
+ this.#syncPoleProperties();
653
+ const changed = this.#detectChanged(previous, this.#poles());
654
+ this.#isUserInteraction = isUserInteraction;
655
+ const valueLane = this.#lanes.get(`value`);
656
+ if (valueLane && valueLane.type === `value`) {
657
+ valueLane.valueMin = valueToRelative(this.#low(), this.min, this.max);
658
+ valueLane.valueMax = valueToRelative(this.#high(), this.min, this.max);
659
+ }
660
+ if (this.hasAttribute(`data-tooltip-live`)) {
661
+ const percentage = Math.round(valueToRelative(this.#high(), this.min, this.max) * 100);
662
+ this.setAttribute(`data-tooltip`, `${percentage}%`);
663
+ }
664
+ this.#emitInputAndChange({
665
+ previousValue: Math.max(previous.poleA, previous.poleB),
666
+ previousValueMin: Math.min(previous.poleA, previous.poleB),
667
+ previousValueMax: Math.max(previous.poleA, previous.poleB),
668
+ previousValueC: previous.poleC
669
+ }, isUserInteraction, changed);
670
+ }
671
+ #poles() {
672
+ return {
673
+ poleA: this.#poleA,
674
+ poleB: this.#poleB,
675
+ poleC: this.#poleC
676
+ };
677
+ }
678
+ #syncPoleProperties() {
679
+ this.valueA = this.#poleA;
680
+ this.valueB = this.#poleB;
681
+ if (this.valueMode === `triple` && this.#poleC !== void 0) {
682
+ this.valueC = this.#poleC;
683
+ this.#hasInitialisedValueC = true;
684
+ }
685
+ }
686
+ #resolvePoleChange(handle, proposed, originalHandle) {
687
+ let poleA = this.#poleA;
688
+ let poleB = this.#poleB;
689
+ let poleC = this.#poleC;
690
+ if (this.valueMode === `triple` && handle === `c`) {
691
+ const clamped = clamp(proposed, this.#low(), this.#high());
692
+ const quantised = this.#quantise(clamped);
693
+ if (quantised === this.#poleC) return null;
694
+ return {
695
+ poleA,
696
+ poleB,
697
+ poleC: quantised
698
+ };
699
+ }
700
+ const gap = this.#getEffectiveValueMin();
701
+ const other = handle === `a` ? poleB : poleA;
702
+ let desired = this.#quantise(clamp(proposed, this.min, this.max));
703
+ if (this.crossover === `none`) {
704
+ if ((handle === `a` ? poleA : poleB) <= other) desired = Math.min(desired, other - gap);
705
+ else desired = Math.max(desired, other + gap);
706
+ } else if (desired > other - gap && desired < other + gap) desired = Math.abs(desired - (other - gap)) <= Math.abs(desired - (other + gap)) ? other - gap : other + gap;
707
+ desired = clamp(desired, this.min, this.max);
708
+ if (handle === `a`) {
709
+ if (desired === poleA) return null;
710
+ poleA = desired;
711
+ } else {
712
+ if (desired === poleB) return null;
713
+ poleB = desired;
714
+ }
715
+ if (this.valueMode === `triple` && poleC !== void 0) {
716
+ const oldLo = Math.min(this.#poleA, this.#poleB);
717
+ const oldHi = Math.max(this.#poleA, this.#poleB);
718
+ const newLo = Math.min(poleA, poleB);
719
+ const newHi = Math.max(poleA, poleB);
720
+ if (this.valueCFollow === `proportional` && oldHi > oldLo) poleC = newLo + clamp((poleC - oldLo) / (oldHi - oldLo), 0, 1) * (newHi - newLo);
721
+ else {
722
+ if (poleC < newLo) poleC = newLo;
723
+ if (poleC > newHi) poleC = newHi;
724
+ }
725
+ }
726
+ return {
727
+ poleA,
728
+ poleB,
729
+ poleC
730
+ };
731
+ }
732
+ #detectChanged(prev, curr) {
733
+ const aDiff = prev.poleA !== curr.poleA;
734
+ const bDiff = prev.poleB !== curr.poleB;
735
+ const cDiff = prev.poleC !== curr.poleC;
736
+ if (this.valueMode === `triple`) {
737
+ if (aDiff && bDiff && !cDiff) return `both`;
738
+ if (aDiff && bDiff) return `both`;
739
+ if (aDiff) return `min`;
740
+ if (bDiff) return `max`;
741
+ if (cDiff) return `c`;
742
+ return `min`;
743
+ }
744
+ if (aDiff && bDiff) return `both`;
745
+ if (aDiff) return `min`;
746
+ if (bDiff) return `max`;
747
+ return `min`;
748
+ }
749
+ #emitInputAndChange(previous, isUserInteraction, changed) {
750
+ const detailInput = this.#buildInputDetail(changed);
751
+ const detailChange = this.#buildChangeDetail(previous, changed);
752
+ if (isUserInteraction) {
753
+ this.dispatchEvent(new CustomEvent(`input`, {
754
+ detail: detailInput,
755
+ bubbles: true,
756
+ composed: true
757
+ }));
758
+ this.#scheduleChangeEvent(detailChange);
759
+ } else this.#fireChangeEvent(detailChange);
760
+ }
761
+ #buildInputDetail(changed) {
762
+ if (this.valueMode === `single`) return {
763
+ value: this.valueA,
764
+ valueRelative: this.#getRelativeValueA()
765
+ };
766
+ const lo = this.#low();
767
+ const hi = this.#high();
768
+ const valueMinRel = valueToRelative(lo, this.min, this.max);
769
+ const valueMaxRel = valueToRelative(hi, this.min, this.max);
770
+ if (this.valueMode === `triple` && this.#poleC !== void 0) return {
771
+ value: hi,
772
+ valueRelative: valueMaxRel,
773
+ valueMin: lo,
774
+ valueMax: hi,
775
+ valueMinRelative: valueMinRel,
776
+ valueMaxRelative: valueMaxRel,
777
+ valueC: this.#poleC,
778
+ valueCRelative: valueToRelative(this.#poleC, this.min, this.max),
779
+ changed
780
+ };
781
+ return {
782
+ value: hi,
783
+ valueRelative: valueMaxRel,
784
+ valueMin: lo,
785
+ valueMax: hi,
786
+ valueMinRelative: valueMinRel,
787
+ valueMaxRelative: valueMaxRel,
788
+ changed
789
+ };
790
+ }
791
+ #buildChangeDetail(previous, changed) {
792
+ if (this.valueMode === `single`) return {
793
+ value: this.valueA,
794
+ valueRelative: this.#getRelativeValueA(),
795
+ previousValue: previous.previousValue
796
+ };
797
+ const lo = this.#low();
798
+ const hi = this.#high();
799
+ const valueMaxRel = valueToRelative(hi, this.min, this.max);
800
+ if (this.valueMode === `triple` && this.#poleC !== void 0) return {
801
+ value: hi,
802
+ valueRelative: valueMaxRel,
803
+ previousValue: previous.previousValue,
804
+ valueMin: lo,
805
+ valueMax: hi,
806
+ previousValueMin: previous.previousValueMin ?? lo,
807
+ previousValueMax: previous.previousValueMax ?? hi,
808
+ valueC: this.#poleC,
809
+ previousValueC: previous.previousValueC ?? this.#poleC,
810
+ changed
811
+ };
812
+ return {
813
+ value: hi,
814
+ valueRelative: valueMaxRel,
815
+ previousValue: previous.previousValue,
816
+ valueMin: lo,
817
+ valueMax: hi,
818
+ previousValueMin: previous.previousValueMin ?? lo,
819
+ previousValueMax: previous.previousValueMax ?? hi,
820
+ changed
821
+ };
822
+ }
823
+ #scheduleChangeEvent(detail) {
824
+ if (this.#changeDebounceTimer !== void 0) clearTimeout(this.#changeDebounceTimer);
825
+ this.#changeDebounceTimer = window.setTimeout(() => {
826
+ this.#fireChangeEvent(detail);
827
+ this.#changeDebounceTimer = void 0;
828
+ }, CHANGE_DEBOUNCE_MS);
829
+ }
830
+ #fireChangeEvent(detail) {
831
+ this.dispatchEvent(new CustomEvent(`change`, {
832
+ detail,
833
+ bubbles: true,
834
+ composed: true
835
+ }));
836
+ }
837
+ #getCssVar(name, defaultValue) {
838
+ const style = getComputedStyle(this);
839
+ const val = style.getPropertyValue(name).trim();
840
+ if (!val) return defaultValue;
841
+ if (val.includes(`calc(`)) {
842
+ const valueRelative = style.getPropertyValue(`--value-relative`).trim();
843
+ if (!valueRelative) {
844
+ const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));
845
+ return Number.isNaN(parsed) ? defaultValue : parsed;
846
+ }
847
+ const evalStr = val.replace(/var\(--value-relative\)/g, valueRelative);
848
+ try {
849
+ const result = new Function(`return ${evalStr.replace(/calc\s*\(([^)]+)\)/g, `$1`)}`)();
850
+ return typeof result === `number` && !Number.isNaN(result) ? result : defaultValue;
851
+ } catch {
852
+ const parsed = Number.parseFloat(val.replace(/[^0-9.-]/g, ``));
853
+ return Number.isNaN(parsed) ? defaultValue : parsed;
854
+ }
855
+ }
856
+ const parsed = Number.parseFloat(val);
857
+ return Number.isNaN(parsed) ? defaultValue : parsed;
858
+ }
859
+ #onPointerDown;
860
+ #onPointerMove;
861
+ #applyLengthDragFromRelative(relativeValue) {
862
+ if (!this.#lengthDragStart) return;
863
+ const range = this.max - this.min;
864
+ const deltaValue = (relativeValue - this.#lengthDragStartRelative) * range;
865
+ let proposedLow = this.#lengthDragStartLow + deltaValue;
866
+ let proposedHigh = proposedLow + this.#lengthDragLength;
867
+ if (this.valueDrag === `stop`) {
868
+ const originalLength = this.#lengthDragLength;
869
+ let candidateLow = proposedLow;
870
+ let candidateHigh = proposedLow + originalLength;
871
+ if (candidateLow < this.min) {
872
+ const maxDelta = this.min - this.#lengthDragStartLow;
873
+ candidateLow = this.#lengthDragStartLow + maxDelta;
874
+ candidateHigh = candidateLow + originalLength;
875
+ }
876
+ if (candidateHigh > this.max) {
877
+ const shift = this.max - candidateHigh;
878
+ candidateLow += shift;
879
+ candidateHigh += shift;
880
+ }
881
+ proposedLow = candidateLow;
882
+ proposedHigh = candidateHigh;
883
+ } else if (this.valueDrag === `flex`) {
884
+ proposedLow = clamp(proposedLow, this.min, this.max);
885
+ proposedHigh = clamp(proposedHigh, this.min, this.max);
886
+ }
887
+ const lowKey = this.#lengthDragLowKey ?? this.#lowKey();
888
+ this.#lengthDragHighKey ?? this.#highKey();
889
+ const previous = this.#poles();
890
+ const newPoleA = lowKey === `a` ? proposedLow : proposedHigh;
891
+ const newPoleB = lowKey === `b` ? proposedLow : proposedHigh;
892
+ this.#poleA = newPoleA;
893
+ this.#poleB = newPoleB;
894
+ if (this.valueMode === `triple` && this.#poleC !== void 0) {
895
+ const oldLo = Math.min(previous.poleA, previous.poleB);
896
+ const oldHi = Math.max(previous.poleA, previous.poleB);
897
+ const newLo = Math.min(newPoleA, newPoleB);
898
+ const newHi = Math.max(newPoleA, newPoleB);
899
+ if (this.valueCFollow === `proportional` && oldHi > oldLo) {
900
+ const frac = clamp((this.#poleC - oldLo) / (oldHi - oldLo), 0, 1);
901
+ this.#poleC = newLo + frac * (newHi - newLo);
902
+ } else {
903
+ if (this.#poleC < newLo) this.#poleC = newLo;
904
+ if (this.#poleC > newHi) this.#poleC = newHi;
905
+ }
906
+ }
907
+ this.#syncPoleProperties();
908
+ const changed = `both`;
909
+ this.#isUserInteraction = true;
910
+ const valueLane = this.#lanes.get(`value`);
911
+ if (valueLane && valueLane.type === `value`) {
912
+ valueLane.valueMin = valueToRelative(this.#low(), this.min, this.max);
913
+ valueLane.valueMax = valueToRelative(this.#high(), this.min, this.max);
914
+ }
915
+ this.#emitInputAndChange({
916
+ previousValue: previous.poleA,
917
+ previousValueMin: Math.min(previous.poleA, previous.poleB),
918
+ previousValueMax: Math.max(previous.poleA, previous.poleB),
919
+ previousValueC: previous.poleC
920
+ }, true, changed);
921
+ }
922
+ #onPointerUp;
923
+ #onKeyDown;
924
+ #stepHandleByRelativeDelta(handle, targetRelative) {
925
+ if (this.valueMode === `single`) {
926
+ this.#setSingleValueFromRelative(clamp(targetRelative, 0, 1), true);
927
+ return;
928
+ }
929
+ if (handle === `c`) {
930
+ const lo = this.#low();
931
+ const hi = this.#high();
932
+ const clamped = clamp(scale(clamp(targetRelative, 0, 1), 0, 1, this.min, this.max), lo, hi);
933
+ const q = this.#quantise(clamped);
934
+ if (q === this.#poleC) return;
935
+ const previous = this.#poles();
936
+ this.#poleC = q;
937
+ this.#syncPoleProperties();
938
+ this.#isUserInteraction = true;
939
+ this.#emitInputAndChange({
940
+ previousValue: previous.poleA,
941
+ previousValueMin: Math.min(previous.poleA, previous.poleB),
942
+ previousValueMax: Math.max(previous.poleA, previous.poleB),
943
+ previousValueC: previous.poleC
944
+ }, true, `c`);
945
+ return;
946
+ }
947
+ this.#setDualOrTripleValueFromRelative(clamp(targetRelative, 0, 1), handle, true);
948
+ }
949
+ _applyWheelDelta(direction, event) {
950
+ let handle = this.#lastInteractedHandle;
951
+ const target = event.target;
952
+ if (target && typeof target.closest === `function`) {
953
+ const handleEl = target.closest(`[data-handle]`);
954
+ if (handleEl) {
955
+ const h = handleEl.dataset?.handle;
956
+ if (h === `a` || h === `b` || h === `c`) handle = h;
957
+ }
958
+ }
959
+ if (this.valueMode === `single`) handle = `a`;
960
+ this.#lastInteractedHandle = handle;
961
+ const range = this.max - this.min;
962
+ const effectiveStep = resolveAxisStep(range, this.stops, this.step, event.shiftKey);
963
+ const targetRel = clamp(valueToRelative(handle === `a` ? this.#poleA : handle === `b` ? this.#poleB : this.#poleC ?? this.#low(), this.min, this.max) - direction * effectiveStep / range, 0, 1);
964
+ this.#stepHandleByRelativeDelta(handle, targetRel);
965
+ }
966
+ #onFocusIn;
967
+ #viewBox() {
968
+ if (this.orientation === `horizontal`) return `0 0 100 24`;
969
+ return `0 0 24 100`;
970
+ }
971
+ #renderTrack() {
972
+ const isVertical = this.orientation === `vertical`;
973
+ const strokeWidth = this.#getCssVar(`--track-width`, 2);
974
+ const color = this.#getCssVarColor(`--track-color`, `var(--surface-4)`);
975
+ const fill = this.#getCssVarColor(`--track-fill`, `transparent`);
976
+ if (isVertical) return svg`
977
+ <rect
978
+ class="track"
979
+ x="${12 - strokeWidth / 2}"
980
+ y="${5}"
981
+ width="${strokeWidth}"
982
+ height="${90}"
983
+ fill="${fill}"
984
+ stroke="${color}"
985
+ stroke-width="0"
986
+ />
987
+ `;
988
+ return svg`
989
+ <rect
990
+ class="track"
991
+ x="${5}"
992
+ y="${12 - strokeWidth / 2}"
993
+ width="${90}"
994
+ height="${strokeWidth}"
995
+ fill="${fill}"
996
+ stroke="${color}"
997
+ stroke-width="0"
998
+ />
999
+ `;
1000
+ }
1001
+ #getCssVarColor(name, fallback) {
1002
+ return getComputedStyle(this).getPropertyValue(name).trim() || fallback;
1003
+ }
1004
+ #renderLane(lane) {
1005
+ const bounds = getTrackBounds(this.orientation);
1006
+ let path = ``;
1007
+ if (lane.type === `fixed`) path = generateLinePath(this.orientation === `horizontal` ? {
1008
+ x: bounds.trackStart,
1009
+ y: bounds.crossCentre
1010
+ } : {
1011
+ x: bounds.crossCentre,
1012
+ y: bounds.trackStart
1013
+ }, this.orientation === `horizontal` ? {
1014
+ x: bounds.trackEnd,
1015
+ y: bounds.crossCentre
1016
+ } : {
1017
+ x: bounds.crossCentre,
1018
+ y: bounds.trackEnd
1019
+ });
1020
+ else if (lane.type === `anchored`) {
1021
+ const seg = getAnchoredSegment(this.#getRelativeValueA(), lane.value ?? .1, this.singleStyle, bounds, this.orientation);
1022
+ path = generateLinePath(seg.start, seg.end);
1023
+ } else {
1024
+ const seg = getValueSegment(lane.valueMin ?? 0, lane.valueMax ?? 0, this.singleStyle, bounds, this.orientation);
1025
+ path = generateLinePath(seg.start, seg.end);
1026
+ }
1027
+ if (!path) return svg``;
1028
+ const offset = lane.offset;
1029
+ const w = this.orientation === `horizontal` ? `calc(var(--lane-${lane.key}-width, var(--lane-width)) * 24%)` : `calc(var(--lane-${lane.key}-width, var(--lane-width)) * 24%)`;
1030
+ const transform = this.orientation === `horizontal` ? `translateY(${offset * 24 / 2}px)` : `translateX(${offset * 24 / 2}px)`;
1031
+ return svg`
1032
+ <path
1033
+ class="lane"
1034
+ data-lane-key="${lane.key}"
1035
+ part="lane lane-${lane.key}"
1036
+ d="${path}"
1037
+ style="
1038
+ stroke-width: ${w};
1039
+ stroke-linecap: round;
1040
+ transform: ${transform};
1041
+ "
1042
+ />
1043
+ `;
1044
+ }
1045
+ #renderValueMarker(handle, defs) {
1046
+ const bounds = getTrackBounds(this.orientation);
1047
+ const isHorizontal = this.orientation === `horizontal`;
1048
+ let value;
1049
+ if (this.valueMode === `single`) value = this.#getRelativeValueA();
1050
+ else if (handle === `a`) value = this.#getRelativeValueA();
1051
+ else if (handle === `b`) value = this.#getRelativeValueB();
1052
+ else value = this.#getRelativeValueC();
1053
+ const pos = valueToPosition(value, this.singleStyle, bounds, this.orientation);
1054
+ const markerOffset = this.#getCssVar(`--marker-offset`, 0);
1055
+ const markerLength = this.#getCssVar(`--marker-length`, .8);
1056
+ const markerThickness = this.#getCssVar(`--marker-thickness`, .15);
1057
+ const markerBorderWidth = this.#getCssVar(`--marker-border-width`, 0);
1058
+ const center = isHorizontal ? {
1059
+ x: pos.x,
1060
+ y: bounds.crossCentre + markerOffset * 12
1061
+ } : {
1062
+ x: bounds.crossCentre + markerOffset * 12,
1063
+ y: pos.y
1064
+ };
1065
+ const lowCenter = this.valueMode === `single` && this.singleStyle === `unipolar` ? isHorizontal ? {
1066
+ x: 2 * bounds.centre - center.x,
1067
+ y: center.y
1068
+ } : {
1069
+ x: center.x,
1070
+ y: 2 * bounds.centre - center.y
1071
+ } : void 0;
1072
+ const id = handle ?? `s`;
1073
+ const effectiveMarkerStyle = handle === `c` && this.markerStyleC ? this.markerStyleC : this.markerStyle;
1074
+ if (effectiveMarkerStyle === `dot`) {
1075
+ const r = markerThickness * 12;
1076
+ const renderDot = (cx, cy, mid) => {
1077
+ if (markerBorderWidth > 0) {
1078
+ defs.push(svg`<circle id="dot-${mid}-path" cx="${cx}" cy="${cy}" r="${r}" />`);
1079
+ return svg`
1080
+ <use href="#dot-${mid}-path" class="marker-border" />
1081
+ <use href="#dot-${mid}-path" class="marker-fill" />
1082
+ `;
1083
+ }
1084
+ return svg`<circle cx="${cx}" cy="${cy}" r="${r}" />`;
1085
+ };
1086
+ const high = renderDot(center.x, center.y, id);
1087
+ if (lowCenter) return svg`${high}${renderDot(lowCenter.x, lowCenter.y, `${id}-low`)}`;
1088
+ return high;
1089
+ }
1090
+ if (effectiveMarkerStyle === `needle`) {
1091
+ const halfLen = markerLength * 24 / 4;
1092
+ const renderNeedle = (cx, cy, mid) => {
1093
+ let start;
1094
+ let end;
1095
+ if (isHorizontal) {
1096
+ start = {
1097
+ x: cx,
1098
+ y: cy - halfLen
1099
+ };
1100
+ end = {
1101
+ x: cx,
1102
+ y: cy + halfLen
1103
+ };
1104
+ } else {
1105
+ start = {
1106
+ x: cx - halfLen,
1107
+ y: cy
1108
+ };
1109
+ end = {
1110
+ x: cx + halfLen,
1111
+ y: cy
1112
+ };
1113
+ }
1114
+ const path = `M ${start.x} ${start.y} L ${end.x} ${end.y}`;
1115
+ if (markerBorderWidth > 0) {
1116
+ defs.push(svg`<path id="needle-${mid}-path" d="${path}" />`);
1117
+ return svg`
1118
+ <use href="#needle-${mid}-path" class="marker-border" />
1119
+ <use href="#needle-${mid}-path" class="marker-fill" />
1120
+ `;
1121
+ }
1122
+ return svg`<path d="${path}" stroke-linecap="round" />`;
1123
+ };
1124
+ const high = renderNeedle(center.x, center.y, id);
1125
+ if (lowCenter) return svg`${high}${renderNeedle(lowCenter.x, lowCenter.y, `${id}-low`)}`;
1126
+ return high;
1127
+ }
1128
+ if (effectiveMarkerStyle === `triangle`) {
1129
+ const halfLen = markerLength * 24 / 4;
1130
+ const halfBase = markerThickness * 12;
1131
+ const renderTriangle = (cx, cy, mid) => {
1132
+ let apex;
1133
+ let base1;
1134
+ let base2;
1135
+ if (isHorizontal) {
1136
+ apex = {
1137
+ x: cx,
1138
+ y: cy
1139
+ };
1140
+ base1 = {
1141
+ x: cx - halfBase,
1142
+ y: cy + halfLen
1143
+ };
1144
+ base2 = {
1145
+ x: cx + halfBase,
1146
+ y: cy + halfLen
1147
+ };
1148
+ } else {
1149
+ apex = {
1150
+ x: cx,
1151
+ y: cy
1152
+ };
1153
+ base1 = {
1154
+ x: cx + halfLen,
1155
+ y: cy - halfBase
1156
+ };
1157
+ base2 = {
1158
+ x: cx + halfLen,
1159
+ y: cy + halfBase
1160
+ };
1161
+ }
1162
+ const points = `${apex.x},${apex.y} ${base1.x},${base1.y} ${base2.x},${base2.y}`;
1163
+ if (markerBorderWidth > 0) {
1164
+ defs.push(svg`<polygon id="triangle-${mid}-path" points="${points}" />`);
1165
+ return svg`
1166
+ <use href="#triangle-${mid}-path" class="marker-border" />
1167
+ <use href="#triangle-${mid}-path" class="marker-fill" />
1168
+ `;
1169
+ }
1170
+ return svg`<polygon points="${points}" />`;
1171
+ };
1172
+ const high = renderTriangle(center.x, center.y, id);
1173
+ if (lowCenter) return svg`${high}${renderTriangle(lowCenter.x, lowCenter.y, `${id}-low`)}`;
1174
+ return high;
1175
+ }
1176
+ return svg``;
1177
+ }
1178
+ #renderMidpointMarker() {
1179
+ if (this.valueMode !== `single`) return svg``;
1180
+ if (this.singleStyle !== `bipolar` && this.singleStyle !== `unipolar`) return svg``;
1181
+ const bounds = getTrackBounds(this.orientation);
1182
+ const isHorizontal = this.orientation === `horizontal`;
1183
+ const halfLen = this.#getCssVar(`--midpoint-length`, .15) * 24 / 4;
1184
+ const center = isHorizontal ? {
1185
+ x: bounds.centre,
1186
+ y: bounds.crossCentre
1187
+ } : {
1188
+ x: bounds.crossCentre,
1189
+ y: bounds.centre
1190
+ };
1191
+ if (this.markerStyle === `dot`) {
1192
+ const size = this.#getCssVar(`--midpoint-thickness`, .08);
1193
+ return svg`
1194
+ <g class="midpoint-marker" part="midpoint-marker">
1195
+ <circle cx="${center.x}" cy="${center.y}" r="${size * 12}" />
1196
+ </g>
1197
+ `;
1198
+ }
1199
+ let start;
1200
+ let end;
1201
+ if (isHorizontal) {
1202
+ start = {
1203
+ x: center.x,
1204
+ y: center.y - halfLen
1205
+ };
1206
+ end = {
1207
+ x: center.x,
1208
+ y: center.y + halfLen
1209
+ };
1210
+ } else {
1211
+ start = {
1212
+ x: center.x - halfLen,
1213
+ y: center.y
1214
+ };
1215
+ end = {
1216
+ x: center.x + halfLen,
1217
+ y: center.y
1218
+ };
1219
+ }
1220
+ return svg`
1221
+ <g class="midpoint-marker" part="midpoint-marker">
1222
+ <line x1="${start.x}" y1="${start.y}" x2="${end.x}" y2="${end.y}" />
1223
+ </g>
1224
+ `;
1225
+ }
1226
+ #renderHandleGroup(handle, content) {
1227
+ if (!handle) return svg`<g role="slider"
1228
+ class="value-marker"
1229
+ aria-orientation="${this.orientation}"
1230
+ aria-valuemin="${this.min}" aria-valuemax="${this.max}"
1231
+ aria-valuenow="${this.valueA}"
1232
+ part="value-marker">${content}</g>`;
1233
+ const partName = `value-marker value-marker-${handle}`;
1234
+ const value = handle === `a` ? this.valueA : handle === `b` ? this.valueB : this.valueC ?? (this.#poleA + this.#poleB) / 2;
1235
+ return svg`<g tabindex="${this.disabled ? -1 : 0}" role="slider"
1236
+ class="value-marker"
1237
+ data-handle="${handle}"
1238
+ aria-label="${handle}"
1239
+ aria-orientation="${this.orientation}"
1240
+ aria-valuemin="${this.min}" aria-valuemax="${this.max}"
1241
+ aria-valuenow="${value}"
1242
+ part="${partName}">${content}</g>`;
1243
+ }
1244
+ #isMultiHandle() {
1245
+ return this.valueMode !== `single`;
1246
+ }
1247
+ willUpdate(changed) {
1248
+ super.willUpdate(changed);
1249
+ if (changed.has(`valueA`) || changed.has(`valueB`) || !this.hasUpdated) {
1250
+ this.#poleA = this.valueA;
1251
+ this.#poleB = this.valueB;
1252
+ }
1253
+ if (this.valueMode === `triple`) {
1254
+ if (this.valueC === void 0 || !this.#hasInitialisedValueC || changed.has(`valueMode`)) {
1255
+ this.#poleC = (this.#poleA + this.#poleB) / 2;
1256
+ this.valueC = this.#poleC;
1257
+ this.#hasInitialisedValueC = true;
1258
+ } else this.#poleC = this.valueC;
1259
+ const lo = Math.min(this.#poleA, this.#poleB);
1260
+ const hi = Math.max(this.#poleA, this.#poleB);
1261
+ if (this.#poleC < lo) this.#poleC = lo;
1262
+ if (this.#poleC > hi) this.#poleC = hi;
1263
+ this.valueC = this.#poleC;
1264
+ } else this.#poleC = void 0;
1265
+ if (changed.has(`valueA`) || changed.has(`valueB`) || changed.has(`valueMode`) || changed.has(`singleStyle`) || !this.hasUpdated) {
1266
+ const valueLane = this.#lanes.get(`value`);
1267
+ if (valueLane && valueLane.type === `value`) {
1268
+ if (this.valueMode === `single`) this.#applySingleStyleValueLane(valueLane, this.#getRelativeValueA());
1269
+ else {
1270
+ valueLane.valueMin = valueToRelative(this.#low(), this.min, this.max);
1271
+ valueLane.valueMax = valueToRelative(this.#high(), this.min, this.max);
1272
+ }
1273
+ }
1274
+ }
1275
+ }
1276
+ /**
1277
+ * Sync the default 'value' lane's valueMin/valueMax for single-mode single-styles.
1278
+ * Range: bar from trackStart to value.
1279
+ * Bipolar: bar grows from track centre outward to value (or value to centre if below midpoint).
1280
+ * Unipolar: bar is centred on the track centre with length proportional to value.
1281
+ */
1282
+ #applySingleStyleValueLane(valueLane, relativeValue) {
1283
+ if (this.singleStyle === `bipolar`) {
1284
+ valueLane.valueMin = Math.min(.5, relativeValue);
1285
+ valueLane.valueMax = Math.max(.5, relativeValue);
1286
+ } else {
1287
+ valueLane.valueMin = 0;
1288
+ valueLane.valueMax = relativeValue;
1289
+ }
1290
+ }
1291
+ render() {
1292
+ const lanes = Array.from(this.#lanes.values());
1293
+ const cursor = this.disabled ? `not-allowed` : this.valueMode === `single` ? `pointer` : `default`;
1294
+ const handles = this.#isMultiHandle() ? this.valueMode === `triple` ? [
1295
+ `a`,
1296
+ `b`,
1297
+ `c`
1298
+ ] : [`a`, `b`] : [];
1299
+ const markerDefs = [];
1300
+ const handleGroups = this.#isMultiHandle() ? handles.map((h) => this.#renderHandleGroup(h, this.#renderValueMarker(h, markerDefs))) : this.#renderHandleGroup(void 0, this.#renderValueMarker(void 0, markerDefs));
1301
+ return html`
1302
+ <svg
1303
+ class="slider-svg"
1304
+ viewBox="${this.#viewBox()}"
1305
+ preserveAspectRatio="none"
1306
+ tabindex="${this.disabled ? -1 : 0}"
1307
+ style="cursor: ${cursor}; width: 100%; height: 100%;"
1308
+ @pointerdown="${this.#onPointerDown}"
1309
+ @pointermove="${this.#onPointerMoveCursor}"
1310
+ @pointerleave="${this.#onPointerLeaveCursor}"
1311
+ @keydown="${this.#onKeyDown}"
1312
+ @focusin="${this.#onFocusIn}"
1313
+ >
1314
+ ${markerDefs.length > 0 ? svg`<defs>${markerDefs}</defs>` : svg``}
1315
+ ${this.#renderTrack()}
1316
+ ${lanes.map((lane) => this.#renderLane(lane))}
1317
+ ${this.#renderMidpointMarker()}
1318
+ ${handleGroups}
1319
+ </svg>
1320
+ `;
1321
+ }
1322
+ /**
1323
+ * Compute the cursor for the current pointer position. The mapping:
1324
+ * - `grab` over a marker handle (single A, or multi A/B/C)
1325
+ * - `move` over a draggable segment (between A and B when value-drag is on)
1326
+ * - `pointer` over empty track where clicking sets a value (single mode,
1327
+ * or the dead zones outside A/B in multi mode)
1328
+ * - `default` over no-op segments (between A and B in dual mode when
1329
+ * value-drag is none, or between A-C/C-B in triple when value-drag is none)
1330
+ * - `not-allowed` when disabled
1331
+ *
1332
+ * `relativeValue` is in the host's long-axis space (0 = start, 1 = end,
1333
+ * already flipped for vertical). For unipolar single mode, the value
1334
+ * bar's marker positions are computed in the same space; the abs(2x-1)
1335
+ * remap only applies when setting the value on click/drag.
1336
+ */
1337
+ #cursorAtRelative(relativeValue) {
1338
+ if (this.disabled) return `not-allowed`;
1339
+ if (this.#dragging) return `grabbing`;
1340
+ const HANDLE_HIT = .06;
1341
+ if (this.valueMode === `single`) {
1342
+ if (this.singleStyle === `unipolar`) {
1343
+ const halfLen = this.#getRelativeValueA() / 2;
1344
+ const low = .5 - halfLen;
1345
+ const high = .5 + halfLen;
1346
+ if (Math.abs(relativeValue - low) < HANDLE_HIT || Math.abs(relativeValue - high) < HANDLE_HIT) return `grab`;
1347
+ return `pointer`;
1348
+ }
1349
+ const aRel = this.#getRelativeValueA();
1350
+ if (Math.abs(relativeValue - aRel) < HANDLE_HIT) return `grab`;
1351
+ return `pointer`;
1352
+ }
1353
+ const aRel = this.#getRelativeValueA();
1354
+ const bRel = this.#getRelativeValueB();
1355
+ const lo = Math.min(aRel, bRel);
1356
+ const hi = Math.max(aRel, bRel);
1357
+ if (Math.abs(relativeValue - aRel) < HANDLE_HIT) return `grab`;
1358
+ if (Math.abs(relativeValue - bRel) < HANDLE_HIT) return `grab`;
1359
+ if (this.valueMode === `triple`) {
1360
+ const cRel = this.#getRelativeValueC();
1361
+ if (Math.abs(relativeValue - cRel) < HANDLE_HIT) return `grab`;
1362
+ }
1363
+ if (relativeValue > lo && relativeValue < hi) return this.valueDrag !== `none` ? `move` : `default`;
1364
+ return `pointer`;
1365
+ }
1366
+ #onPointerMoveCursor;
1367
+ #onPointerLeaveCursor;
1368
+ updated(changed) {
1369
+ super.updated(changed);
1370
+ if (this.valueMode === `single`) this.style.setProperty(`--value-relative`, this.#getRelativeValueA().toString());
1371
+ else {
1372
+ const lo = valueToRelative(this.#low(), this.min, this.max);
1373
+ const hi = valueToRelative(this.#high(), this.min, this.max);
1374
+ this.style.setProperty(`--value-min-relative`, lo.toString());
1375
+ this.style.setProperty(`--value-max-relative`, hi.toString());
1376
+ this.style.setProperty(`--value-relative`, hi.toString());
1377
+ if (this.valueMode === `triple` && this.#poleC !== void 0) this.style.setProperty(`--value-c-relative`, valueToRelative(this.#poleC, this.min, this.max).toString());
1378
+ }
1379
+ }
1380
+ disconnectedCallback() {
1381
+ super.disconnectedCallback();
1382
+ this.removeEventListener(`keydown`, this.#onHostKeyDown);
1383
+ this.#gradientObserver?.disconnect();
1384
+ this.#gradientObserver = void 0;
1385
+ if (this.#changeDebounceTimer !== void 0) {
1386
+ clearTimeout(this.#changeDebounceTimer);
1387
+ this.#changeDebounceTimer = void 0;
1388
+ }
1389
+ this.#onPointerUp();
1390
+ }
1391
+ static {
1392
+ this.styles = [
1393
+ themeFallbacks,
1394
+ prominenceStyles,
1395
+ css`
1396
+ :host {
1397
+ display: inline-block;
1398
+ width: 200px;
1399
+ height: 24px;
1400
+ touch-action: none;
1401
+ user-select: none;
1402
+ tabindex: 0;
1403
+ --value-relative: 0;
1404
+ --value-min-relative: 0;
1405
+ --value-max-relative: 0;
1406
+ --value-c-relative: 0;
1407
+
1408
+ --lane-width: 0.16;
1409
+ --lane-track-width: 0.08;
1410
+ --lane-value-width: 0.12;
1411
+ }
1412
+
1413
+ :host([orientation="vertical"]) {
1414
+ width: 24px;
1415
+ height: 200px;
1416
+ }
1417
+
1418
+ :host([disabled]) {
1419
+ cursor: not-allowed;
1420
+ }
1421
+
1422
+ :host {
1423
+ outline: none;
1424
+ }
1425
+
1426
+ :host(:focus-visible) {
1427
+ outline: var(--slider-input-svg-focus-outline, 2px solid var(--accent));
1428
+ outline-offset: var(--slider-input-svg-focus-outline-offset, 2px);
1429
+ }
1430
+
1431
+ svg.slider-svg {
1432
+ width: 100%;
1433
+ height: 100%;
1434
+ outline: none;
1435
+ overflow: visible;
1436
+ }
1437
+
1438
+ svg.slider-svg:focus {
1439
+ outline: none;
1440
+ }
1441
+
1442
+ svg.slider-svg:focus-visible,
1443
+ :host(:focus-visible) svg.slider-svg {
1444
+ outline: var(--slider-input-svg-focus-outline, 2px solid var(--accent));
1445
+ outline-offset: var(--slider-input-svg-focus-outline-offset, 2px);
1446
+ }
1447
+
1448
+ .track {
1449
+ stroke: var(--track-color, var(--surface-4));
1450
+ stroke-width: var(--track-width, 2);
1451
+ }
1452
+
1453
+ .lane {
1454
+ fill: none;
1455
+ stroke-linecap: round;
1456
+ stroke-dasharray: var(--lane-dasharray, none);
1457
+ stroke-dashoffset: var(--lane-dashoffset, 0);
1458
+ vector-effect: non-scaling-stroke;
1459
+ }
1460
+
1461
+ .lane[data-lane-key='track'] {
1462
+ stroke: var(--lane-track-color, var(--surface-4));
1463
+ }
1464
+
1465
+ .lane[data-lane-key='value'] {
1466
+ stroke: var(--lane-value-color, var(--accent));
1467
+ }
1468
+
1469
+ .lane:not([data-lane-key='track']):not([data-lane-key='value']) {
1470
+ stroke: var(--lane-color, var(--accent));
1471
+ }
1472
+
1473
+ .value-marker > path:not(.marker-border):not(.marker-fill),
1474
+ .value-marker > .marker-fill {
1475
+ stroke: var(--marker-color, var(--accent));
1476
+ stroke-width: calc(var(--marker-thickness, 0.15) * 12px);
1477
+ stroke-linecap: round;
1478
+ stroke-dasharray: var(--marker-dasharray, none);
1479
+ stroke-dashoffset: var(--marker-dashoffset, 0);
1480
+ vector-effect: non-scaling-stroke;
1481
+ }
1482
+
1483
+ .value-marker > .marker-border {
1484
+ stroke: var(--marker-border-color, transparent);
1485
+ stroke-width: calc((var(--marker-thickness, 0.15) + var(--marker-border-width, 0) * 2) * 12px);
1486
+ stroke-linecap: round;
1487
+ vector-effect: non-scaling-stroke;
1488
+ }
1489
+
1490
+ .value-marker .marker-fill,
1491
+ .value-marker circle,
1492
+ .value-marker polygon {
1493
+ fill: var(--marker-color, var(--accent));
1494
+ stroke-width: calc(var(--marker-thickness, 0.15) * 12px);
1495
+ }
1496
+
1497
+ .value-marker[data-handle="a"] > path:not(.marker-border):not(.marker-fill),
1498
+ .value-marker[data-handle="a"] > .marker-fill,
1499
+ .value-marker[data-handle="a"] > circle,
1500
+ .value-marker[data-handle="a"] > polygon {
1501
+ stroke: var(--marker-a-color, var(--marker-color, var(--accent)));
1502
+ fill: var(--marker-a-color, var(--marker-color, var(--accent)));
1503
+ }
1504
+
1505
+ .value-marker[data-handle="b"] > path:not(.marker-border):not(.marker-fill),
1506
+ .value-marker[data-handle="b"] > .marker-fill,
1507
+ .value-marker[data-handle="b"] > circle,
1508
+ .value-marker[data-handle="b"] > polygon {
1509
+ stroke: var(--marker-b-color, var(--marker-color, var(--accent)));
1510
+ fill: var(--marker-b-color, var(--marker-color, var(--accent)));
1511
+ }
1512
+
1513
+ .value-marker[data-handle="c"] > path:not(.marker-border):not(.marker-fill),
1514
+ .value-marker[data-handle="c"] > .marker-fill,
1515
+ .value-marker[data-handle="c"] > circle,
1516
+ .value-marker[data-handle="c"] > polygon {
1517
+ stroke: var(--marker-c-color, var(--marker-color, var(--accent)));
1518
+ fill: var(--marker-c-color, var(--marker-color, var(--accent)));
1519
+ }
1520
+
1521
+ .midpoint-marker line {
1522
+ stroke: var(--midpoint-color, var(--surface-muted-text));
1523
+ stroke-width: var(--midpoint-thickness, 1px);
1524
+ stroke-linecap: round;
1525
+ vector-effect: non-scaling-stroke;
1526
+ }
1527
+
1528
+ .midpoint-marker circle {
1529
+ fill: var(--midpoint-color, var(--surface-muted-text));
1530
+ stroke: var(--midpoint-border-color, transparent);
1531
+ stroke-width: var(--midpoint-border-width, 0);
1532
+ vector-effect: non-scaling-stroke;
1533
+ }
1534
+ `
1535
+ ];
1536
+ }
1537
+ };
1538
+ __decorate([property({
1539
+ type: Number,
1540
+ attribute: `value-a`
1541
+ })], SliderInputElement.prototype, "valueA", void 0);
1542
+ __decorate([property({
1543
+ type: Number,
1544
+ attribute: `value-b`
1545
+ })], SliderInputElement.prototype, "valueB", void 0);
1546
+ __decorate([property({
1547
+ type: Number,
1548
+ attribute: `value-c`
1549
+ })], SliderInputElement.prototype, "valueC", void 0);
1550
+ __decorate([property({ type: Number })], SliderInputElement.prototype, "min", void 0);
1551
+ __decorate([property({ type: Number })], SliderInputElement.prototype, "max", void 0);
1552
+ __decorate([property({ type: Number })], SliderInputElement.prototype, "step", void 0);
1553
+ __decorate([property({ type: Number })], SliderInputElement.prototype, "stops", void 0);
1554
+ __decorate([property({
1555
+ type: String,
1556
+ attribute: `value-mode`
1557
+ })], SliderInputElement.prototype, "valueMode", void 0);
1558
+ __decorate([property({
1559
+ type: String,
1560
+ attribute: `single-style`
1561
+ })], SliderInputElement.prototype, "singleStyle", void 0);
1562
+ __decorate([property({ type: String })], SliderInputElement.prototype, "orientation", void 0);
1563
+ __decorate([property({
1564
+ type: String,
1565
+ attribute: `marker-style`
1566
+ })], SliderInputElement.prototype, "markerStyle", void 0);
1567
+ __decorate([property({
1568
+ type: String,
1569
+ attribute: `marker-style-c`
1570
+ })], SliderInputElement.prototype, "markerStyleC", void 0);
1571
+ __decorate([property({ type: String })], SliderInputElement.prototype, "crossover", void 0);
1572
+ __decorate([property({
1573
+ type: String,
1574
+ attribute: `value-drag`
1575
+ })], SliderInputElement.prototype, "valueDrag", void 0);
1576
+ __decorate([property({
1577
+ type: String,
1578
+ attribute: `value-c-follow`
1579
+ })], SliderInputElement.prototype, "valueCFollow", void 0);
1580
+ __decorate([property({
1581
+ type: Number,
1582
+ attribute: `value-min`
1583
+ })], SliderInputElement.prototype, "valueMin", void 0);
1584
+ __decorate([property({
1585
+ type: Boolean,
1586
+ reflect: true
1587
+ })], SliderInputElement.prototype, "disabled", void 0);
1588
+ SliderInputElement = __decorate([safeCustomElement(`ixfx-slider-input`)], SliderInputElement);
1589
+ //#endregion
1590
+ export { SliderInputElement as t };
1591
+
1592
+ //# sourceMappingURL=slider-input-DFpG23bS.js.map