@weasel-js/ui 1.7.3 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist/chunks/{Callout-BUn0dP_S.js → Callout-DpTO3fkb.js} +10 -10
  2. package/dist/chunks/Callout-DpTO3fkb.js.map +1 -0
  3. package/dist/chunks/{Checkbox-DFn-x0JO.js → Checkbox-d4IfhiXh.js} +2 -2
  4. package/dist/chunks/{Checkbox-DFn-x0JO.js.map → Checkbox-d4IfhiXh.js.map} +1 -1
  5. package/dist/chunks/{CloseButton-BmNmRVtb.js → CloseButton-wb7Or5Eg.js} +2 -2
  6. package/dist/chunks/{CloseButton-BmNmRVtb.js.map → CloseButton-wb7Or5Eg.js.map} +1 -1
  7. package/dist/chunks/{ColorModeControl-DaVJ-SyX.js → ColorModeControl-DET6Itwq.js} +3 -3
  8. package/dist/chunks/{ColorModeControl-DaVJ-SyX.js.map → ColorModeControl-DET6Itwq.js.map} +1 -1
  9. package/dist/chunks/{DataGrid-CmHJe57v.js → DataGrid-B6IS9vUk.js} +2 -2
  10. package/dist/chunks/{DataGrid-CmHJe57v.js.map → DataGrid-B6IS9vUk.js.map} +1 -1
  11. package/dist/chunks/{DetentSlider-DNTAk8xP.js → DetentSlider-D40V8Oma.js} +2 -2
  12. package/dist/chunks/{DetentSlider-DNTAk8xP.js.map → DetentSlider-D40V8Oma.js.map} +1 -1
  13. package/dist/chunks/{Dialog-DKfsk5VN.js → Dialog-h-5MeYpu.js} +9 -9
  14. package/dist/chunks/{Dialog-DKfsk5VN.js.map → Dialog-h-5MeYpu.js.map} +1 -1
  15. package/dist/chunks/{Disclosure-Ch2KxYuB.js → Disclosure-CpWt87nO.js} +7 -7
  16. package/dist/chunks/{Disclosure-Ch2KxYuB.js.map → Disclosure-CpWt87nO.js.map} +1 -1
  17. package/dist/chunks/FitLabel-BzZGkWfL.js +76 -0
  18. package/dist/chunks/FitLabel-BzZGkWfL.js.map +1 -0
  19. package/dist/chunks/{GradientEditor-DD2OjQtn.js → GradientEditor-vz771M-N.js} +4 -4
  20. package/dist/chunks/{GradientEditor-DD2OjQtn.js.map → GradientEditor-vz771M-N.js.map} +1 -1
  21. package/dist/chunks/{Icon--B1ME-6c.js → Icon-DPRUIo3G.js} +4 -1
  22. package/dist/chunks/Icon-DPRUIo3G.js.map +1 -0
  23. package/dist/chunks/{Jog-BrkzdGV2.js → Jog-TE6QI9i1.js} +3 -3
  24. package/dist/chunks/{Jog-BrkzdGV2.js.map → Jog-TE6QI9i1.js.map} +1 -1
  25. package/dist/chunks/{LayerList-BqeWFuGc.js → LayerList-BXTAjK-4.js} +6 -6
  26. package/dist/chunks/{LayerList-BqeWFuGc.js.map → LayerList-BXTAjK-4.js.map} +1 -1
  27. package/dist/chunks/{ListEditor-OQDzG4A5.js → ListEditor-DGtwj99m.js} +2 -2
  28. package/dist/chunks/{ListEditor-OQDzG4A5.js.map → ListEditor-DGtwj99m.js.map} +1 -1
  29. package/dist/chunks/{MeshEditor-DWpvg8H6.js → MeshEditor-8CRsu-Td.js} +2 -2
  30. package/dist/chunks/{MeshEditor-DWpvg8H6.js.map → MeshEditor-8CRsu-Td.js.map} +1 -1
  31. package/dist/chunks/{NumberField-DS0WSiin.js → NumberField-BXFrOi5M.js} +8 -5
  32. package/dist/chunks/{NumberField-DS0WSiin.js.map → NumberField-BXFrOi5M.js.map} +1 -1
  33. package/dist/chunks/{PaintField-BdmQM53v.js → PaintField-DX4uYlF9.js} +2 -2
  34. package/dist/chunks/{PaintField-BdmQM53v.js.map → PaintField-DX4uYlF9.js.map} +1 -1
  35. package/dist/chunks/{PaintInput-DaQMeBsI.js → PaintInput-CWyOJXUM.js} +5 -5
  36. package/dist/chunks/{PaintInput-DaQMeBsI.js.map → PaintInput-CWyOJXUM.js.map} +1 -1
  37. package/dist/chunks/{PatternPicker-B3HcMZWg.js → PatternPicker-0j51aDJ3.js} +9 -9
  38. package/dist/chunks/PatternPicker-0j51aDJ3.js.map +1 -0
  39. package/dist/chunks/{Prefs-BnVEyfWO.js → Prefs-gbjYqvSD.js} +41 -41
  40. package/dist/chunks/Prefs-gbjYqvSD.js.map +1 -0
  41. package/dist/chunks/{Properties-BoG2MMHR.js → Properties-BbNuMiic.js} +4 -4
  42. package/dist/chunks/{Properties-BoG2MMHR.js.map → Properties-BbNuMiic.js.map} +1 -1
  43. package/dist/chunks/PropertyField-CWUtNK1w.js +751 -0
  44. package/dist/chunks/PropertyField-CWUtNK1w.js.map +1 -0
  45. package/dist/chunks/{SelectionPanel-COZ6z9_P.js → SelectionPanel-BpUqP75b.js} +2 -2
  46. package/dist/chunks/{SelectionPanel-COZ6z9_P.js.map → SelectionPanel-BpUqP75b.js.map} +1 -1
  47. package/dist/chunks/{SidebarPanel-AXQ0jRDx.js → SidebarPanel--dJ-k9-V.js} +3 -3
  48. package/dist/chunks/{SidebarPanel-AXQ0jRDx.js.map → SidebarPanel--dJ-k9-V.js.map} +1 -1
  49. package/dist/chunks/Slider-CHBQ8uUC.js +439 -0
  50. package/dist/chunks/Slider-CHBQ8uUC.js.map +1 -0
  51. package/dist/chunks/{SwatchGrid-9ZPiDmxd.js → SwatchGrid-DxK9e_IV.js} +3 -3
  52. package/dist/chunks/SwatchGrid-DxK9e_IV.js.map +1 -0
  53. package/dist/chunks/{SwatchStrip-Bi9iPLoh.js → SwatchStrip-BY6A511W.js} +2 -2
  54. package/dist/chunks/{SwatchStrip-Bi9iPLoh.js.map → SwatchStrip-BY6A511W.js.map} +1 -1
  55. package/dist/chunks/{ThemeSwitcher-DiBmMh_B.js → ThemeSwitcher-tnATkIo1.js} +2 -2
  56. package/dist/chunks/{ThemeSwitcher-DiBmMh_B.js.map → ThemeSwitcher-tnATkIo1.js.map} +1 -1
  57. package/dist/chunks/{Timeline-sMCH2fah.js → Timeline-FlLUm8nd.js} +4 -4
  58. package/dist/chunks/{Timeline-sMCH2fah.js.map → Timeline-FlLUm8nd.js.map} +1 -1
  59. package/dist/chunks/{Toast-C3ozq11V.js → Toast-CuHkT0x0.js} +2 -2
  60. package/dist/chunks/{Toast-C3ozq11V.js.map → Toast-CuHkT0x0.js.map} +1 -1
  61. package/dist/chunks/ToggleBar-BZ4Gk1IU.js +84 -0
  62. package/dist/chunks/ToggleBar-BZ4Gk1IU.js.map +1 -0
  63. package/dist/chunks/{TokenPanel-WtKGY2Db.js → TokenPanel-DongdiZM.js} +23 -23
  64. package/dist/chunks/{TokenPanel-WtKGY2Db.js.map → TokenPanel-DongdiZM.js.map} +1 -1
  65. package/dist/chunks/ToolOptionsBar-wTPJFfOn.js +94 -0
  66. package/dist/chunks/ToolOptionsBar-wTPJFfOn.js.map +1 -0
  67. package/dist/chunks/{Tree-Cc8lIzgM.js → Tree-hzIbj2q1.js} +2 -2
  68. package/dist/chunks/{Tree-Cc8lIzgM.js.map → Tree-hzIbj2q1.js.map} +1 -1
  69. package/dist/chunks/checkbox.module-Dcx8ILgR.js +5 -0
  70. package/dist/chunks/checkbox.module-Dcx8ILgR.js.map +1 -0
  71. package/dist/chunks/{icons-Dr2-r30x.js → icons-CofIOTiI.js} +2 -2
  72. package/dist/chunks/{icons-Dr2-r30x.js.map → icons-CofIOTiI.js.map} +1 -1
  73. package/dist/chunks/{model-B6zuQg6b.js → model-qQj8Ny9w.js} +134 -133
  74. package/dist/chunks/model-qQj8Ny9w.js.map +1 -0
  75. package/dist/chunks/{prefField-BE_Ytr2H.js → prefField-CCm_nzlM.js} +81 -75
  76. package/dist/chunks/prefField-CCm_nzlM.js.map +1 -0
  77. package/dist/components/Callout/index.js +1 -1
  78. package/dist/components/Checkbox/index.js +1 -1
  79. package/dist/components/CloseButton/index.js +1 -1
  80. package/dist/components/ColorModeControl/index.js +1 -1
  81. package/dist/components/DataGrid/index.js +1 -1
  82. package/dist/components/DetentSlider/index.js +1 -1
  83. package/dist/components/Dialog/index.js +1 -1
  84. package/dist/components/Disclosure/index.js +1 -1
  85. package/dist/components/FitLabel/FitLabel.d.ts +41 -0
  86. package/dist/components/FitLabel/FitLabel.d.ts.map +1 -0
  87. package/dist/components/FitLabel/index.d.ts +3 -0
  88. package/dist/components/FitLabel/index.d.ts.map +1 -0
  89. package/dist/components/FitLabel/index.js +2 -0
  90. package/dist/components/GradientEditor/index.js +1 -1
  91. package/dist/components/InlineRange/InlineRange.d.ts +1 -1
  92. package/dist/components/Jog/index.js +1 -1
  93. package/dist/components/LayerList/index.js +1 -1
  94. package/dist/components/ListEditor/index.js +1 -1
  95. package/dist/components/MeshEditor/index.js +1 -1
  96. package/dist/components/NumberField/UnitField.d.ts.map +1 -1
  97. package/dist/components/NumberField/index.js +1 -1
  98. package/dist/components/PaintField/index.js +1 -1
  99. package/dist/components/PaintInput/index.js +1 -1
  100. package/dist/components/PatternPicker/index.js +1 -1
  101. package/dist/components/Prefs/index.js +2 -2
  102. package/dist/components/Prefs/prefField.d.ts.map +1 -1
  103. package/dist/components/Properties/NumberControls.d.ts +16 -0
  104. package/dist/components/Properties/NumberControls.d.ts.map +1 -0
  105. package/dist/components/Properties/PropertyField.d.ts +18 -8
  106. package/dist/components/Properties/PropertyField.d.ts.map +1 -1
  107. package/dist/components/Properties/index.js +2 -2
  108. package/dist/components/SelectionPanel/index.js +2 -2
  109. package/dist/components/SelectionPanel/renderLeaf.d.ts.map +1 -1
  110. package/dist/components/SidebarPanel/index.js +1 -1
  111. package/dist/components/Slider/Slider.d.ts +6 -0
  112. package/dist/components/Slider/Slider.d.ts.map +1 -1
  113. package/dist/components/Slider/index.js +1 -1
  114. package/dist/components/SwatchGrid/index.js +1 -1
  115. package/dist/components/SwatchStrip/index.js +1 -1
  116. package/dist/components/ThemeSwitcher/index.js +1 -1
  117. package/dist/components/Timeline/index.js +1 -1
  118. package/dist/components/Toast/index.js +1 -1
  119. package/dist/components/ToggleBar/ToggleBar.d.ts.map +1 -1
  120. package/dist/components/ToggleBar/index.js +1 -1
  121. package/dist/components/TokenPanel/index.js +1 -1
  122. package/dist/components/ToolOptionsBar/ToolOptionsBar.d.ts +1 -1
  123. package/dist/components/ToolOptionsBar/ToolOptionsBar.d.ts.map +1 -1
  124. package/dist/components/ToolOptionsBar/index.js +1 -1
  125. package/dist/components/Tree/index.js +1 -1
  126. package/dist/components/stanceCss.d.ts +6 -0
  127. package/dist/components/stanceCss.d.ts.map +1 -1
  128. package/dist/components/stanceSurfaces.d.ts.map +1 -1
  129. package/dist/endless.d.ts +7 -0
  130. package/dist/endless.d.ts.map +1 -0
  131. package/dist/icons/paths.d.ts +2 -0
  132. package/dist/icons/paths.d.ts.map +1 -1
  133. package/dist/index.d.ts +1 -0
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +72 -70
  136. package/dist/index.js.map +1 -1
  137. package/dist/style.css +1 -1
  138. package/package.json +9 -9
  139. package/dist/chunks/Callout-BUn0dP_S.js.map +0 -1
  140. package/dist/chunks/Icon--B1ME-6c.js.map +0 -1
  141. package/dist/chunks/PatternPicker-B3HcMZWg.js.map +0 -1
  142. package/dist/chunks/Prefs-BnVEyfWO.js.map +0 -1
  143. package/dist/chunks/PropertyField-PMrqBA0n.js +0 -743
  144. package/dist/chunks/PropertyField-PMrqBA0n.js.map +0 -1
  145. package/dist/chunks/Slider-DSrlMEq8.js +0 -389
  146. package/dist/chunks/Slider-DSrlMEq8.js.map +0 -1
  147. package/dist/chunks/SwatchGrid-9ZPiDmxd.js.map +0 -1
  148. package/dist/chunks/ToggleBar-Z2jZiguF.js +0 -80
  149. package/dist/chunks/ToggleBar-Z2jZiguF.js.map +0 -1
  150. package/dist/chunks/ToolOptionsBar-BJKwnemU.js +0 -88
  151. package/dist/chunks/ToolOptionsBar-BJKwnemU.js.map +0 -1
  152. package/dist/chunks/checkbox.module-DLQV9tMX.js +0 -5
  153. package/dist/chunks/checkbox.module-DLQV9tMX.js.map +0 -1
  154. package/dist/chunks/model-B6zuQg6b.js.map +0 -1
  155. package/dist/chunks/prefField-BE_Ytr2H.js.map +0 -1
@@ -1,389 +0,0 @@
1
- import { openPointerSession as e } from "@weasel-js/core";
2
- import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
- import { useCallback as r, useEffect as i, useMemo as a, useRef as o } from "react";
4
- import { decimal as s, qty as c } from "@weasel-js/quantity";
5
- var l = {
6
- root: "_root_6k91g_1",
7
- row: "_row_6k91g_12",
8
- track: "_track_6k91g_18",
9
- thumb: "_thumb_6k91g_35 _numeric_zns9n_4",
10
- slim: "_slim_6k91g_46",
11
- notched: "_notched_6k91g_47",
12
- overhang: "_overhang_6k91g_51",
13
- rail: "_rail_6k91g_55",
14
- trackInner: "_trackInner_6k91g_29",
15
- thumbActive: "_thumbActive_6k91g_120",
16
- readoutsBelow: "_readoutsBelow_6k91g_128",
17
- readoutBelow: "_readoutBelow_6k91g_135 _numeric_zns9n_4",
18
- readoutInline: "_readoutInline_6k91g_146 _numeric_zns9n_4",
19
- ticks: "_ticks_6k91g_176",
20
- tick: "_tick_6k91g_176",
21
- withStopLabels: "_withStopLabels_6k91g_197",
22
- stopLabels: "_stopLabels_6k91g_201",
23
- stopLabel: "_stopLabel_6k91g_201 _numeric_zns9n_4"
24
- };
25
- //#endregion
26
- //#region src/components/Slider/Slider.tsx
27
- function u(e, t, n) {
28
- return t === void 0 || t <= 0 ? e : Math.round((e - n) / t) * t + n;
29
- }
30
- function d(e, t, n) {
31
- return Math.max(t, Math.min(n, e));
32
- }
33
- var f = 8, p = { capture: !1 };
34
- function m(e, t, n) {
35
- if (!e || e.length === 0) return [];
36
- let r = /* @__PURE__ */ new Map();
37
- for (let i of e) {
38
- let e = typeof i == "number" ? { value: i } : i;
39
- if (e.value < t || e.value > n) continue;
40
- let a = r.get(e.value);
41
- (!a || a.label === void 0 && e.label !== void 0) && r.set(e.value, e);
42
- }
43
- return [...r.values()].sort((e, t) => e.value - t.value);
44
- }
45
- function h(e, t, n, r) {
46
- let i = r(e), a = e, o = n;
47
- for (let e of t) {
48
- let t = Math.abs(r(e) - i);
49
- t <= o && (a = e, o = t);
50
- }
51
- return a;
52
- }
53
- function g(e, t, n, r) {
54
- if (n > 0) {
55
- let n = e.findIndex((e) => e > t);
56
- return n === -1 ? e[e.length - 1] : e[Math.min(e.length - 1, n + r - 1)];
57
- }
58
- let i = -1;
59
- for (let n = e.length - 1; n >= 0; n--) if (e[n] < t) {
60
- i = n;
61
- break;
62
- }
63
- return i === -1 ? e[0] : e[Math.max(0, i - (r - 1))];
64
- }
65
- function _(e, t, n) {
66
- return e !== void 0 && e > 0 ? e : (n - t) / 100;
67
- }
68
- function v(e, t, n, r, i, a) {
69
- let o = a !== void 0 && a > 0 ? a : (i - r) / 1e3;
70
- return d(e, n > 0 ? t[n - 1].value + o : r, n < t.length - 1 ? t[n + 1].value - o : i);
71
- }
72
- function y(e, t, n, r) {
73
- if (!e.bounds) return [n, r];
74
- let i = typeof e.bounds == "function" ? e.bounds(t) : e.bounds;
75
- return [i[0], i[1]];
76
- }
77
- function b(e) {
78
- return `calc(var(--rp-thumb-inset) + ${e} * (100% - 2 * var(--rp-thumb-inset)))`;
79
- }
80
- var x = s({ places: 3 });
81
- function S(s) {
82
- let { thumbs: S, onInput: C, onChange: w, step: T, constraint: E, trackHeight: D, density: O, ariaLabel: ee, className: te } = s, k = a(() => m(s.stops, s.min, s.max), [
83
- s.stops,
84
- s.min,
85
- s.max
86
- ]), A = a(() => k.map((e) => e.value), [k]), j = s.spacing === "even" && A.length >= 2, M = s.snap === "strict" && A.length > 0, N = j ? A[0] : s.min, P = j ? A[A.length - 1] : s.max, F = o(null), I = o(null), L = o(null), R = o(null);
87
- i(() => () => {
88
- R.current?.cancel();
89
- }, []);
90
- let z = r((e) => {
91
- if (P === N) return 0;
92
- if (!j) return d((e - N) / (P - N), 0, 1);
93
- let t = A.length - 1, n = d(e, N, P), r = 0;
94
- for (; r < t - 1 && n > A[r + 1];) r++;
95
- return (r + (n - A[r]) / (A[r + 1] - A[r])) / t;
96
- }, [
97
- N,
98
- P,
99
- j,
100
- A
101
- ]), B = r((e) => {
102
- let t = d(e, 0, 1);
103
- if (!j) return N + t * (P - N);
104
- let n = A.length - 1, r = Math.min(n - 1, Math.floor(t * n));
105
- return A[r] + (t * n - r) * (A[r + 1] - A[r]);
106
- }, [
107
- N,
108
- P,
109
- j,
110
- A
111
- ]), V = r((e, t) => {
112
- let n = B(e);
113
- if (M) return h(n, A, Infinity, z);
114
- let r = d(u(n, T, N), N, P);
115
- return A.length > 0 ? h(r, A, f / t, z) : r;
116
- }, [
117
- B,
118
- z,
119
- M,
120
- A,
121
- T,
122
- N,
123
- P
124
- ]), H = r((t, n, r, i) => {
125
- R.current?.cancel();
126
- let a = (i ?? S).map((e) => ({ ...e }));
127
- L.current = a;
128
- let o = !1;
129
- R.current = e(t, n, {
130
- onMove: (e) => {
131
- let t = I.current, n = L.current;
132
- if (!t || !n) return;
133
- let i = t.getBoundingClientRect(), a = V((e.clientX - i.left) / i.width, i.width), [c, l] = y(n[r], {
134
- thumbs: n,
135
- index: r
136
- }, N, P);
137
- a = d(a, c, l), E === "ordered" && (a = v(a, n, r, N, P, T));
138
- let u = i.height;
139
- s.onRemoveThumb && (o = e.clientY < i.top - u || e.clientY > i.bottom + u), n[r] = {
140
- ...n[r],
141
- value: a
142
- }, C(n.map((e) => ({ ...e })));
143
- },
144
- onEnd: () => {
145
- R.current = null;
146
- let e = L.current;
147
- if (L.current = null, e) {
148
- if (o && s.onRemoveThumb && s.onRemoveThumb(r)) {
149
- let t = e.filter((e, t) => t !== r).map((e) => ({ ...e }));
150
- C(t), w?.(t);
151
- return;
152
- }
153
- w?.(e.map((e) => ({ ...e })));
154
- }
155
- },
156
- onCancel: () => {
157
- R.current = null, L.current = null;
158
- }
159
- }, p);
160
- }, [
161
- S,
162
- C,
163
- w,
164
- V,
165
- N,
166
- P,
167
- T,
168
- E,
169
- s
170
- ]), ne = r((t, n, r) => {
171
- R.current?.cancel();
172
- let i = S.map((e) => ({ ...e })), a = i.map((e) => e.value);
173
- L.current = i, R.current = e(t, n, {
174
- onMove: (e) => {
175
- let t = I.current, n = L.current;
176
- if (!t || !n) return;
177
- let i = t.getBoundingClientRect(), o = (e.clientX - r) / i.width;
178
- if (j) {
179
- let e = a.map(z), t = d(o, -Math.min(...e), 1 - Math.max(...e));
180
- for (let r = 0; r < n.length; r++) n[r] = {
181
- ...n[r],
182
- value: B(e[r] + t)
183
- };
184
- C(n.map((e) => ({ ...e })));
185
- return;
186
- }
187
- let s = o * (P - N);
188
- s = u(s, T, 0);
189
- let c = -Infinity, l = Infinity;
190
- for (let e = 0; e < a.length; e++) c = Math.max(c, N - a[e]), l = Math.min(l, P - a[e]);
191
- s = d(s, c, l);
192
- for (let e = 0; e < n.length; e++) n[e] = {
193
- ...n[e],
194
- value: d(a[e] + s, N, P)
195
- };
196
- C(n.map((e) => ({ ...e })));
197
- },
198
- onEnd: () => {
199
- R.current = null;
200
- let e = L.current;
201
- L.current = null, e && w?.(e.map((e) => ({ ...e })));
202
- },
203
- onCancel: () => {
204
- R.current = null, L.current = null;
205
- }
206
- }, p);
207
- }, [
208
- S,
209
- C,
210
- w,
211
- N,
212
- P,
213
- T,
214
- j,
215
- z,
216
- B
217
- ]), re = (e) => (t) => {
218
- if (typeof t.button == "number" && t.button > 0) return;
219
- t.currentTarget.focus?.(), t.preventDefault(), t.stopPropagation();
220
- let n = t.currentTarget;
221
- t.shiftKey && s.allowShiftAll ? ne(n, t, t.clientX) : H(n, t, e);
222
- }, ie = (e) => (t) => {
223
- if (!s.onRemoveThumb || (t.preventDefault(), !s.onRemoveThumb(e))) return;
224
- let n = S.filter((t, n) => n !== e).map((e) => ({ ...e }));
225
- C(n), w?.(n);
226
- }, U = (e) => {
227
- if (typeof e.button == "number" && e.button > 0) return;
228
- let t = (s.trackClick ?? (S.length === 1 ? "move-nearest" : "none")) === "move-nearest" && S.length > 0;
229
- if (!s.onAddThumb && !t || e.target.closest(`.${l.thumb}`)) return;
230
- e.preventDefault();
231
- let n = F.current, r = I.current;
232
- if (!n || !r) return;
233
- let i = r.getBoundingClientRect(), a = V((e.clientX - i.left) / i.width, i.width);
234
- if (s.onAddThumb) {
235
- let e = s.onAddThumb(a);
236
- if (!e) return;
237
- let t = [...S.map((e) => ({ ...e })), e];
238
- C(t), w?.(t);
239
- return;
240
- }
241
- let o = 0;
242
- for (let e = 1; e < S.length; e++) Math.abs(S[e].value - a) < Math.abs(S[o].value - a) && (o = e);
243
- let c = S.map((e) => ({ ...e })), [u, f] = y(c[o], {
244
- thumbs: c,
245
- index: o
246
- }, N, P), p = d(a, u, f);
247
- E === "ordered" && (p = v(p, c, o, N, P, T)), c[o] = {
248
- ...c[o],
249
- value: p
250
- }, C(c), n.querySelectorAll(`.${l.thumb}`)[o]?.focus(), H(n, e, o, c);
251
- }, W = (e) => (t) => {
252
- let n = _(T, N, P), r = 0, i = 0, a = null;
253
- switch (t.key) {
254
- case "ArrowRight":
255
- case "ArrowUp":
256
- r = t.shiftKey ? n * 10 : n, i = t.shiftKey ? 10 : 1;
257
- break;
258
- case "ArrowLeft":
259
- case "ArrowDown":
260
- r = t.shiftKey ? -n * 10 : -n, i = t.shiftKey ? -10 : -1;
261
- break;
262
- case "PageUp":
263
- r = n * 10, i = 10;
264
- break;
265
- case "PageDown":
266
- r = -n * 10, i = -10;
267
- break;
268
- case "Home":
269
- a = "home";
270
- break;
271
- case "End":
272
- a = "end";
273
- break;
274
- default: return;
275
- }
276
- t.preventDefault();
277
- let o = S.map((e) => ({ ...e })), [s, c] = y(o[e], {
278
- thumbs: o,
279
- index: e
280
- }, N, P), l = Math.max(N, s), f = Math.min(P, c), p;
281
- p = a === "home" ? l : a === "end" ? f : A.length > 0 ? g(A, o[e].value, i > 0 ? 1 : -1, Math.abs(i)) : u(o[e].value + r, T, N), p = d(p, l, f), E === "ordered" && (p = v(p, o, e, l, f, T)), o[e] = {
282
- ...o[e],
283
- value: p
284
- }, C(o), w?.(o);
285
- }, G = s.readoutPlacement ?? "none", K = s.renderReadout, q = s.display, J = O === "slim", Y = {};
286
- J && (Y["--rp-track-height"] = "var(--wzl-slider-track-h)", Y["--rp-thumb-size"] = "var(--wzl-slider-thumb-size)"), D !== void 0 && (Y["--rp-track-height"] = `${D}px`);
287
- let X = s.stopLabels ?? "all", Z = k.length - 1, Q = X === "none" ? [] : k.map((e, t) => ({
288
- stop: e,
289
- i: t
290
- })).filter(({ stop: e, i: t }) => e.label !== void 0 && (X === "all" || t === 0 || t === Z)), $ = Q.length > 0 && /* @__PURE__ */ t("div", {
291
- className: l.stopLabels,
292
- "data-slider-stop-labels": !0,
293
- "aria-hidden": "true",
294
- children: Q.map(({ stop: e, i: n }) => /* @__PURE__ */ t("span", {
295
- className: l.stopLabel,
296
- "data-slider-stop-label": !0,
297
- "data-selected": S.some((t) => t.value === e.value) ? "true" : void 0,
298
- "data-edge": n === 0 ? "start" : n === Z ? "end" : void 0,
299
- style: { left: `${z(e.value) * 100}%` },
300
- children: e.label
301
- }, e.value))
302
- }), ae = [
303
- l.root,
304
- J && l.slim,
305
- s.thumbFit === "overhang" && l.overhang,
306
- $ && l.withStopLabels,
307
- te
308
- ].filter(Boolean).join(" ");
309
- return /* @__PURE__ */ n("div", {
310
- className: ae,
311
- style: Object.keys(Y).length > 0 ? Y : void 0,
312
- children: [/* @__PURE__ */ n("div", {
313
- className: l.row,
314
- children: [/* @__PURE__ */ n("div", {
315
- className: l.track,
316
- ref: F,
317
- onPointerDown: U,
318
- children: [s.renderTrack && /* @__PURE__ */ t("div", {
319
- className: l.trackInner,
320
- children: s.renderTrack({
321
- trackWidth: F.current?.getBoundingClientRect().width ?? 0,
322
- valueToFraction: z,
323
- fractionToPosition: b
324
- })
325
- }), /* @__PURE__ */ n("div", {
326
- className: l.rail,
327
- ref: I,
328
- "data-slider-rail": !0,
329
- children: [
330
- (s.showStops ?? !0) && A.length > 0 && /* @__PURE__ */ t("div", {
331
- className: l.ticks,
332
- "data-slider-ticks": !0,
333
- "aria-hidden": "true",
334
- children: A.map((e) => {
335
- let n = z(e);
336
- return /* @__PURE__ */ t("span", {
337
- className: l.tick,
338
- "data-slider-tick": !0,
339
- "data-fraction": String(n),
340
- style: { left: `${n * 100}%` }
341
- }, e);
342
- })
343
- }),
344
- $,
345
- S.map((e, n) => {
346
- let r = e.shape === "notched", i = typeof e.shape == "object" && e.shape !== null ? e.shape.render : null, a = `${l.thumb}${r ? ` ${l.notched}` : ""}`;
347
- return /* @__PURE__ */ t("div", {
348
- role: "slider",
349
- tabIndex: 0,
350
- "aria-orientation": "horizontal",
351
- "aria-valuemin": N,
352
- "aria-valuemax": P,
353
- "aria-valuenow": e.value,
354
- "aria-valuetext": e.valueText ?? (q ? c(e.value, q).spoken : void 0),
355
- "aria-label": [ee, e.label].filter(Boolean).join(" ") || void 0,
356
- className: a,
357
- style: { left: `${z(e.value) * 100}%` },
358
- onPointerDown: re(n),
359
- onKeyDown: W(n),
360
- onContextMenu: ie(n),
361
- children: i ? i({
362
- width: 14,
363
- height: 24,
364
- isActive: !1
365
- }) : e.label ?? ""
366
- }, n);
367
- })
368
- ]
369
- })]
370
- }), G === "inline-after" && /* @__PURE__ */ t("span", {
371
- "data-readout": "inline",
372
- className: l.readoutInline,
373
- children: S.map((e, t) => /* @__PURE__ */ n("span", { children: [t > 0 ? " / " : "", K ? K(e, t) : c(e.value, q ?? x).text] }, t))
374
- })]
375
- }), G === "below-thumb" && /* @__PURE__ */ t("div", {
376
- className: l.readoutsBelow,
377
- children: S.map((e, n) => /* @__PURE__ */ t("span", {
378
- "data-readout": "below",
379
- className: l.readoutBelow,
380
- style: { left: `${z(e.value) * 100}%` },
381
- children: K ? K(e, n) : c(e.value, q ?? x).text
382
- }, n))
383
- })]
384
- });
385
- }
386
- //#endregion
387
- export { S as t };
388
-
389
- //# sourceMappingURL=Slider-DSrlMEq8.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Slider-DSrlMEq8.js","names":["s","Slider_module_default","quantize","step","v","min","clamp","lo","hi","STOP_SNAP_PX","NO_CAPTURE","usableStops","stops","byValue","stop","s","max","seen","a","b","attract","f","toFraction","best","bestGap","tolerance","gap","stepStops","direction","first","count","i","defaultStep","clampOrdered","index","thumbs","resolveBounds","thumb","fallbackMin","fallbackMax","tuple","ctx","fractionToPosition","READOUT","decimal","Slider","props","stopList","useMemo","even","strict","trackRef","useRef","railRef","dragBufferRef","sessionRef","valueToFraction","useCallback","gaps","x","fractionToValue","c","valueAt","q","trackWidth","beginThumbDrag","buf","seed","t","droppedOff","openPointerSession","origin","down","rail","buffer","rect","ev","bLo","bHi","constraint","bandHeight","onInput","next","onChange","beginShiftAllDrag","startValues","dxFraction","anchorX","starts","df","dValue","allowedNeg","allowedPos","onThumbPointerDown","e","onThumbContextMenu","onTrackPointerDown","wantsMove","track","created","moved","onThumbKeyDown","stepSize","delta","stopDelta","snapTo","placement","renderReadout","display","slim","density","rootVars","trackHeight","labelMode","lastStop","labeledStops","stopLabelRow","rootClass","className","isNotched","customRender","cls","qty","ariaLabel"],"sources":["../../src/components/Slider/Slider.module.css","../../src/components/Slider/Slider.tsx"],"sourcesContent":[".root {\n /* The value span stops this far short of each end of the track. Half a\n * thumb puts an end thumb flush with the edge; `fractionToPosition` in\n * Slider.tsx is the same geometry for painters. */\n --rp-thumb-inset: calc(var(--rp-thumb-size, 14px) / 2);\n position: relative;\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n}\n\n.row {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-4);\n}\n\n.track {\n position: relative;\n flex: 1;\n min-width: 0;\n height: var(--rp-track-height, 24px);\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n cursor: var(--rp-track-cursor, crosshair);\n /* No `overflow: hidden` here — thumbs at extreme values would get\n * clipped at the track edges. The gradient/track-paint clip is\n * applied to `.trackInner` instead, which lets thumbs spill freely. */\n}\n\n/* Invisible hit area past the track and each thumb. The thumbs come later in\n * the tree, so a thumb's slop wins where the two overlap. */\n.track::before,\n.thumb::before {\n content: '';\n position: absolute;\n /* Measured from the padding box, so each 1px border comes back out. */\n inset: calc(-1px - var(--wzl-slider-track-slop));\n}\n\n.thumb::before {\n inset: calc(-1px - var(--wzl-slider-thumb-slop));\n}\n\n.slim .thumb::before,\n.notched::before {\n inset: calc(-1 * var(--wzl-slider-thumb-slop));\n}\n\n.overhang {\n --rp-thumb-inset: 0px;\n}\n\n.rail {\n position: absolute;\n inset: 0 var(--rp-thumb-inset);\n}\n\n.trackInner {\n position: absolute;\n inset: 0;\n border-radius: var(--wzl-radius-sm);\n overflow: hidden;\n}\n\n.thumb {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n position: absolute;\n /* Thumb extends 2px above + below the track for grabbability + visual\n * weight. Track itself stays its natural height; the thumb just spills. */\n top: -2px;\n bottom: -2px;\n width: var(--rp-thumb-size, 14px);\n margin-left: calc(var(--rp-thumb-size, 14px) / -2);\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--wzl-fg-muted) 70%, transparent);\n border: 1px solid var(--wzl-border-strong);\n border-radius: var(--wzl-radius-sm);\n cursor: ew-resize;\n /* Frosted-glass over the (often colorful) track. Cheap on modern GPUs;\n * gracefully degrades to just the partial-alpha background on engines\n * without backdrop-filter support. */\n backdrop-filter: blur(3px);\n -webkit-backdrop-filter: blur(3px);\n /* Subtle raise — sells the \"thumb is on top of the track\" hierarchy. */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n font-weight: var(--wzl-font-weight-medium);\n font-size: var(--wzl-font-size-xs);\n line-height: var(--wzl-leading-tight);\n color: var(--wzl-fg-inverse);\n /* White halo so labels stay legible over wildly varying gradient\n * backgrounds (e.g. labels 'T' / 'P' / 'B' over teal, hue 200° over\n * yellow-green, etc.). */\n text-shadow: 0 0 2px rgba(255, 255, 255, 0.7);\n}\n\n/* A slim slider's thumb is round and flush with the track, matching the\n property rows. The default thumb keeps its 2px spill and rounded-rect face. */\n.slim .thumb {\n top: 50%;\n bottom: auto;\n height: var(--rp-thumb-size);\n transform: translateY(-50%);\n border-radius: 50%;\n border: none;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n box-shadow: none;\n background: color-mix(in srgb, var(--wzl-accent) var(--wzl-slider-thumb-mix), transparent);\n}\n\n.thumb:focus-visible {\n outline: 2px solid var(--wzl-accent);\n outline-offset: 1px;\n}\n\n.thumbActive {\n /* Visual marker for the dragging/focused thumb; subclass-overridable. */\n z-index: 1;\n}\n\n/* The row holds absolutely-positioned labels, so it has to state its own\n height: the text's, not the thumb's. It said 14px — the default thumb's\n size — which left 4px of dead space under the labels at either density. */\n.readoutsBelow {\n position: relative;\n margin-inline: var(--rp-thumb-inset);\n height: calc(var(--wzl-font-size-xs) * var(--wzl-leading-tight));\n margin-top: var(--wzl-space-2);\n}\n\n.readoutBelow {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n position: absolute;\n transform: translateX(-50%);\n font-weight: var(--wzl-font-weight-medium);\n font-size: var(--wzl-font-size-xs);\n line-height: var(--wzl-leading-tight);\n color: var(--wzl-fg-muted);\n white-space: nowrap;\n}\n\n.readoutInline {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n display: inline-block;\n margin-left: var(--wzl-space-4);\n font-weight: var(--wzl-font-weight-medium);\n font-size: var(--wzl-font-size-sm);\n line-height: var(--wzl-leading-tight);\n color: var(--wzl-fg-muted);\n vertical-align: middle;\n}\n\n.notched {\n background: var(--thumb-svg, none);\n background-size: 100% 100%;\n background-repeat: no-repeat;\n background-color: transparent;\n border: none;\n /* Notched thumbs need to disable the .thumb defaults that paint to\n * the bounding box: backdrop-filter and box-shadow both ignore the\n * SVG's polygon/notch and would render through the notch cutout.\n * Use filter: drop-shadow instead — it traces the actual rendered\n * alpha (i.e. the polygon shape including the cut). */\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n box-shadow: none;\n filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.25));\n /* Default notched SVG (down-pointing pentagon, matches the perceptual-color experiment). */\n --thumb-svg: url(\"data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 30' preserveAspectRatio='none'%3E%3Cpolygon points='0,0 2.52,0 7,10.3 11.48,0 14,0 14,30 0,30' fill='rgba(255,255,255,0.62)' stroke='rgba(0,0,0,0.55)' stroke-width='0.75' stroke-linejoin='miter'/%3E%3C/svg%3E\");\n}\n\n.ticks {\n position: absolute;\n inset: 0;\n /* A mark must never eat the track click that adds a thumb, nor the\n * pointerdown that starts a drag through it. */\n pointer-events: none;\n}\n\n.tick {\n position: absolute;\n top: var(--rp-tick-inset, 20%);\n bottom: var(--rp-tick-inset, 20%);\n width: var(--rp-tick-width, 1px);\n margin-left: calc(var(--rp-tick-width, 1px) / -2);\n border-radius: var(--wzl-radius-pill);\n background: var(--rp-tick-color, var(--wzl-border-strong));\n}\n\n/* The labels hang below the track out of flow, so the row reserves their\n * height and anything after it — the below-thumb readouts — clears them.\n * Padding, not margin: a margin would collapse into whatever follows. */\n.withStopLabels .row {\n padding-bottom: calc(var(--wzl-space-xs) + var(--wzl-font-size-xs) * var(--wzl-leading-tight));\n}\n\n.stopLabels {\n position: absolute;\n left: 0;\n right: 0;\n top: calc(100% + var(--wzl-space-xs));\n height: calc(var(--wzl-font-size-xs) * var(--wzl-leading-tight));\n pointer-events: none;\n}\n\n.stopLabel {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n position: absolute;\n transform: translateX(-50%);\n white-space: nowrap;\n font-size: var(--wzl-font-size-xs);\n line-height: var(--wzl-leading-tight);\n color: var(--wzl-fg-subtle);\n}\n\n/* The end labels are pinned inside the track rather than centered on it, so a\n * wide one can't spill past the control and misalign whatever sits beside it. */\n.stopLabel[data-edge=\"start\"] {\n transform: none;\n}\n\n.stopLabel[data-edge=\"end\"] {\n transform: translateX(-100%);\n}\n\n.stopLabel[data-selected] {\n color: var(--wzl-fg);\n font-weight: var(--wzl-font-weight-medium);\n}\n","import { useCallback, useEffect, useMemo, useRef, type CSSProperties, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactElement, type ReactNode } from 'react';\nimport { openPointerSession, type PointerSession } from '@weasel-js/core';\nimport s from './Slider.module.css';\nimport { decimal, qty, type Display } from '@weasel-js/quantity';\n\n/**\n * Passed to a custom thumb renderer: the thumb box in CSS px, and whether\n * this thumb is the one being dragged.\n */\nexport type ThumbRenderCtx = {\n width: number;\n height: number;\n isActive: boolean;\n};\n\n/**\n * A thumb's appearance — one of the two built-in shapes, or a custom\n * renderer.\n */\nexport type ThumbShape =\n | 'round'\n | 'notched'\n | { render: (ctx: ThumbRenderCtx) => ReactNode };\n\n/**\n * One handle on a {@link Slider}. `bounds` narrows the range this particular\n * thumb may move within, either fixed or computed from the current thumb\n * list.\n */\nexport type Thumb = {\n value: number;\n label?: string;\n shape?: ThumbShape;\n bounds?: [number, number] | ((ctx: BoundsCtx) => [number, number]);\n /**\n * Spoken form of `value`, published as `aria-valuetext`. Required by ARIA\n * whenever `value` is not the quantity the user is choosing — an index into\n * a value list, a log-scaled position — since `aria-valuenow` alone then\n * announces a number that means nothing.\n */\n valueText?: string;\n};\n\n/**\n * Passed to a thumb's `bounds` function: the full thumb list and this thumb's\n * index in it, so a bound can be expressed relative to its neighbors.\n */\nexport type BoundsCtx = {\n thumbs: readonly Thumb[];\n index: number;\n};\n\n/**\n * Passed to `renderTrack`: the track's width in CSS px, a mapping from a\n * slider value to its 0..1 position along the value span, and the CSS\n * position (a `calc()` against the painted box's width) of a 0..1 fraction of\n * that span. A painter places everything that must line up with the thumbs\n * through `fractionToPosition`: under `thumbFit: 'inside'` the span stops half\n * a thumb short of each end of the track.\n */\nexport type TrackCtx = {\n trackWidth: number;\n valueToFraction: (v: number) => number;\n fractionToPosition: (f: number) => string;\n};\n\n/** A {@link Slider} stop that carries a label, drawn under the track at the stop. */\nexport type SliderStop = {\n value: number;\n label?: ReactNode;\n};\n\n/**\n * Props for {@link Slider}.\n *\n * `onInput` fires continuously through a drag; `onChange` fires once when it\n * ends and is the one to write to history.\n *\n * `stops` are attractors: a drag that passes within a few pixels of one lands\n * on it, and the arrow keys move stop to stop. `step` still quantizes the\n * values between them. Each one is drawn on the track as a mark; pass\n * `showStops: false` for a track whose own paint already reads as the stops.\n * A stop given as a {@link SliderStop} with a `label` gets that label drawn\n * under it; `stopLabels` narrows the row to the two ends or turns it off.\n *\n * `snap: 'strict'` makes every stop a detent the thumb must rest on: drags and\n * track presses land on the nearest one, and `step` no longer applies.\n * `spacing: 'even'` places the stops at equal intervals, runs the track from\n * the first stop to the last, and maps values linearly within each gap — so a\n * geometric list (0.25/0.5/1/2/4) gets usable room for every stop instead of\n * four crowded into the first fifth. Both need stops; with fewer than two,\n * `'even'` falls back to linear.\n *\n * `trackClick: 'move-nearest'` makes a press on bare track send the closest\n * thumb there, focus it, and continue as a drag. It is the default for a\n * single thumb. With several it defaults to `'none'`, because on a\n * multi-thumb editor a stray click would yank a stop the user was not aiming\n * at; `onAddThumb`, where it is set, keeps the track press.\n *\n * `constraint: 'ordered'` keeps thumbs from crossing each other. Supplying\n * `onAddThumb` makes a click on empty track create a thumb, and supplying\n * `onRemoveThumb` lets a right-click or a drag off the track remove one —\n * both callbacks can decline by returning `null`/`false`. `allowShiftAll`\n * makes shift-drag translate every thumb together.\n *\n * `thumbFit: 'inside'`, the default, runs the value span half a thumb short of\n * each end of the track, so a thumb at `min` or `max` sits flush with the\n * track's edge instead of hanging half over it — the layout a native range\n * input has. `'overhang'` runs the span edge to edge and centers an end thumb\n * on the edge.\n */\nexport type SliderProps<T extends Thumb = Thumb> = {\n thumbs: readonly T[];\n onInput: (next: T[]) => void;\n onChange?: (next: T[]) => void;\n min: number;\n max: number;\n step?: number;\n stops?: readonly (number | SliderStop)[];\n showStops?: boolean;\n stopLabels?: 'all' | 'ends' | 'none';\n snap?: 'magnetic' | 'strict';\n spacing?: 'linear' | 'even';\n trackClick?: 'none' | 'move-nearest';\n constraint?: 'free' | 'ordered';\n onAddThumb?: (atValue: number) => T | null;\n onRemoveThumb?: (index: number) => boolean;\n allowShiftAll?: boolean;\n renderTrack?: (ctx: TrackCtx) => ReactNode;\n thumbFit?: 'inside' | 'overhang';\n trackHeight?: number;\n /** `'slim'` drives the track and thumb from the kit's slider tokens, so a\n * Slider matches the property rows. `trackHeight` still wins if given. */\n density?: 'default' | 'slim';\n renderReadout?: (thumb: T, index: number) => ReactNode;\n /** How a thumb's value shows in the default readout and is spoken as its\n * `aria-valuetext` when the thumb gives no `valueText` of its own. Default:\n * three decimals shown, nothing spoken beyond `aria-valuenow`. */\n display?: Display;\n readoutPlacement?: 'none' | 'inline-after' | 'below-thumb';\n ariaLabel?: string;\n className?: string;\n};\n\nfunction quantize(v: number, step: number | undefined, min: number): number {\n if (step === undefined || step <= 0) return v;\n return Math.round((v - min) / step) * step + min;\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, v));\n}\n\n/** How close a drag has to come to a stop, in track pixels, to land on it. */\nconst STOP_SNAP_PX = 8;\n\n/** A custom `ThumbShape.render` may put interactive content inside the thumb,\n * and capture would retarget pointerup and kill the click on it. */\nconst NO_CAPTURE = { capture: false } as const;\n\n/** The stops that are actually reachable: inside the range, deduped, ascending.\n * A duplicate keeps the first label given for its value. */\nfunction usableStops(stops: SliderProps['stops'], min: number, max: number): SliderStop[] {\n if (!stops || stops.length === 0) return [];\n const byValue = new Map<number, SliderStop>();\n for (const stop of stops) {\n const s = typeof stop === 'number' ? { value: stop } : stop;\n if (s.value < min || s.value > max) continue;\n const seen = byValue.get(s.value);\n if (!seen || (seen.label === undefined && s.label !== undefined)) byValue.set(s.value, s);\n }\n return [...byValue.values()].sort((a, b) => a.value - b.value);\n}\n\n/** The stop nearest `v` along the track, or `v` itself when none lies within\n * `tolerance` (a track fraction). Measured on the track rather than in value\n * units so evenly spaced stops attract over the same distance. */\nfunction attract(v: number, stops: number[], tolerance: number, toFraction: (v: number) => number): number {\n const f = toFraction(v);\n let best = v;\n let bestGap = tolerance;\n for (const stop of stops) {\n const gap = Math.abs(toFraction(stop) - f);\n if (gap <= bestGap) {\n best = stop;\n bestGap = gap;\n }\n }\n return best;\n}\n\n/** The stop `count` places past `v` in `direction`, saturating at either end. */\nfunction stepStops(stops: number[], v: number, direction: 1 | -1, count: number): number {\n if (direction > 0) {\n const first = stops.findIndex(stop => stop > v);\n if (first === -1) return stops[stops.length - 1];\n return stops[Math.min(stops.length - 1, first + count - 1)];\n }\n let first = -1;\n for (let i = stops.length - 1; i >= 0; i--) {\n if (stops[i] < v) {\n first = i;\n break;\n }\n }\n if (first === -1) return stops[0];\n return stops[Math.max(0, first - (count - 1))];\n}\n\nfunction defaultStep(step: number | undefined, min: number, max: number): number {\n if (step !== undefined && step > 0) return step;\n return (max - min) / 100;\n}\n\n/** Keep a thumb inside its neighbors when `constraint` is `'ordered'`. */\nfunction clampOrdered(\n v: number,\n thumbs: readonly Thumb[],\n index: number,\n min: number,\n max: number,\n step: number | undefined,\n): number {\n const gap = step !== undefined && step > 0 ? step : (max - min) / 1000;\n const lower = index > 0 ? thumbs[index - 1].value + gap : min;\n const upper = index < thumbs.length - 1 ? thumbs[index + 1].value - gap : max;\n return clamp(v, lower, upper);\n}\n\nfunction resolveBounds(thumb: Thumb, ctx: BoundsCtx, fallbackMin: number, fallbackMax: number): [number, number] {\n if (!thumb.bounds) return [fallbackMin, fallbackMax];\n const tuple = typeof thumb.bounds === 'function' ? thumb.bounds(ctx) : thumb.bounds;\n return [tuple[0], tuple[1]];\n}\n\n/** Where fraction `f` of the value span falls in a box as wide as the track.\n * The rail element is laid out from the same `--rp-thumb-inset`. */\nfunction fractionToPosition(f: number): string {\n return `calc(var(--rp-thumb-inset) + ${f} * (100% - 2 * var(--rp-thumb-inset)))`;\n}\n\nconst READOUT = decimal({ places: 3 });\n\n/**\n * Multi-thumb slider over a shared track. The thumb list is fully controlled:\n * every change, live or committed, arrives as a whole new array.\n *\n * Thumbs are draggable, and arrow/Home/End move the focused thumb — those\n * keystrokes fire `onInput` and `onChange` together, since there is no\n * in-flight state to buffer.\n */\nexport function Slider<T extends Thumb = Thumb>(props: SliderProps<T>): ReactElement {\n const { thumbs, onInput, onChange, step, constraint, trackHeight, density, ariaLabel, className } = props;\n\n const stopList = useMemo(() => usableStops(props.stops, props.min, props.max), [props.stops, props.min, props.max]);\n const stops = useMemo(() => stopList.map(stop => stop.value), [stopList]);\n const even = props.spacing === 'even' && stops.length >= 2;\n const strict = props.snap === 'strict' && stops.length > 0;\n const min = even ? stops[0] : props.min;\n const max = even ? stops[stops.length - 1] : props.max;\n\n const trackRef = useRef<HTMLDivElement | null>(null);\n // The value span. Every pointer is measured against it, and thumbs, ticks\n // and stop labels are laid out inside it, so they agree by construction.\n const railRef = useRef<HTMLDivElement | null>(null);\n // In-flight thumb buffer during a drag; null when not dragging.\n const dragBufferRef = useRef<T[] | null>(null);\n const sessionRef = useRef<PointerSession | null>(null);\n\n useEffect(() => () => { sessionRef.current?.cancel(); }, []);\n\n const valueToFraction = useCallback(\n (v: number): number => {\n if (max === min) return 0;\n if (!even) return clamp((v - min) / (max - min), 0, 1);\n const gaps = stops.length - 1;\n const x = clamp(v, min, max);\n let i = 0;\n while (i < gaps - 1 && x > stops[i + 1]) i++;\n return (i + (x - stops[i]) / (stops[i + 1] - stops[i])) / gaps;\n },\n [min, max, even, stops],\n );\n\n const fractionToValue = useCallback(\n (f: number): number => {\n const c = clamp(f, 0, 1);\n if (!even) return min + c * (max - min);\n const gaps = stops.length - 1;\n const i = Math.min(gaps - 1, Math.floor(c * gaps));\n return stops[i] + (c * gaps - i) * (stops[i + 1] - stops[i]);\n },\n [min, max, even, stops],\n );\n\n /** The value a pointer at track fraction `f` chooses, before bounds. */\n const valueAt = useCallback(\n (f: number, trackWidth: number): number => {\n const v = fractionToValue(f);\n if (strict) return attract(v, stops, Infinity, valueToFraction);\n const q = clamp(quantize(v, step, min), min, max);\n return stops.length > 0 ? attract(q, stops, STOP_SNAP_PX / trackWidth, valueToFraction) : q;\n },\n [fractionToValue, valueToFraction, strict, stops, step, min, max],\n );\n\n // `seed` replaces the thumb list the drag starts from. A track press moves a\n // thumb before the drag begins, and without seeding, a release with no\n // movement would commit the stale buffer and undo that move.\n const beginThumbDrag = useCallback(\n (origin: Element, down: ReactPointerEvent, index: number, seed?: readonly T[]) => {\n sessionRef.current?.cancel();\n const buf: T[] = (seed ?? thumbs).map(t => ({ ...t }));\n dragBufferRef.current = buf;\n let droppedOff = false;\n\n const onMove = (ev: PointerEvent) => {\n const rail = railRef.current;\n const buffer = dragBufferRef.current;\n if (!rail || !buffer) return;\n const rect = rail.getBoundingClientRect();\n let v = valueAt((ev.clientX - rect.left) / rect.width, rect.width);\n\n const [bLo, bHi] = resolveBounds(buffer[index], { thumbs: buffer, index }, min, max);\n v = clamp(v, bLo, bHi);\n\n if (constraint === 'ordered') v = clampOrdered(v, buffer, index, min, max, step);\n\n // Drop-off detection: pointer exits the track vertically by more than trackHeight.\n const bandHeight = rect.height;\n if (props.onRemoveThumb) {\n if (ev.clientY < rect.top - bandHeight || ev.clientY > rect.bottom + bandHeight) {\n droppedOff = true;\n } else {\n droppedOff = false;\n }\n }\n\n buffer[index] = { ...buffer[index], value: v };\n onInput(buffer.map(t => ({ ...t })));\n };\n\n // A drag that ends without a release — cancelled pointer, lost capture,\n // an unmount — drops the buffer without committing.\n const onCancel = () => {\n sessionRef.current = null;\n dragBufferRef.current = null;\n };\n\n const onEnd = () => {\n sessionRef.current = null;\n const buffer = dragBufferRef.current;\n dragBufferRef.current = null;\n if (!buffer) return;\n\n if (droppedOff && props.onRemoveThumb) {\n const accepted = props.onRemoveThumb(index);\n if (accepted) {\n const next = buffer.filter((_, i) => i !== index).map(t => ({ ...t })) as T[];\n onInput(next);\n onChange?.(next);\n return;\n }\n }\n\n onChange?.(buffer.map(t => ({ ...t })));\n };\n\n sessionRef.current = openPointerSession(origin, down, { onMove, onEnd, onCancel }, NO_CAPTURE);\n },\n [thumbs, onInput, onChange, valueAt, min, max, step, constraint, props],\n );\n\n const beginShiftAllDrag = useCallback(\n (origin: Element, down: ReactPointerEvent, anchorX: number) => {\n sessionRef.current?.cancel();\n const buf: T[] = thumbs.map(t => ({ ...t }));\n const startValues = buf.map(t => t.value);\n dragBufferRef.current = buf;\n\n const onMove = (ev: PointerEvent) => {\n const rail = railRef.current;\n const buffer = dragBufferRef.current;\n if (!rail || !buffer) return;\n const rect = rail.getBoundingClientRect();\n const dxFraction = (ev.clientX - anchorX) / rect.width;\n // Evenly spaced gaps span different amounts of value, so equal value\n // offsets would pull the thumbs apart; move them by track distance.\n if (even) {\n const starts = startValues.map(valueToFraction);\n const df = clamp(dxFraction, -Math.min(...starts), 1 - Math.max(...starts));\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] = { ...buffer[i], value: fractionToValue(starts[i] + df) };\n }\n onInput(buffer.map(t => ({ ...t })));\n return;\n }\n let dValue = dxFraction * (max - min);\n dValue = quantize(dValue, step, 0);\n\n // Clamp delta so no thumb leaves [min, max] (per-thumb bounds intentionally\n // not enforced — matches the experiment's hue-band shift-translate semantics).\n let allowedNeg = -Infinity;\n let allowedPos = Infinity;\n for (let i = 0; i < startValues.length; i++) {\n allowedNeg = Math.max(allowedNeg, min - startValues[i]);\n allowedPos = Math.min(allowedPos, max - startValues[i]);\n }\n dValue = clamp(dValue, allowedNeg, allowedPos);\n\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] = { ...buffer[i], value: clamp(startValues[i] + dValue, min, max) };\n }\n onInput(buffer.map(t => ({ ...t })));\n };\n\n const onCancel = () => {\n sessionRef.current = null;\n dragBufferRef.current = null;\n };\n\n const onEnd = () => {\n sessionRef.current = null;\n const buffer = dragBufferRef.current;\n dragBufferRef.current = null;\n if (buffer) onChange?.(buffer.map(t => ({ ...t })));\n };\n\n sessionRef.current = openPointerSession(origin, down, { onMove, onEnd, onCancel }, NO_CAPTURE);\n },\n [thumbs, onInput, onChange, min, max, step, even, valueToFraction, fractionToValue],\n );\n\n const onThumbPointerDown = (index: number) => (e: ReactPointerEvent) => {\n // Only bail on explicit non-primary buttons (button > 0). jsdom's PointerEvent\n // leaves `button` undefined; treat that as primary so tests can drive drags.\n if (typeof e.button === 'number' && e.button > 0) return;\n // preventDefault below suppresses the focus the press would otherwise\n // give the thumb, and the arrow keys are on the thumb.\n (e.currentTarget as HTMLElement).focus?.();\n e.preventDefault();\n e.stopPropagation();\n const origin = e.currentTarget as HTMLElement;\n if (e.shiftKey && props.allowShiftAll) {\n beginShiftAllDrag(origin, e, e.clientX);\n } else {\n beginThumbDrag(origin, e, index);\n }\n };\n\n const onThumbContextMenu = (index: number) => (e: ReactMouseEvent) => {\n if (!props.onRemoveThumb) return;\n e.preventDefault();\n const accepted = props.onRemoveThumb(index);\n if (!accepted) return;\n const next = thumbs.filter((_, i) => i !== index).map(t => ({ ...t })) as T[];\n onInput(next);\n onChange?.(next);\n };\n\n const onTrackPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (typeof e.button === 'number' && e.button > 0) return;\n const trackClick = props.trackClick ?? (thumbs.length === 1 ? 'move-nearest' : 'none');\n const wantsMove = trackClick === 'move-nearest' && thumbs.length > 0;\n if (!props.onAddThumb && !wantsMove) return;\n // If the event originated on a thumb, the thumb's own handler ran first; this is a track click.\n if ((e.target as HTMLElement).closest(`.${s.thumb}`)) return;\n e.preventDefault();\n const track = trackRef.current;\n const rail = railRef.current;\n if (!track || !rail) return;\n const rect = rail.getBoundingClientRect();\n const v = valueAt((e.clientX - rect.left) / rect.width, rect.width);\n\n if (props.onAddThumb) {\n const created = props.onAddThumb(v);\n if (!created) return;\n const next = [...thumbs.map(t => ({ ...t })), created] as T[];\n onInput(next);\n onChange?.(next);\n return;\n }\n\n let index = 0;\n for (let i = 1; i < thumbs.length; i++) {\n if (Math.abs(thumbs[i].value - v) < Math.abs(thumbs[index].value - v)) index = i;\n }\n const next = thumbs.map(t => ({ ...t })) as T[];\n const [bLo, bHi] = resolveBounds(next[index], { thumbs: next, index }, min, max);\n let moved = clamp(v, bLo, bHi);\n if (constraint === 'ordered') moved = clampOrdered(moved, next, index, min, max, step);\n next[index] = { ...next[index], value: moved };\n onInput(next);\n // preventDefault above suppresses the focus the press would give; the arrow keys are on the thumb.\n track.querySelectorAll<HTMLElement>(`.${s.thumb}`)[index]?.focus();\n beginThumbDrag(track, e, index, next);\n };\n\n const onThumbKeyDown = (index: number) => (e: ReactKeyboardEvent) => {\n const stepSize = defaultStep(step, min, max);\n let delta = 0;\n // With stops, a keystroke moves by whole stops rather than by value.\n let stopDelta = 0;\n let snapTo: 'home' | 'end' | null = null;\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n delta = e.shiftKey ? stepSize * 10 : stepSize;\n stopDelta = e.shiftKey ? 10 : 1;\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n delta = e.shiftKey ? -stepSize * 10 : -stepSize;\n stopDelta = e.shiftKey ? -10 : -1;\n break;\n case 'PageUp':\n delta = stepSize * 10;\n stopDelta = 10;\n break;\n case 'PageDown':\n delta = -stepSize * 10;\n stopDelta = -10;\n break;\n case 'Home':\n snapTo = 'home';\n break;\n case 'End':\n snapTo = 'end';\n break;\n default:\n return;\n }\n\n e.preventDefault();\n const next = thumbs.map(t => ({ ...t }));\n const [bLo, bHi] = resolveBounds(next[index], { thumbs: next, index }, min, max);\n const lo = Math.max(min, bLo);\n const hi = Math.min(max, bHi);\n let v: number;\n if (snapTo === 'home') v = lo;\n else if (snapTo === 'end') v = hi;\n else if (stops.length > 0) {\n v = stepStops(stops, next[index].value, stopDelta > 0 ? 1 : -1, Math.abs(stopDelta));\n } else {\n v = quantize(next[index].value + delta, step, min);\n }\n v = clamp(v, lo, hi);\n if (constraint === 'ordered') v = clampOrdered(v, next, index, lo, hi, step);\n next[index] = { ...next[index], value: v };\n onInput(next);\n onChange?.(next);\n };\n\n const placement = props.readoutPlacement ?? 'none';\n const renderReadout = props.renderReadout;\n const display = props.display;\n\n const slim = density === 'slim';\n const rootVars: Record<string, string> = {};\n if (slim) {\n rootVars['--rp-track-height'] = 'var(--wzl-slider-track-h)';\n rootVars['--rp-thumb-size'] = 'var(--wzl-slider-thumb-size)';\n }\n // An explicit trackHeight wins: a caller who named a number meant it.\n if (trackHeight !== undefined) rootVars['--rp-track-height'] = `${trackHeight}px`;\n\n const labelMode = props.stopLabels ?? 'all';\n const lastStop = stopList.length - 1;\n const labeledStops = labelMode === 'none'\n ? []\n : stopList\n .map((stop, i) => ({ stop, i }))\n .filter(({ stop, i }) => stop.label !== undefined && (labelMode === 'all' || i === 0 || i === lastStop));\n // Inside the track so it shares the track's width, not the row's — an inline\n // readout beside the track would otherwise shift every label off its stop.\n const stopLabelRow = labeledStops.length > 0 && (\n <div className={s.stopLabels} data-slider-stop-labels aria-hidden=\"true\">\n {labeledStops.map(({ stop, i }) => (\n <span\n key={stop.value}\n className={s.stopLabel}\n data-slider-stop-label\n data-selected={thumbs.some(t => t.value === stop.value) ? 'true' : undefined}\n data-edge={i === 0 ? 'start' : i === lastStop ? 'end' : undefined}\n style={{ left: `${valueToFraction(stop.value) * 100}%` }}\n >\n {stop.label}\n </span>\n ))}\n </div>\n );\n const rootClass = [s.root, slim && s.slim, props.thumbFit === 'overhang' && s.overhang, stopLabelRow && s.withStopLabels, className].filter(Boolean).join(' ');\n\n return (\n <div\n className={rootClass}\n style={Object.keys(rootVars).length > 0 ? (rootVars as CSSProperties) : undefined}\n >\n <div className={s.row}>\n <div className={s.track} ref={trackRef} onPointerDown={onTrackPointerDown}>\n {props.renderTrack && (\n <div className={s.trackInner}>\n {props.renderTrack({\n trackWidth: trackRef.current?.getBoundingClientRect().width ?? 0,\n valueToFraction,\n fractionToPosition,\n })}\n </div>\n )}\n <div className={s.rail} ref={railRef} data-slider-rail>\n {(props.showStops ?? true) && stops.length > 0 && (\n <div className={s.ticks} data-slider-ticks aria-hidden=\"true\">\n {stops.map(v => {\n const f = valueToFraction(v);\n return (\n <span\n key={v}\n className={s.tick}\n data-slider-tick\n data-fraction={String(f)}\n style={{ left: `${f * 100}%` }}\n />\n );\n })}\n </div>\n )}\n {stopLabelRow}\n {thumbs.map((thumb, i) => {\n const isNotched = thumb.shape === 'notched';\n const customRender = typeof thumb.shape === 'object' && thumb.shape !== null ? thumb.shape.render : null;\n const cls = `${s.thumb}${isNotched ? ` ${s.notched}` : ''}`;\n return (\n <div\n key={i}\n role=\"slider\"\n tabIndex={0}\n aria-orientation=\"horizontal\"\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={thumb.value}\n aria-valuetext={thumb.valueText ?? (display ? qty(thumb.value, display).spoken : undefined)}\n aria-label={[ariaLabel, thumb.label].filter(Boolean).join(' ') || undefined}\n className={cls}\n style={{ left: `${valueToFraction(thumb.value) * 100}%` }}\n onPointerDown={onThumbPointerDown(i)}\n onKeyDown={onThumbKeyDown(i)}\n onContextMenu={onThumbContextMenu(i)}\n >\n {customRender ? customRender({ width: 14, height: 24, isActive: false }) : (thumb.label ?? '')}\n </div>\n );\n })}\n </div>\n </div>\n {placement === 'inline-after' && (\n <span data-readout=\"inline\" className={s.readoutInline}>\n {thumbs.map((t, i) => (\n <span key={i}>{i > 0 ? ' / ' : ''}{renderReadout ? renderReadout(t, i) : qty(t.value, display ?? READOUT).text}</span>\n ))}\n </span>\n )}\n </div>\n {placement === 'below-thumb' && (\n <div className={s.readoutsBelow}>\n {thumbs.map((t, i) => (\n <span\n key={i}\n data-readout=\"below\"\n className={s.readoutBelow}\n style={{ left: `${valueToFraction(t.value) * 100}%` }}\n >\n {renderReadout ? renderReadout(t, i) : qty(t.value, display ?? READOUT).text}\n </span>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"SwatchGrid-9ZPiDmxd.js","names":["s","useRecordRecentColor","SwatchGrid_module_default","nameOf","o","SwatchGrid","props","record","onChange","v","onAltChange","columns","rootRef","useRef","current","value","options","tabStop","focusAt","i","onKeyDown","last","next","e","className","name"],"sources":["../../src/components/SwatchGrid/SwatchGrid.module.css","../../src/components/SwatchGrid/SwatchGrid.tsx"],"sourcesContent":[".grid {\n display: grid;\n grid-template-columns: repeat(var(--columns), minmax(0, 1fr));\n}\n\n.swatch {\n aspect-ratio: 1 / 1;\n min-inline-size: 0;\n padding: 0;\n border: 0.5px solid var(--wzl-border);\n border-radius: 0;\n /* The color over the checker, so a translucent swatch reads as one. */\n background:\n linear-gradient(var(--swatch), var(--swatch)),\n repeating-conic-gradient(var(--wzl-checker-a) 0% 25%, var(--wzl-checker-b) 0% 50%)\n 50% / var(--wzl-checker-size) var(--wzl-checker-size);\n background-clip: padding-box;\n cursor: pointer;\n}\n\n.swatch:hover {\n border-color: var(--wzl-fg-muted);\n}\n\n.swatch[aria-current] {\n border-color: var(--wzl-accent);\n box-shadow: 0 0 0 1px var(--wzl-accent);\n}\n\n.swatch:focus-visible {\n position: relative;\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n}\n\n/* No paint: the checker crossed by the danger diagonal. */\n.swatch[data-none] {\n background:\n linear-gradient(\n 45deg,\n transparent calc(50% - 1px),\n var(--wzl-danger) calc(50% - 1px),\n var(--wzl-danger) calc(50% + 1px),\n transparent calc(50% + 1px)\n ),\n repeating-conic-gradient(var(--wzl-checker-a) 0% 25%, var(--wzl-checker-b) 0% 50%)\n 50% / var(--wzl-checker-size) var(--wzl-checker-size);\n}\n","import { useRef, type CSSProperties, type KeyboardEvent, type ReactElement } from 'react';\nimport { useRecordRecentColor } from '../RecentColors/RecentColorsProvider';\nimport s from './SwatchGrid.module.css';\n\n/** One swatch. `value: null` is the \"no paint\" swatch. */\nexport interface SwatchGridOption {\n value: string | null;\n /** Accessible name and tooltip. Defaults to the value, or \"None\". */\n label?: string;\n}\n\n/** Props for {@link SwatchGrid}. */\nexport interface SwatchGridProps {\n options: readonly SwatchGridOption[];\n /** The swatch to mark current, if any matches. */\n value?: string | null;\n /** A click, Enter or Space on a swatch. */\n onChange: (value: string | null) => void;\n /** The alternate target — shift-click, right-click (the browser menu is\n * suppressed) and Shift+Enter. Drawing apps route this to the slot that\n * isn't focused: click for fill, shift-click for stroke. */\n onAltChange?: (value: string | null) => void;\n /** Swatches per row; also the step for ArrowUp/ArrowDown. Default 6. */\n columns?: number;\n 'aria-label'?: string;\n className?: string;\n}\n\nfunction nameOf(o: SwatchGridOption): string {\n return o.label ?? o.value ?? 'None';\n}\n\n/**\n * A palette: a grid of color swatches that apply on click. Keyboard focus\n * roves — one tab stop, on the current swatch, with the arrows moving by one\n * across and by a row up and down.\n *\n * The grid applies nothing itself. `onChange` and `onAltChange` hand the\n * color to the app, which decides what \"apply\" means: set its active paint,\n * write the selection through `useOngoingAction`'s `commit`, or both.\n */\nexport function SwatchGrid(props: SwatchGridProps): ReactElement {\n const { options, value, className } = props;\n const record = useRecordRecentColor();\n const onChange = (v: string | null): void => {\n props.onChange(v);\n if (v !== null) record(v);\n };\n const onAltChange = props.onAltChange && ((v: string | null): void => {\n props.onAltChange?.(v);\n if (v !== null) record(v);\n });\n const columns = props.columns ?? 6;\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n const current = value === undefined ? -1 : options.findIndex((o) => o.value === value);\n const tabStop = current >= 0 ? current : 0;\n\n const focusAt = (i: number): void => {\n rootRef.current?.querySelectorAll<HTMLElement>('button')[i]?.focus();\n };\n\n const onKeyDown = (i: number) => (e: KeyboardEvent<HTMLButtonElement>): void => {\n const last = options.length - 1;\n let next = -1;\n switch (e.key) {\n case 'ArrowLeft': next = Math.max(0, i - 1); break;\n case 'ArrowRight': next = Math.min(last, i + 1); break;\n case 'ArrowUp': next = i - columns >= 0 ? i - columns : i; break;\n case 'ArrowDown': next = i + columns <= last ? i + columns : i; break;\n case 'Home': next = 0; break;\n case 'End': next = last; break;\n case 'Enter':\n if (e.shiftKey && onAltChange) {\n e.preventDefault();\n onAltChange(options[i].value);\n }\n return;\n default: return;\n }\n e.preventDefault();\n if (next !== i) focusAt(next);\n };\n\n return (\n <div\n ref={rootRef}\n className={[s.grid, className].filter(Boolean).join(' ')}\n role=\"group\"\n aria-label={props['aria-label']}\n style={{ '--columns': columns } as CSSProperties}\n >\n {options.map((o, i) => {\n const name = nameOf(o);\n return (\n <button\n key={o.value ?? `none-${i}`}\n type=\"button\"\n className={s.swatch}\n aria-label={name}\n title={name}\n tabIndex={i === tabStop ? 0 : -1}\n aria-current={i === current ? 'true' : undefined}\n {...(o.value === null\n ? { 'data-none': '' }\n : { style: { '--swatch': o.value } as CSSProperties })}\n onClick={(e) => {\n if (e.shiftKey && onAltChange) onAltChange(o.value);\n else onChange(o.value);\n }}\n onContextMenu={onAltChange ? (e) => { e.preventDefault(); onAltChange(o.value); } : undefined}\n onKeyDown={onKeyDown(i)}\n />\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AC4BA,SAASG,EAAO,GAA6B;CAC3C,OAAOC,EAAE,SAASA,EAAE,SAAS;AAC/B;AAWA,SAAgBC,EAAW,GAAsC;CAC/D,IAAM,EAAE,YAAS,UAAO,iBAAcC,GAChCC,IAASN,EAAqB,GAC9BO,KAAY,MAA2B;EAE3C,AADAF,EAAM,SAASG,CAAC,GACZA,MAAM,QAAMF,EAAOE,CAAC;CAC1B,GACMC,IAAcJ,EAAM,iBAAiB,MAA2B;EAEpE,AADAA,EAAM,cAAcG,CAAC,GACjBA,MAAM,QAAMF,EAAOE,CAAC;CAC1B,IACME,IAAUL,EAAM,WAAW,GAC3BM,IAAUC,EAA8B,IAAI,GAE5CC,IAAUC,MAAU,KAAA,IAAY,KAAKC,EAAQ,WAAW,MAAMZ,EAAE,UAAUW,CAAK,GAC/EE,IAAUH,KAAW,IAAIA,IAAU,GAEnCI,KAAW,MAAoB;EACnC,EAAQ,SAAS,iBAA8B,QAAQ,CAAC,CAACC,EAAE,EAAE,MAAM;CACrE,GAEMC,KAAa,OAAe,MAA8C;EAC9E,IAAMC,IAAOL,EAAQ,SAAS,GAC1BM,IAAO;EACX,QAAQC,EAAE,KAAV;GACE,KAAK;IAAa,IAAO,KAAK,IAAI,GAAGJ,IAAI,CAAC;IAAG;GAC7C,KAAK;IAAc,IAAO,KAAK,IAAIE,GAAMF,IAAI,CAAC;IAAG;GACjD,KAAK;IAAW,IAAOA,IAAIR,KAAW,IAAIQ,IAAIR,IAAUQ;IAAG;GAC3D,KAAK;IAAa,IAAOA,IAAIR,KAAWU,IAAOF,IAAIR,IAAUQ;IAAG;GAChE,KAAK;IAAQ,IAAO;IAAG;GACvB,KAAK;IAAO,IAAOE;IAAM;GACzB,KAAK;IACH,AAAIE,EAAE,YAAYb,MAChBa,EAAE,eAAe,GACjBb,EAAYM,EAAQG,EAAE,CAAC,KAAK;IAE9B;GACF,SAAS;EACX;EAEA,AADAI,EAAE,eAAe,GACbD,MAASH,KAAGD,EAAQI,CAAI;CAC9B;CAEA,OACE,kBAAC,OAAD;EACE,KAAKV;EACL,WAAW,CAACZ,EAAE,MAAMwB,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EACvD,MAAK;EACL,cAAYlB,EAAM;EAClB,OAAO,EAAE,aAAaK,EAAQ;EAE7B,UAAAK,EAAQ,KAAK,GAAG,MAAM;GACrB,IAAMS,IAAOtB,EAAOC,CAAC;GACrB,OACE,kBAAC,UAAD;IAEE,MAAK;IACL,WAAWJ,EAAE;IACb,cAAYyB;IACZ,OAAOA;IACP,UAAUN,MAAMF,IAAU,IAAI;IAC9B,gBAAcE,MAAML,IAAU,SAAS,KAAA;IACvC,GAAKV,EAAE,UAAU,OACb,EAAE,aAAa,GAAG,IAClB,EAAE,OAAO,EAAE,YAAYA,EAAE,MAAM,EAAmB;IACtD,UAAU,MAAM;KACd,AAAImB,EAAE,YAAYb,IAAaA,EAAYN,EAAE,KAAK,IAC7CI,EAASJ,EAAE,KAAK;IACvB;IACA,eAAeM,KAAe,MAAM;KAAsB,AAApBa,EAAE,eAAe,GAAGb,EAAYN,EAAE,KAAK;IAAG,IAAI,KAAA;IACpF,WAAWgB,EAAUD,CAAC;GACvB,GAhBMf,EAAE,SAAS,QAAQe,GAgBzB;EAEL,CAAC;CACE,CAAA;AAET"}
@@ -1,80 +0,0 @@
1
- import { r as e, t } from "./segmentTooltip-CGxCOWNz.js";
2
- import { t as n } from "./useRovingTabIndex-D7u22Ia_.js";
3
- import { jsx as r } from "react/jsx-runtime";
4
- import "react";
5
- var i = {
6
- root: "_root_15sd7_6",
7
- segment: "_segment_15sd7_30",
8
- segmentSelected: "_segmentSelected_15sd7_84",
9
- segmentMixed: "_segmentMixed_15sd7_143",
10
- variant_minimal: "_variant_minimal_15sd7_153",
11
- size_sm: "_size_sm_15sd7_162",
12
- variant_flat: "_variant_flat_15sd7_237"
13
- };
14
- //#endregion
15
- //#region src/components/ToggleBar/ToggleBar.tsx
16
- function a(a) {
17
- let { items: o, ariaLabel: s, className: c, height: l, size: u, variant: d } = a, f = a.mode ?? "single", p = (e) => f === "multiple" ? a.value.includes(e) : a.value === e, m = (e) => {
18
- if (f !== "multiple") return !1;
19
- let t = a.mixedValues;
20
- return t !== void 0 && t.includes(e) && !p(e);
21
- }, h = (e) => () => {
22
- let t = o[e];
23
- if (!t.disabled) {
24
- if (f === "multiple") {
25
- let e = a.value, n = e.includes(t.value) ? e.filter((e) => e !== t.value) : [...e, t.value];
26
- a.onChange(n);
27
- } else {
28
- if (a.value === t.value) {
29
- a.allowDeselect && a.onChange(null);
30
- return;
31
- }
32
- a.onChange(t.value);
33
- }
34
- }
35
- }, g = f === "single" ? o.findIndex((e) => e.value === a.value) : -1, _ = n({
36
- itemSelector: `.${i.segment}`,
37
- tabStopIndex: g >= 0 ? g : void 0,
38
- onNavigate: f === "single" ? (e) => a.onChange(o[e].value) : void 0,
39
- onActivate: f === "multiple" ? (e) => h(e)() : void 0
40
- }), v = l === void 0 ? void 0 : { "--tb-h": `${l}px` }, y = [
41
- i.root,
42
- u && i[`size_${u}`],
43
- d && i[`variant_${d}`],
44
- c
45
- ].filter(Boolean).join(" ");
46
- return /* @__PURE__ */ r("div", {
47
- ref: _.rootRef,
48
- onKeyDown: _.onKeyDown,
49
- className: y,
50
- role: f === "multiple" ? "group" : "radiogroup",
51
- "aria-label": s,
52
- style: v,
53
- children: o.map((n, a) => {
54
- let o = p(n.value), s = m(n.value), c = [
55
- i.segment,
56
- o && i.segmentSelected,
57
- s && i.segmentMixed
58
- ].filter(Boolean).join(" ");
59
- return /* @__PURE__ */ r(t, {
60
- content: e(n),
61
- disabled: n.disabled,
62
- children: /* @__PURE__ */ r("button", {
63
- type: "button",
64
- role: f === "multiple" ? void 0 : "radio",
65
- "aria-checked": f === "multiple" ? void 0 : o,
66
- "aria-pressed": f === "multiple" ? s ? "mixed" : o : void 0,
67
- "aria-label": n.ariaLabel,
68
- disabled: n.disabled,
69
- className: c,
70
- onClick: h(a),
71
- children: n.label
72
- })
73
- }, n.value);
74
- })
75
- });
76
- }
77
- //#endregion
78
- export { a as t };
79
-
80
- //# sourceMappingURL=ToggleBar-Z2jZiguF.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToggleBar-Z2jZiguF.js","names":["s","segmentTooltipContent","useRovingTabIndex","ToggleBar_module_default","ToggleBar","props","mode","isSelected","value","isMixed","mixed","handleClick","item","items","index","current","next","v","selectedIndex","it","roving","style","height","rootCls","size","variant","className","ariaLabel","selected","cls","SegmentTooltip","i"],"sources":["../../src/components/ToggleBar/ToggleBar.module.css","../../src/components/ToggleBar/ToggleBar.tsx"],"sourcesContent":["/* Aqua-glass segmented control. The track is a pill-shaped well; the\n selected segment becomes a tinted glass cylinder that reads as the same\n \"lozenge\" family as the standalone Button. Visual params come from the\n `--btn-*` custom properties (see Button.module.css) so the Button Lab\n can tune both surfaces with one set of sliders. */\n.root {\n display: inline-flex;\n height: var(--tb-h, var(--wzl-control-h));\n padding: var(--wzl-space-1);\n background: linear-gradient(\n 180deg,\n rgba(0, 0, 0, 0.18) 0%,\n rgba(0, 0, 0, 0.05) 100%\n );\n border-radius: var(--wzl-radius-pill);\n /* Clip segments to the track outline so interior corners read crisp and\n the selected segment's glass treatment doesn't paint over the rounded\n outer pixels. */\n overflow: hidden;\n box-shadow:\n inset 0 1px 2px rgba(0, 0, 0, 0.4),\n 0 1px 0 rgba(255, 255, 255, 0.06);\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n /* The track clips, so without this a squeezing row shrinks it to nothing. */\n min-width: max-content;\n}\n\n.segment {\n position: relative;\n flex: 1 1 0;\n /* Equal widths when there is room, never narrower than the label. The\n segment clips its gloss, which otherwise drops its flex minimum to 0. */\n min-width: max-content;\n padding: 0 var(--wzl-space-6);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n margin: 0;\n font-family: var(--wzl-font-ui);\n font-weight: var(--wzl-font-weight-normal);\n font-size: var(--btn-fontsize, var(--wzl-font-size));\n line-height: 1;\n color: var(--wzl-fg-muted);\n cursor: pointer;\n /* Only round the outer corners of the bar — middle segments stay square\n so the seams between them don't render as facing parentheses. */\n border-radius: 0;\n isolation: isolate;\n overflow: hidden;\n transition: color var(--wzl-motion-fast);\n}\n\n/* 8px around a 14px glyph is most of a segment. */\n.segment:has(> svg) {\n padding: 0 5px;\n}\n\n.segment:first-of-type {\n border-radius: var(--wzl-radius-pill) 0 0 var(--wzl-radius-pill);\n}\n\n.segment:last-of-type {\n border-radius: 0 var(--wzl-radius-pill) var(--wzl-radius-pill) 0;\n}\n\n.segment:only-of-type {\n border-radius: var(--wzl-radius-pill);\n}\n\n.segment:focus-visible {\n outline: 2px solid var(--wzl-accent);\n outline-offset: -2px;\n}\n\n.segment:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.segment:hover:not(:disabled):not(.segmentSelected) {\n color: var(--wzl-fg);\n}\n\n/* Selected segment — full Aqua glass cylinder. Mirrors Button.variant_primary. */\n.segmentSelected {\n background: linear-gradient(\n 180deg,\n color-mix(in srgb, var(--wzl-accent) var(--btn-top-alpha, 45%), transparent) 0%,\n color-mix(in srgb, var(--wzl-accent) var(--btn-upper-alpha, 70%), transparent) calc(var(--btn-equator, 52%) - var(--btn-equator-spread, 12%)),\n color-mix(in srgb, var(--wzl-accent) var(--btn-equator-tint, 95%), white) var(--btn-equator, 52%),\n var(--wzl-accent) calc(var(--btn-equator, 52%) + var(--btn-equator-spread, 12%) * 1.6),\n color-mix(in srgb, var(--wzl-accent) var(--btn-base-tint, 70%), black) 100%\n );\n backdrop-filter: blur(var(--btn-blur, 6px)) saturate(var(--btn-saturate, 1.3));\n -webkit-backdrop-filter: blur(var(--btn-blur, 6px)) saturate(var(--btn-saturate, 1.3));\n color: var(--wzl-fg-on-accent);\n text-shadow:\n 0 1px 0 rgba(0, 0, 0, var(--btn-text-shadow, 0.35)),\n var(--btn-text-inset-x, 0px) var(--btn-text-inset-y, 1px) 0 rgba(0, 0, 0, var(--btn-text-inset, 0.55)),\n 0 0 6px rgba(0, 0, 0, calc(var(--btn-text-shadow, 0.35) * 0.5));\n box-shadow:\n inset 0 1px 0 rgba(255, 255, 255, var(--btn-inset-hi, 0.42)),\n inset 0 -1px 2px rgba(0, 0, 0, var(--btn-inset-lo, 0.3)),\n 0 0 0 1px rgba(0, 0, 0, var(--btn-rim, 0.22)),\n 0 1px 2px rgba(0, 0, 0, var(--btn-drop-near, 0.3)),\n 0 2px 4px rgba(0, 0, 0, calc(var(--btn-drop-far, 0.18) * 0.8));\n}\n\n/* Specular gloss on the selected segment. Top corners inherit the\n segment's radius so they hug the upper edge cleanly; bottom corners\n are flattened so the gloss ends in a straight line at the equator\n instead of two little curves that read as `)(` brackets. */\n.segmentSelected::before {\n content: '';\n position: absolute;\n inset: 1px 1px var(--btn-gloss-bottom, 50%) 1px;\n border-radius: inherit;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, var(--btn-gloss-top, 0.42)) 0%,\n rgba(255, 255, 255, var(--btn-gloss-mid, 0.12)) 60%,\n rgba(255, 255, 255, 0) 100%\n );\n pointer-events: none;\n z-index: 1;\n}\n\n.segmentSelected > * {\n position: relative;\n z-index: 2;\n}\n\n/* Mixed segment (`aria-pressed=\"mixed\"`) — the sources disagree. Reads as a\n deliberate third state rather than a dimmed \"on\": a flat accent wash with\n none of the selected segment's gloss/cylinder treatment, so it can't be\n mistaken for the glass lozenge at a glance. */\n.segmentMixed {\n background: color-mix(in srgb, var(--wzl-accent) 22%, transparent);\n color: var(--wzl-fg);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--wzl-accent) 45%, transparent);\n}\n\n.segmentMixed:hover:not(:disabled) {\n background: color-mix(in srgb, var(--wzl-accent) 32%, transparent);\n}\n\n.variant_minimal .segmentMixed {\n background: color-mix(in srgb, var(--wzl-accent) 18%, transparent);\n box-shadow: none;\n color: var(--wzl-fg);\n}\n\n/* Small variant — ~60% of the default height, with proportionally reduced\n padding and font. Sized for dense surfaces (lab control panels, inline\n filters, etc.). */\n.size_sm {\n height: 17px;\n padding: 1px;\n}\n\n.size_sm .segment {\n padding: 0 var(--wzl-space-4);\n font-size: var(--wzl-font-size-sm);\n}\n\n/* `.size_sm .segment` outranks the glyph rule above on specificity, so a\n small bar's glyph segments need the tight padding restated to stay\n near-square rather than reading as wide plates. */\n.size_sm .segment:has(> svg) {\n padding: 0 3px;\n}\n\n/* Minimal variant — strips the pill track, glass selected state, inset\n shadows, and outer padding. Used in dense diagnostic surfaces (dispatch\n trace, lab inspectors) where the bar must read as plain text controls. */\n.variant_minimal {\n height: auto;\n padding: 0;\n background: none;\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n box-shadow: none;\n gap: 0;\n}\n\n.variant_minimal .segment {\n padding: 0 var(--wzl-space-3);\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-medium);\n line-height: 1.4;\n border-radius: 0;\n color: var(--wzl-fg-muted);\n transition: none;\n}\n\n/* Dividers between segments make the mutual-exclusion grouping visible\n without a heavy track. Only the inner edges get a rule — the outer\n border on `.variant_minimal` handles the perimeter. */\n.variant_minimal .segment ~ .segment {\n border-left: 1px solid var(--wzl-border);\n}\n\n.variant_minimal .segment:first-of-type,\n.variant_minimal .segment:last-of-type,\n.variant_minimal .segment:only-of-type {\n border-radius: 0;\n}\n\n.variant_minimal .segment:hover:not(:disabled):not(.segmentSelected) {\n color: var(--wzl-fg);\n background: var(--wzl-surface-hover);\n}\n\n.variant_minimal .segmentSelected {\n background: none;\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n color: var(--wzl-accent-fg);\n text-shadow: none;\n box-shadow: none;\n font-weight: var(--wzl-font-weight-medium);\n}\n\n.variant_minimal .segmentSelected::before {\n content: none;\n}\n\n/* Flat variant — no track, no glass, no pill: each segment is its own\n outlined cell. For property panels, where a bar sits in a row beside\n inputs and the glass cylinder makes it the loudest thing on the surface. */\n.variant_flat {\n padding: 0;\n background: none;\n border-radius: var(--wzl-radius-sm);\n box-shadow: none;\n overflow: visible;\n}\n\n.variant_flat .segment {\n border: 1px solid var(--wzl-border-strong);\n border-radius: 0;\n /* Sunken, so an unselected cell sits below the panel and the selected one\n reads as raised without needing the glass treatment back. */\n background: var(--wzl-surface-sunken);\n color: var(--wzl-fg-muted);\n}\n\n/* Adjacent cells share one rule rather than stacking two. */\n.variant_flat .segment ~ .segment {\n margin-inline-start: -1px;\n}\n\n.variant_flat .segment:first-of-type {\n border-start-start-radius: var(--wzl-radius-sm);\n border-end-start-radius: var(--wzl-radius-sm);\n}\n\n.variant_flat .segment:last-of-type {\n border-start-end-radius: var(--wzl-radius-sm);\n border-end-end-radius: var(--wzl-radius-sm);\n}\n\n.variant_flat .segment:hover:not(:disabled):not(.segmentSelected) {\n background: var(--wzl-surface-hover);\n color: var(--wzl-fg);\n}\n\n.variant_flat .segmentSelected {\n /* Above its neighbors, or the next cell's left border covers its right one. */\n z-index: 1;\n background: var(--wzl-accent);\n color: var(--wzl-fg-on-accent);\n backdrop-filter: none;\n -webkit-backdrop-filter: none;\n text-shadow: none;\n box-shadow: none;\n}\n\n.variant_flat .segmentSelected::before {\n content: none;\n}\n","import { type CSSProperties, type ReactElement, type ReactNode } from 'react';\nimport s from './ToggleBar.module.css';\nimport { useRovingTabIndex } from '../../useRovingTabIndex';\nimport { SegmentTooltip, segmentTooltipContent, type SegmentTooltipFields } from '../segmentTooltip';\n\n/** One segment of a {@link ToggleBar}, identified by its `value`. */\nexport type ToggleBarItem<V extends string | number = string> = {\n value: V;\n label?: ReactNode;\n ariaLabel?: string;\n disabled?: boolean;\n} & SegmentTooltipFields;\n\n/** Segment height and type scale for a {@link ToggleBar}. */\nexport type ToggleBarSize = 'sm' | 'md';\n/** Visual treatment of a {@link ToggleBar}. */\nexport type ToggleBarVariant = 'default' | 'minimal' | 'flat';\n\ntype CommonProps = {\n ariaLabel?: string;\n className?: string;\n height?: number;\n /** Size variant. `sm` is ~60% of the default height with reduced padding\n * and font size — sized for dense surfaces like lab control panels. */\n size?: ToggleBarSize;\n /** Visual variant. `minimal` strips the pill track and glass treatment;\n * selection becomes a flat accent. For dense diagnostic surfaces. */\n variant?: ToggleBarVariant;\n};\n\n/**\n * Props for {@link ToggleBar}, discriminated on `mode`. Single mode is\n * controlled by one value or `null`; multiple mode by an array, and only it\n * accepts `mixedValues`.\n */\nexport type ToggleBarProps<V extends string | number = string> =\n | (CommonProps & {\n mode?: 'single';\n items: readonly ToggleBarItem<V>[];\n value: V | null;\n onChange: (next: V | null) => void;\n allowDeselect?: boolean;\n })\n | (CommonProps & {\n mode: 'multiple';\n items: readonly ToggleBarItem<V>[];\n value: readonly V[];\n /**\n * Values that are neither on nor off — the sources this bar\n * aggregates disagree (a text range that is bold in part of it, a\n * multi-selection whose nodes differ). Rendered `aria-pressed=\"mixed\"`,\n * the ARIA tri-state a toggle button actually has, rather than\n * `SelectionPanel`'s reduced-opacity-plus-`title` workaround for\n * `Switch`, which has no indeterminate state to render.\n *\n * `value` wins where the two lists overlap, so a caller that can't\n * cheaply keep them disjoint doesn't get an ambiguous segment.\n *\n * Clicking a mixed segment turns it fully **on**, matching the\n * everywhere-else convention (and `toggleFlagInRange`'s rule for a\n * partially-styled text range) that a mixed toggle resolves toward\n * the affirmative rather than clearing.\n */\n mixedValues?: readonly V[];\n onChange: (next: V[]) => void;\n });\n\n/**\n * Segmented control for choosing among a fixed set of values — one of them\n * (`mode: 'single'`, the default) or any number (`mode: 'multiple'`).\n *\n * Single mode ignores a click on the already-selected segment unless\n * `allowDeselect` is set. Arrow keys move focus without changing the value;\n * Space and Enter commit.\n */\nexport function ToggleBar<V extends string | number = string>(props: ToggleBarProps<V>): ReactElement {\n const { items, ariaLabel, className, height, size, variant } = props;\n const mode = props.mode ?? 'single';\n\n const isSelected = (value: V): boolean => {\n if (mode === 'multiple') return (props.value as readonly V[]).includes(value);\n return (props.value as V | null) === value;\n };\n\n /** Mixed only in multiple mode, and only where `value` doesn't already\n * claim the segment. Single mode has nothing to be mixed about — its\n * aggregate is one value or none. */\n const isMixed = (value: V): boolean => {\n if (mode !== 'multiple') return false;\n const mixed = (props as { mixedValues?: readonly V[] }).mixedValues;\n return mixed !== undefined && mixed.includes(value) && !isSelected(value);\n };\n\n const handleClick = (index: number) => () => {\n const item = items[index];\n if (item.disabled) return;\n if (mode === 'multiple') {\n const current = props.value as readonly V[];\n const next = current.includes(item.value)\n ? current.filter(v => v !== item.value)\n : [...current, item.value];\n (props.onChange as (n: V[]) => void)(next);\n } else {\n const current = props.value as V | null;\n if (current === item.value) {\n if ((props as { allowDeselect?: boolean }).allowDeselect) {\n (props.onChange as (n: V | null) => void)(null);\n }\n return;\n }\n (props.onChange as (n: V | null) => void)(item.value);\n }\n };\n\n const selectedIndex = mode === 'single'\n ? items.findIndex(it => it.value === (props.value as V | null))\n : -1;\n\n const roving = useRovingTabIndex({\n itemSelector: `.${s.segment}`,\n // Single mode is a radiogroup: the tab stop is the current value, and\n // arrow keys move the selection with the focus. Multiple mode is a set of\n // independent toggles — focus alone moves, and Space/Enter flips.\n tabStopIndex: selectedIndex >= 0 ? selectedIndex : undefined,\n onNavigate: mode === 'single'\n ? (index) => (props.onChange as (n: V | null) => void)(items[index].value)\n : undefined,\n onActivate: mode === 'multiple' ? (index) => handleClick(index)() : undefined,\n });\n\n const style: CSSProperties | undefined = height !== undefined\n ? ({ ['--tb-h' as string]: `${height}px` } as CSSProperties)\n : undefined;\n\n const rootCls = [\n s.root,\n size && s[`size_${size}`],\n variant && s[`variant_${variant}`],\n className,\n ].filter(Boolean).join(' ');\n\n return (\n <div\n ref={roving.rootRef}\n onKeyDown={roving.onKeyDown}\n className={rootCls}\n role={mode === 'multiple' ? 'group' : 'radiogroup'}\n aria-label={ariaLabel}\n style={style}\n >\n {items.map((item, i) => {\n const selected = isSelected(item.value);\n const mixed = isMixed(item.value);\n const cls = [s.segment, selected && s.segmentSelected, mixed && s.segmentMixed]\n .filter(Boolean)\n .join(' ');\n return (\n <SegmentTooltip key={item.value} content={segmentTooltipContent(item)} disabled={item.disabled}>\n <button\n type=\"button\"\n role={mode === 'multiple' ? undefined : 'radio'}\n aria-checked={mode === 'multiple' ? undefined : selected}\n aria-pressed={mode === 'multiple' ? (mixed ? 'mixed' : selected) : undefined}\n aria-label={item.ariaLabel}\n disabled={item.disabled}\n className={cls}\n onClick={handleClick(i)}\n >\n {item.label}\n </button>\n </SegmentTooltip>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AC2EA,SAAgBI,EAA8C,GAAwC;CACpG,IAAM,EAAE,UAAO,cAAW,cAAW,WAAQ,SAAM,eAAYC,GACzDC,IAAOD,EAAM,QAAQ,UAErBE,KAAc,MACdD,MAAS,aAAoBD,EAAM,MAAuB,SAASG,CAAK,IACpEH,EAAM,UAAuBG,GAMjCC,KAAW,MAAsB;EACrC,IAAIH,MAAS,YAAY,OAAO;EAChC,IAAMI,IAASL,EAAyC;EACxD,OAAOK,MAAU,KAAA,KAAaA,EAAM,SAASF,CAAK,KAAK,CAACD,EAAWC,CAAK;CAC1E,GAEMG,KAAe,YAAwB;EAC3C,IAAMC,IAAOC,EAAMC;EACnB,IAAI,GAAK,UACT;OAAIR,MAAS,YAAY;IACvB,IAAMS,IAAUV,EAAM,OAChBW,IAAOD,EAAQ,SAASH,EAAK,KAAK,IACpCG,EAAQ,QAAO,MAAKE,MAAML,EAAK,KAAK,IACpC,CAAC,GAAGG,GAASH,EAAK,KAAK;IAC3B,EAAO,SAA8BI,CAAI;GAC3C,OAAO;IAEL,IADgBX,EAAM,UACNO,EAAK,OAAO;KAC1B,AAAKP,EAAsC,iBACzCA,EAAO,SAAmC,IAAI;KAEhD;IACF;IACA,EAAO,SAAmCO,EAAK,KAAK;GACtD;;CACF,GAEMM,IAAgBZ,MAAS,WAC3BO,EAAM,WAAU,MAAMM,EAAG,UAAWd,EAAM,KAAkB,IAC5D,IAEEe,IAASlB,EAAkB;EAC/B,cAAc,IAAIF,EAAE;EAIpB,cAAckB,KAAiB,IAAIA,IAAgB,KAAA;EACnD,YAAYZ,MAAS,YAChB,MAAWD,EAAM,SAAmCQ,EAAMC,EAAM,CAAC,KAAK,IACvE,KAAA;EACJ,YAAYR,MAAS,cAAc,MAAUK,EAAYG,CAAK,CAAC,CAAC,IAAI,KAAA;CACtE,CAAC,GAEKO,IAAmCC,MAAW,KAAA,IAEhD,KAAA,IADC,EAAG,UAAqB,GAAGA,EAAO,IAAI,GAGrCC,IAAU;EACdvB,EAAE;EACFwB,KAAQxB,EAAE,QAAQwB;EAClBC,KAAWzB,EAAE,WAAWyB;EACxBC;CACF,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE1B,OACE,kBAAC,OAAD;EACE,KAAKN,EAAO;EACZ,WAAWA,EAAO;EAClB,WAAWG;EACX,MAAMjB,MAAS,aAAa,UAAU;EACtC,cAAYqB;EACL;EAEN,UAAAd,EAAM,KAAK,GAAM,MAAM;GACtB,IAAMe,IAAWrB,EAAWK,EAAK,KAAK,GAChCF,IAAQD,EAAQG,EAAK,KAAK,GAC1BiB,IAAM;IAAC7B,EAAE;IAAS4B,KAAY5B,EAAE;IAAiBU,KAASV,EAAE;GAAY,CAAC,CAC5E,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;GACX,OACE,kBAAC8B,GAAD;IAAiC,SAAS7B,EAAsBW,CAAI;IAAG,UAAUA,EAAK;IACpF,UAAA,kBAAC,UAAD;KACE,MAAK;KACL,MAAMN,MAAS,aAAa,KAAA,IAAY;KACxC,gBAAcA,MAAS,aAAa,KAAA,IAAYsB;KAChD,gBAActB,MAAS,aAAcI,IAAQ,UAAUkB,IAAY,KAAA;KACnE,cAAYhB,EAAK;KACjB,UAAUA,EAAK;KACf,WAAWiB;KACX,SAASlB,EAAYoB,CAAC;KAErB,UAAAnB,EAAK;IACA,CAAA;GACM,GAbKA,EAAK,KAaV;EAEpB,CAAC;CACE,CAAA;AAET"}