@fangxinyan/lumina 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,25 @@
1
1
  /* ============ Slider ============ */
2
2
  .slider {
3
3
  position: relative;
4
- height: 36px;
5
4
  width: 100%;
6
5
  display: flex;
7
6
  align-items: center;
7
+ gap: 10px;
8
8
  cursor: pointer;
9
+ min-height: 36px;
9
10
  }
10
11
  .slider.has-marks { padding-bottom: 26px; }
11
12
  .slider.disabled { opacity: 0.5; cursor: not-allowed; }
13
+
14
+ /* Rail: positioning context for track + thumbs + marks */
15
+ .slider-rail {
16
+ position: relative;
17
+ flex: 1 1 0%;
18
+ height: 36px;
19
+ display: flex;
20
+ align-items: center;
21
+ }
22
+
12
23
  .slider-track {
13
24
  position: relative;
14
25
  width: 100%;
@@ -16,8 +27,10 @@
16
27
  border-radius: 999px;
17
28
  background: var(--bg);
18
29
  box-shadow: var(--neu-in-sm);
19
- overflow: hidden;
30
+ touch-action: none;
31
+ z-index: 1;
20
32
  }
33
+
21
34
  .slider-fill {
22
35
  position: absolute;
23
36
  top: 0; bottom: 0;
@@ -25,7 +38,9 @@
25
38
  box-shadow: 0 0 12px var(--accent-glow);
26
39
  border-radius: 999px;
27
40
  transition: left var(--dur-fast) var(--ease), right var(--dur-fast) var(--ease);
41
+ pointer-events: none;
28
42
  }
43
+
29
44
  .slider-thumb {
30
45
  position: absolute;
31
46
  top: 50%;
@@ -36,43 +51,45 @@
36
51
  transform: translate(-50%, -50%);
37
52
  transition: all var(--dur-fast) var(--ease);
38
53
  display: grid; place-items: center;
39
- pointer-events: none;
54
+ z-index: 3;
55
+ outline: none;
56
+ touch-action: none;
40
57
  }
41
- .slider.has-marks .slider-thumb { top: calc(50% - 13px); }
42
- .slider.has-marks .slider-track { margin-top: -26px; }
43
58
  .slider-thumb::after {
44
59
  content: "";
45
60
  width: 6px; height: 6px;
46
61
  border-radius: 999px;
47
- background: var(--accent);
62
+ background: var(--slider-thumb-color, var(--accent));
63
+ }
64
+ .slider-thumb:focus-visible {
65
+ box-shadow: var(--neu-out-sm), 0 0 0 3px var(--accent-glow);
48
66
  }
49
67
  .slider:hover .slider-thumb { transform: translate(-50%, -50%) scale(1.08); }
50
- .slider input[type="range"] {
51
- position: absolute;
52
- inset: 0;
53
- width: 100%; height: 100%;
54
- opacity: 0;
55
- cursor: inherit;
56
- margin: 0;
57
- pointer-events: auto;
68
+
69
+ .slider-value {
70
+ font-family: var(--font-mono);
71
+ font-size: 12px;
72
+ color: var(--accent-ink);
73
+ flex-shrink: 0;
74
+ white-space: nowrap;
58
75
  }
59
- /* In range mode (two stacked inputs), split the hit zones by half so the user can drag either thumb */
60
- .slider input[type="range"]:nth-of-type(1):not(:only-of-type) { right: 50%; }
61
- .slider input[type="range"]:nth-of-type(2) { left: 50%; }
62
- .slider.has-marks input[type="range"] { height: calc(100% - 26px); }
63
- .slider-value { font-family: var(--font-mono); font-size: 12px; color: var(--accent-ink); margin-left: 10px; }
76
+
77
+ /* Tone variants */
78
+ .slider.success .slider-thumb { --slider-thumb-color: var(--success); }
64
79
  .slider.success .slider-fill { background: linear-gradient(90deg, color-mix(in oklch, var(--success) 70%, white), var(--success)); box-shadow: 0 0 10px color-mix(in oklch, var(--success) 35%, transparent); }
65
- .slider.success .slider-thumb::after { background: var(--success); }
80
+ .slider.success .slider-thumb:focus-visible { box-shadow: var(--neu-out-sm), 0 0 0 3px color-mix(in oklch, var(--success) 35%, transparent); }
81
+ .slider.warning .slider-thumb { --slider-thumb-color: var(--warning); }
66
82
  .slider.warning .slider-fill { background: linear-gradient(90deg, color-mix(in oklch, var(--warning) 70%, white), var(--warning)); box-shadow: 0 0 10px color-mix(in oklch, var(--warning) 35%, transparent); }
67
- .slider.warning .slider-thumb::after { background: var(--warning); }
83
+ .slider.warning .slider-thumb:focus-visible { box-shadow: var(--neu-out-sm), 0 0 0 3px color-mix(in oklch, var(--warning) 35%, transparent); }
84
+ .slider.danger .slider-thumb { --slider-thumb-color: var(--danger); }
68
85
  .slider.danger .slider-fill { background: linear-gradient(90deg, color-mix(in oklch, var(--danger) 70%, white), var(--danger)); box-shadow: 0 0 10px color-mix(in oklch, var(--danger) 35%, transparent); }
69
- .slider.danger .slider-thumb::after { background: var(--danger); }
86
+ .slider.danger .slider-thumb:focus-visible { box-shadow: var(--neu-out-sm), 0 0 0 3px color-mix(in oklch, var(--danger) 35%, transparent); }
70
87
 
71
88
  /* Marks */
72
89
  .slider-marks {
73
90
  position: absolute;
74
91
  left: 0; right: 0;
75
- bottom: 0;
92
+ bottom: -26px;
76
93
  height: 22px;
77
94
  pointer-events: none;
78
95
  }
@@ -94,8 +111,10 @@
94
111
  border-radius: 999px;
95
112
  background: var(--bg);
96
113
  box-shadow: var(--neu-in-sm);
97
- margin-top: -18px;
98
- position: relative;
114
+ position: absolute;
115
+ top: -12px;
116
+ left: 50%;
117
+ transform: translateX(-50%);
99
118
  z-index: 1;
100
119
  }
101
120
  .slider-mark.active .slider-mark-dot { background: var(--accent); box-shadow: 0 0 6px var(--accent-glow); }
package/dist/Slider.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunkB5JH7NRZ_cjs = require('./chunk-B5JH7NRZ.cjs');
3
+ var chunkN6ZLZUBF_cjs = require('./chunk-N6ZLZUBF.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "Slider", {
8
8
  enumerable: true,
9
- get: function () { return chunkB5JH7NRZ_cjs.Slider; }
9
+ get: function () { return chunkN6ZLZUBF_cjs.Slider; }
10
10
  });
11
11
  //# sourceMappingURL=Slider.cjs.map
12
12
  //# sourceMappingURL=Slider.cjs.map
package/dist/Slider.d.cts CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
- type SliderSingleValue = number;
4
3
  type SliderRangeValue = [number, number];
5
- type SliderValue = SliderSingleValue | SliderRangeValue;
4
+ type SliderValue = number | SliderRangeValue;
6
5
  interface SliderBaseProps {
7
6
  min?: number;
8
7
  max?: number;
@@ -10,6 +9,8 @@ interface SliderBaseProps {
10
9
  disabled?: boolean;
11
10
  showValue?: boolean;
12
11
  tone?: "accent" | "success" | "warning" | "danger";
12
+ /** Custom gradient colors mapped from min→max. Overrides `tone` for fill & thumb. */
13
+ colors?: string[];
13
14
  /** Tick marks with labels. Clicking a mark snaps to it. */
14
15
  marks?: Record<number, React.ReactNode>;
15
16
  className?: string;
@@ -27,7 +28,9 @@ interface SliderRangeProps extends SliderBaseProps {
27
28
  onChange?: (value: SliderRangeValue) => void;
28
29
  }
29
30
  type SliderProps = SliderSingleProps | SliderRangeProps;
30
- /** `Slider` — neumorphic range input with grooved track. */
31
- declare const Slider: React.FC<SliderProps>;
31
+ /** `Slider` — neumorphic range input with grooved track.
32
+ * @example <Slider defaultValue={40} showValue />
33
+ */
34
+ declare const Slider: React.ForwardRefExoticComponent<SliderProps & React.RefAttributes<HTMLDivElement>>;
32
35
 
33
- export { Slider, type SliderProps, type SliderRangeProps, type SliderRangeValue, type SliderSingleProps, type SliderSingleValue, type SliderValue };
36
+ export { Slider, type SliderProps, type SliderRangeProps, type SliderRangeValue, type SliderSingleProps, type SliderValue };
package/dist/Slider.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
- type SliderSingleValue = number;
4
3
  type SliderRangeValue = [number, number];
5
- type SliderValue = SliderSingleValue | SliderRangeValue;
4
+ type SliderValue = number | SliderRangeValue;
6
5
  interface SliderBaseProps {
7
6
  min?: number;
8
7
  max?: number;
@@ -10,6 +9,8 @@ interface SliderBaseProps {
10
9
  disabled?: boolean;
11
10
  showValue?: boolean;
12
11
  tone?: "accent" | "success" | "warning" | "danger";
12
+ /** Custom gradient colors mapped from min→max. Overrides `tone` for fill & thumb. */
13
+ colors?: string[];
13
14
  /** Tick marks with labels. Clicking a mark snaps to it. */
14
15
  marks?: Record<number, React.ReactNode>;
15
16
  className?: string;
@@ -27,7 +28,9 @@ interface SliderRangeProps extends SliderBaseProps {
27
28
  onChange?: (value: SliderRangeValue) => void;
28
29
  }
29
30
  type SliderProps = SliderSingleProps | SliderRangeProps;
30
- /** `Slider` — neumorphic range input with grooved track. */
31
- declare const Slider: React.FC<SliderProps>;
31
+ /** `Slider` — neumorphic range input with grooved track.
32
+ * @example <Slider defaultValue={40} showValue />
33
+ */
34
+ declare const Slider: React.ForwardRefExoticComponent<SliderProps & React.RefAttributes<HTMLDivElement>>;
32
35
 
33
- export { Slider, type SliderProps, type SliderRangeProps, type SliderRangeValue, type SliderSingleProps, type SliderSingleValue, type SliderValue };
36
+ export { Slider, type SliderProps, type SliderRangeProps, type SliderRangeValue, type SliderSingleProps, type SliderValue };
package/dist/Slider.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Slider } from './chunk-QJGKI2BD.js';
1
+ export { Slider } from './chunk-2TZ2R7L2.js';
2
2
  //# sourceMappingURL=Slider.js.map
3
3
  //# sourceMappingURL=Slider.js.map
@@ -0,0 +1,235 @@
1
+ import './tokens-BIIUTPKN.css';
2
+ import './shared-NJHRNLRH.css';
3
+ import './Slider-3JKA5EW6.css';
4
+ import * as React from 'react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ // src/components/Slider/Slider.tsx
8
+ function clamp(v, lo, hi) {
9
+ return Math.max(lo, Math.min(hi, v));
10
+ }
11
+ function snap(raw, step, min) {
12
+ return Math.round((raw - min) / step) * step + min;
13
+ }
14
+ function lerpColor(colors, t) {
15
+ const pos = Math.max(0, Math.min(1, t)) * (colors.length - 1);
16
+ const i = Math.min(Math.floor(pos), colors.length - 2);
17
+ const frac = pos - i;
18
+ if (frac === 0) return colors[i];
19
+ return `color-mix(in oklch, ${colors[i + 1]} ${Math.round(frac * 100)}%, ${colors[i]})`;
20
+ }
21
+ var Slider = React.forwardRef(
22
+ (props, ref) => {
23
+ const {
24
+ min = 0,
25
+ max = 100,
26
+ step = 1,
27
+ disabled,
28
+ showValue,
29
+ tone = "accent",
30
+ colors,
31
+ marks,
32
+ className = ""
33
+ } = props;
34
+ const range = props.range === true;
35
+ const controlled = props.value;
36
+ const defaultVal = props.defaultValue;
37
+ const onChange = props.onChange;
38
+ const initRef = React.useRef(
39
+ defaultVal !== void 0 ? Array.isArray(defaultVal) ? [...defaultVal] : [defaultVal] : range ? [min, max] : [min]
40
+ );
41
+ const [inner, setInner] = React.useState(initRef.current);
42
+ const isControlled = controlled !== void 0;
43
+ const vals = isControlled ? Array.isArray(controlled) ? controlled : [controlled] : inner;
44
+ const valsRef = React.useRef(vals);
45
+ valsRef.current = vals;
46
+ const trackRef = React.useRef(null);
47
+ const thumbEls = React.useRef([]);
48
+ const dragging = React.useRef(null);
49
+ const pct = (n) => (n - min) / (max - min) * 100;
50
+ const commit = React.useCallback(
51
+ (next) => {
52
+ if (!isControlled) setInner(next);
53
+ if (range) {
54
+ onChange?.([next[0], next[1]]);
55
+ } else {
56
+ onChange?.(next[0]);
57
+ }
58
+ },
59
+ [isControlled, range, onChange]
60
+ );
61
+ const setAt = React.useCallback(
62
+ (index, raw) => {
63
+ const snapped = snap(clamp(raw, min, max), step, min);
64
+ const clamped = clamp(snapped, min, max);
65
+ const out = [...valsRef.current];
66
+ out[index] = clamped;
67
+ if (range && out.length === 2 && out[0] > out[1]) out.sort((a, b) => a - b);
68
+ commit(out);
69
+ },
70
+ [min, max, step, range, commit]
71
+ );
72
+ const valFromX = React.useCallback(
73
+ (clientX) => {
74
+ const rect = trackRef.current?.getBoundingClientRect();
75
+ if (!rect) return min;
76
+ const ratio = clamp((clientX - rect.left) / rect.width, 0, 1);
77
+ return min + ratio * (max - min);
78
+ },
79
+ [min, max]
80
+ );
81
+ const closerIndex = React.useCallback(
82
+ (target) => {
83
+ const v = valsRef.current;
84
+ if (!range || v.length < 2) return 0;
85
+ const d0 = Math.abs(v[0] - target);
86
+ const d1 = Math.abs(v[1] - target);
87
+ if (d0 === d1) return target < v[0] ? 0 : 1;
88
+ return d0 < d1 ? 0 : 1;
89
+ },
90
+ [range]
91
+ );
92
+ const onPointerDown = React.useCallback(
93
+ (e) => {
94
+ if (disabled) return;
95
+ e.preventDefault();
96
+ const val = valFromX(e.clientX);
97
+ const idx = closerIndex(val);
98
+ dragging.current = idx;
99
+ setAt(idx, val);
100
+ trackRef.current?.setPointerCapture(e.pointerId);
101
+ thumbEls.current[idx]?.focus();
102
+ },
103
+ [disabled, valFromX, closerIndex, setAt]
104
+ );
105
+ const onPointerMove = React.useCallback(
106
+ (e) => {
107
+ if (dragging.current === null) return;
108
+ setAt(dragging.current, valFromX(e.clientX));
109
+ },
110
+ [valFromX, setAt]
111
+ );
112
+ const onPointerUp = React.useCallback(() => {
113
+ dragging.current = null;
114
+ }, []);
115
+ const handleKey = React.useCallback(
116
+ (index, e) => {
117
+ if (disabled) return;
118
+ let delta = 0;
119
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") delta = step;
120
+ else if (e.key === "ArrowLeft" || e.key === "ArrowDown") delta = -step;
121
+ else if (e.key === "Home") {
122
+ setAt(index, min);
123
+ return;
124
+ } else if (e.key === "End") {
125
+ setAt(index, max);
126
+ return;
127
+ } else return;
128
+ e.preventDefault();
129
+ setAt(index, valsRef.current[index] + delta);
130
+ },
131
+ [disabled, step, min, max, setAt]
132
+ );
133
+ const handleMarkClick = React.useCallback(
134
+ (mark) => {
135
+ if (disabled) return;
136
+ setAt(closerIndex(mark), mark);
137
+ },
138
+ [disabled, closerIndex, setAt]
139
+ );
140
+ const fillLeft = range ? pct(Math.min(vals[0], vals[1])) : 0;
141
+ const fillRight = range ? 100 - pct(Math.max(vals[0], vals[1])) : 100 - pct(vals[0]);
142
+ const hasGradient = colors && colors.length >= 2;
143
+ const fillStyle = {
144
+ left: `${fillLeft}%`,
145
+ right: `${fillRight}%`
146
+ };
147
+ if (hasGradient) {
148
+ const w = 100 - fillLeft - fillRight;
149
+ if (w > 0) {
150
+ const stops = colors.map((c, i) => {
151
+ const orig = i / (colors.length - 1) * 100;
152
+ return `${c} ${((orig - fillLeft) / w * 100).toFixed(2)}%`;
153
+ }).join(", ");
154
+ fillStyle.background = `linear-gradient(to right, ${stops})`;
155
+ fillStyle.boxShadow = "none";
156
+ }
157
+ }
158
+ return /* @__PURE__ */ jsxs(
159
+ "div",
160
+ {
161
+ ref,
162
+ className: `slider ${range ? "range" : ""} ${hasGradient ? "gradient" : tone} ${disabled ? "disabled" : ""} ${marks ? "has-marks" : ""} ${className}`,
163
+ children: [
164
+ /* @__PURE__ */ jsxs("div", { className: "slider-rail", children: [
165
+ /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ className: "slider-track",
169
+ ref: trackRef,
170
+ onPointerDown,
171
+ onPointerMove,
172
+ onPointerUp,
173
+ children: /* @__PURE__ */ jsx("div", { className: "slider-fill", style: fillStyle })
174
+ }
175
+ ),
176
+ vals.map((n, i) => {
177
+ const thumbStyle = { left: `${pct(n)}%` };
178
+ if (hasGradient) {
179
+ thumbStyle["--slider-thumb-color"] = lerpColor(colors, (n - min) / (max - min));
180
+ }
181
+ return /* @__PURE__ */ jsx(
182
+ "div",
183
+ {
184
+ ref: (el) => {
185
+ thumbEls.current[i] = el;
186
+ },
187
+ className: "slider-thumb",
188
+ style: thumbStyle,
189
+ role: "slider",
190
+ tabIndex: disabled ? -1 : 0,
191
+ "aria-valuemin": min,
192
+ "aria-valuemax": max,
193
+ "aria-valuenow": n,
194
+ "aria-disabled": disabled || void 0,
195
+ onKeyDown: (e) => handleKey(i, e),
196
+ onPointerDown: (e) => {
197
+ if (disabled) return;
198
+ e.preventDefault();
199
+ e.stopPropagation();
200
+ dragging.current = i;
201
+ trackRef.current?.setPointerCapture(e.pointerId);
202
+ e.currentTarget.focus();
203
+ }
204
+ },
205
+ i
206
+ );
207
+ }),
208
+ marks && /* @__PURE__ */ jsx("div", { className: "slider-marks", children: Object.entries(marks).map(([k, label]) => {
209
+ const n = Number(k);
210
+ const active = range ? n >= Math.min(vals[0], vals[1]) && n <= Math.max(vals[0], vals[1]) : n <= vals[0];
211
+ return /* @__PURE__ */ jsxs(
212
+ "span",
213
+ {
214
+ className: `slider-mark ${active ? "active" : ""}`,
215
+ style: { left: `${pct(n)}%` },
216
+ onClick: () => handleMarkClick(n),
217
+ children: [
218
+ /* @__PURE__ */ jsx("span", { className: "slider-mark-dot" }),
219
+ /* @__PURE__ */ jsx("span", { className: "slider-mark-label", children: label })
220
+ ]
221
+ },
222
+ k
223
+ );
224
+ }) })
225
+ ] }),
226
+ showValue && /* @__PURE__ */ jsx("span", { className: "slider-value", children: range ? `${vals[0]} \u2013 ${vals[1]}` : vals[0] })
227
+ ]
228
+ }
229
+ );
230
+ }
231
+ );
232
+
233
+ export { Slider };
234
+ //# sourceMappingURL=chunk-2TZ2R7L2.js.map
235
+ //# sourceMappingURL=chunk-2TZ2R7L2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Slider/Slider.tsx"],"names":[],"mappings":"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","file":"chunk-2TZ2R7L2.js","sourcesContent":["import \"../../styles/tokens.css\";\nimport \"../../styles/shared.css\";\nimport \"./Slider.css\";\nimport * as React from \"react\";\n\nexport type SliderRangeValue = [number, number];\nexport type SliderValue = number | SliderRangeValue;\n\ninterface SliderBaseProps {\n min?: number;\n max?: number;\n step?: number;\n disabled?: boolean;\n showValue?: boolean;\n tone?: \"accent\" | \"success\" | \"warning\" | \"danger\";\n /** Custom gradient colors mapped from min→max. Overrides `tone` for fill & thumb. */\n colors?: string[];\n /** Tick marks with labels. Clicking a mark snaps to it. */\n marks?: Record<number, React.ReactNode>;\n className?: string;\n}\n\nexport interface SliderSingleProps extends SliderBaseProps {\n range?: false;\n value?: number;\n defaultValue?: number;\n onChange?: (value: number) => void;\n}\n\nexport interface SliderRangeProps extends SliderBaseProps {\n range: true;\n value?: SliderRangeValue;\n defaultValue?: SliderRangeValue;\n onChange?: (value: SliderRangeValue) => void;\n}\n\nexport type SliderProps = SliderSingleProps | SliderRangeProps;\n\nfunction clamp(v: number, lo: number, hi: number) {\n return Math.max(lo, Math.min(hi, v));\n}\n\nfunction snap(raw: number, step: number, min: number) {\n return Math.round((raw - min) / step) * step + min;\n}\n\nfunction lerpColor(colors: string[], t: number): string {\n const pos = Math.max(0, Math.min(1, t)) * (colors.length - 1);\n const i = Math.min(Math.floor(pos), colors.length - 2);\n const frac = pos - i;\n if (frac === 0) return colors[i];\n return `color-mix(in oklch, ${colors[i + 1]} ${Math.round(frac * 100)}%, ${colors[i]})`;\n}\n\n/** `Slider` — neumorphic range input with grooved track.\n * @example <Slider defaultValue={40} showValue />\n */\nexport const Slider = React.forwardRef<HTMLDivElement, SliderProps>(\n (props, ref) => {\n const {\n min = 0,\n max = 100,\n step = 1,\n disabled,\n showValue,\n tone = \"accent\",\n colors,\n marks,\n className = \"\",\n } = props;\n\n const range = (props as SliderRangeProps).range === true;\n const controlled = (props as { value?: SliderValue }).value;\n const defaultVal = (props as { defaultValue?: SliderValue }).defaultValue;\n const onChange = (props as { onChange?: (v: any) => void }).onChange;\n\n const initRef = React.useRef<number[]>(\n defaultVal !== undefined\n ? Array.isArray(defaultVal)\n ? [...defaultVal]\n : [defaultVal]\n : range\n ? [min, max]\n : [min]\n );\n const [inner, setInner] = React.useState<number[]>(initRef.current);\n\n const isControlled = controlled !== undefined;\n const vals = isControlled\n ? Array.isArray(controlled)\n ? controlled\n : [controlled]\n : inner;\n\n const valsRef = React.useRef(vals);\n valsRef.current = vals;\n\n const trackRef = React.useRef<HTMLDivElement>(null);\n const thumbEls = React.useRef<(HTMLDivElement | null)[]>([]);\n const dragging = React.useRef<number | null>(null);\n\n const pct = (n: number) => ((n - min) / (max - min)) * 100;\n\n const commit = React.useCallback(\n (next: number[]) => {\n if (!isControlled) setInner(next);\n if (range) {\n onChange?.([next[0], next[1]] as SliderRangeValue);\n } else {\n onChange?.(next[0]);\n }\n },\n [isControlled, range, onChange]\n );\n\n const setAt = React.useCallback(\n (index: number, raw: number) => {\n const snapped = snap(clamp(raw, min, max), step, min);\n const clamped = clamp(snapped, min, max);\n const out = [...valsRef.current];\n out[index] = clamped;\n if (range && out.length === 2 && out[0] > out[1]) out.sort((a, b) => a - b);\n commit(out);\n },\n [min, max, step, range, commit]\n );\n\n const valFromX = React.useCallback(\n (clientX: number) => {\n const rect = trackRef.current?.getBoundingClientRect();\n if (!rect) return min;\n const ratio = clamp((clientX - rect.left) / rect.width, 0, 1);\n return min + ratio * (max - min);\n },\n [min, max]\n );\n\n const closerIndex = React.useCallback(\n (target: number) => {\n const v = valsRef.current;\n if (!range || v.length < 2) return 0;\n const d0 = Math.abs(v[0] - target);\n const d1 = Math.abs(v[1] - target);\n if (d0 === d1) return target < v[0] ? 0 : 1;\n return d0 < d1 ? 0 : 1;\n },\n [range]\n );\n\n const onPointerDown = React.useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n if (disabled) return;\n e.preventDefault();\n const val = valFromX(e.clientX);\n const idx = closerIndex(val);\n dragging.current = idx;\n setAt(idx, val);\n trackRef.current?.setPointerCapture(e.pointerId);\n thumbEls.current[idx]?.focus();\n },\n [disabled, valFromX, closerIndex, setAt]\n );\n\n const onPointerMove = React.useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n if (dragging.current === null) return;\n setAt(dragging.current, valFromX(e.clientX));\n },\n [valFromX, setAt]\n );\n\n const onPointerUp = React.useCallback(() => {\n dragging.current = null;\n }, []);\n\n const handleKey = React.useCallback(\n (index: number, e: React.KeyboardEvent) => {\n if (disabled) return;\n let delta = 0;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") delta = step;\n else if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") delta = -step;\n else if (e.key === \"Home\") { setAt(index, min); return; }\n else if (e.key === \"End\") { setAt(index, max); return; }\n else return;\n e.preventDefault();\n setAt(index, valsRef.current[index] + delta);\n },\n [disabled, step, min, max, setAt]\n );\n\n const handleMarkClick = React.useCallback(\n (mark: number) => {\n if (disabled) return;\n setAt(closerIndex(mark), mark);\n },\n [disabled, closerIndex, setAt]\n );\n\n const fillLeft = range ? pct(Math.min(vals[0], vals[1])) : 0;\n const fillRight = range\n ? 100 - pct(Math.max(vals[0], vals[1]))\n : 100 - pct(vals[0]);\n\n const hasGradient = colors && colors.length >= 2;\n\n const fillStyle: React.CSSProperties = {\n left: `${fillLeft}%`,\n right: `${fillRight}%`,\n };\n if (hasGradient) {\n const w = 100 - fillLeft - fillRight;\n if (w > 0) {\n const stops = colors.map((c, i) => {\n const orig = (i / (colors.length - 1)) * 100;\n return `${c} ${((orig - fillLeft) / w * 100).toFixed(2)}%`;\n }).join(\", \");\n fillStyle.background = `linear-gradient(to right, ${stops})`;\n fillStyle.boxShadow = \"none\";\n }\n }\n\n return (\n <div\n ref={ref}\n className={`slider ${range ? \"range\" : \"\"} ${hasGradient ? \"gradient\" : tone} ${disabled ? \"disabled\" : \"\"} ${marks ? \"has-marks\" : \"\"} ${className}`}\n >\n <div className=\"slider-rail\">\n <div\n className=\"slider-track\"\n ref={trackRef}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n >\n <div className=\"slider-fill\" style={fillStyle} />\n </div>\n\n {vals.map((n, i) => {\n const thumbStyle: React.CSSProperties = { left: `${pct(n)}%` };\n if (hasGradient) {\n (thumbStyle as any)[\"--slider-thumb-color\"] = lerpColor(colors, (n - min) / (max - min));\n }\n return (\n <div\n key={i}\n ref={(el) => { thumbEls.current[i] = el; }}\n className=\"slider-thumb\"\n style={thumbStyle}\n role=\"slider\"\n tabIndex={disabled ? -1 : 0}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={n}\n aria-disabled={disabled || undefined}\n onKeyDown={(e) => handleKey(i, e)}\n onPointerDown={(e) => {\n if (disabled) return;\n e.preventDefault();\n e.stopPropagation();\n dragging.current = i;\n trackRef.current?.setPointerCapture(e.pointerId);\n e.currentTarget.focus();\n }}\n />\n );\n })}\n\n {marks && (\n <div className=\"slider-marks\">\n {Object.entries(marks).map(([k, label]) => {\n const n = Number(k);\n const active = range\n ? n >= Math.min(vals[0], vals[1]) && n <= Math.max(vals[0], vals[1])\n : n <= vals[0];\n return (\n <span\n key={k}\n className={`slider-mark ${active ? \"active\" : \"\"}`}\n style={{ left: `${pct(n)}%` }}\n onClick={() => handleMarkClick(n)}\n >\n <span className=\"slider-mark-dot\" />\n <span className=\"slider-mark-label\">{label}</span>\n </span>\n );\n })}\n </div>\n )}\n </div>\n\n {showValue && (\n <span className=\"slider-value\">\n {range ? `${vals[0]} – ${vals[1]}` : vals[0]}\n </span>\n )}\n </div>\n );\n }\n);\n"]}