@choice-ui/react 1.8.6 → 1.8.8

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 (40) hide show
  1. package/dist/components/button/dist/index.js +7 -0
  2. package/dist/components/colors/src/color-image-paint/color-image-paint.js +3 -3
  3. package/dist/components/dropdown/dist/index.d.ts +6 -0
  4. package/dist/components/dropdown/dist/index.js +12 -8
  5. package/dist/components/dropdown/src/dropdown.d.ts +6 -0
  6. package/dist/components/dropdown/src/dropdown.js +12 -8
  7. package/dist/components/emoji-picker/dist/index.d.ts +29 -1
  8. package/dist/components/emoji-picker/dist/index.js +144 -42
  9. package/dist/components/form/src/adapters/range-adapter.js +2 -2
  10. package/dist/components/icon-button/dist/index.d.ts +1 -1
  11. package/dist/components/icon-button/dist/index.js +39 -0
  12. package/dist/components/menus/dist/index.d.ts +5 -0
  13. package/dist/components/menus/dist/index.js +18 -1
  14. package/dist/components/menus/src/components/menu-scroll-arrow.js +4 -0
  15. package/dist/components/range/dist/index.d.ts +276 -20
  16. package/dist/components/range/dist/index.js +1030 -602
  17. package/dist/components/range/src/components/connects.d.ts +26 -0
  18. package/dist/components/range/src/components/connects.js +192 -0
  19. package/dist/components/range/src/components/dot.d.ts +8 -0
  20. package/dist/components/range/src/components/dot.js +148 -0
  21. package/dist/components/range/src/components/thumb.d.ts +14 -0
  22. package/dist/components/range/src/components/thumb.js +159 -0
  23. package/dist/components/range/src/context/index.d.ts +4 -0
  24. package/dist/components/range/src/context/range-context.d.ts +35 -0
  25. package/dist/components/range/src/context/range-context.js +13 -0
  26. package/dist/components/range/src/context/range-tuple-context.d.ts +42 -0
  27. package/dist/components/range/src/context/range-tuple-context.js +15 -0
  28. package/dist/components/range/src/index.d.ts +4 -2
  29. package/dist/components/range/src/range-tuple.d.ts +17 -9
  30. package/dist/components/range/src/range-tuple.js +375 -441
  31. package/dist/components/range/src/range.d.ts +17 -9
  32. package/dist/components/range/src/range.js +164 -154
  33. package/dist/components/range/src/tv.d.ts +15 -3
  34. package/dist/components/range/src/tv.js +10 -7
  35. package/dist/components/textarea/dist/index.js +3 -1
  36. package/dist/components/tooltip/dist/index.d.ts +2 -0
  37. package/dist/components/tooltip/dist/index.js +23 -5
  38. package/dist/components/virtual-select/dist/index.d.ts +48 -0
  39. package/dist/index.js +6 -0
  40. package/package.json +20 -32
@@ -1,8 +1,12 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { clamp } from "es-toolkit";
3
- import { forwardRef, useState, useCallback, useMemo, useRef, useEffect } from "react";
3
+ import { forwardRef, useMemo, Children, isValidElement, useState, useCallback, useRef, useEffect } from "react";
4
4
  import { useEventCallback } from "usehooks-ts";
5
+ import { RangeTupleConnects, RangeTupleContainer } from "./components/connects.js";
6
+ import { RangeTupleDot } from "./components/dot.js";
7
+ import { RangeTupleThumb } from "./components/thumb.js";
5
8
  import { rangeTv } from "./tv.js";
9
+ import { RangeTupleContext } from "./context/range-tuple-context.js";
6
10
  import { useIsomorphicLayoutEffect } from "../../../shared/hooks/use-isomorphic-layout-effect/use-isomorphic-layout-effect.js";
7
11
  import { tcx } from "../../../shared/utils/tcx/tcx.js";
8
12
  import { mergeRefs } from "../../../shared/utils/merge-refs/merge-refs.js";
@@ -16,281 +20,174 @@ function normalizeTuple(value, min, max) {
16
20
  }
17
21
  return [clamp(value, min, max), max];
18
22
  }
19
- const RangeTuple = forwardRef(
20
- function RangeTuple2(props, ref) {
21
- const {
22
- defaultValue,
23
- value,
24
- onChange,
25
- onChangeStart,
26
- onChangeEnd,
27
- min = 0,
28
- max = 100,
29
- step = 1,
30
- disabled = false,
31
- readOnly = false,
32
- className,
33
- connectsClassName = {
34
- positive: "bg-accent-background",
35
- negative: "bg-accent-background"
36
- },
37
- trackSize = {
38
- width: 256,
39
- height: 16
40
- },
41
- thumbSize = 14
42
- } = props;
43
- const [actualTrackWidth, setActualTrackWidth] = useState();
44
- const valueToPosition = useCallback((val) => (val - min) / (max - min), [min, max]);
45
- const positionToValue = useCallback(
46
- (position) => min + position * (max - min),
47
- [min, max]
48
- );
49
- const normalizedDefaultValue = useMemo(
50
- () => defaultValue ? normalizeTuple(defaultValue, min, max) : void 0,
51
- [defaultValue, min, max]
52
- );
53
- const defaultStepValue = useMemo(() => {
54
- if (!normalizedDefaultValue) return null;
55
- if (step > 1) {
56
- return normalizedDefaultValue.map((v) => Math.round((v - min) / step) * step + min);
57
- }
58
- return normalizedDefaultValue;
59
- }, [normalizedDefaultValue, step, min]);
60
- const sliderRef = useRef(null);
61
- const thumb0Ref = useRef(null);
62
- const thumb1Ref = useRef(null);
63
- const input0Ref = useRef(null);
64
- const input1Ref = useRef(null);
65
- const isDragging = useRef(null);
66
- const [internalValue, setInternalValue] = useState(
67
- normalizeTuple(value, min, max)
68
- );
69
- const currentValue = useMemo(
70
- () => value ? normalizeTuple(value, min, max) : internalValue,
71
- [value, min, max, internalValue]
72
- );
73
- const currentStepValue = useMemo(() => {
74
- if (step > 1) {
75
- return currentValue.map((v) => Math.round(v / step) * step);
76
- }
77
- return currentValue;
78
- }, [currentValue, step]);
79
- const [transforms, setTransforms] = useState({
80
- minTransform: 1,
81
- maxTransform: 0,
82
- transformX0: 0,
83
- transformX1: 0
84
- });
85
- const trackWidth = useMemo(() => {
86
- if ((trackSize == null ? void 0 : trackSize.width) === "auto") {
87
- return actualTrackWidth;
88
- }
89
- return trackSize == null ? void 0 : trackSize.width;
90
- }, [trackSize == null ? void 0 : trackSize.width, actualTrackWidth]);
91
- useIsomorphicLayoutEffect(() => {
92
- if ((trackSize == null ? void 0 : trackSize.width) === "auto" && sliderRef.current) {
93
- const updateWidth = () => {
94
- if (sliderRef.current) {
95
- const width = sliderRef.current.getBoundingClientRect().width;
96
- if (width > 0) {
97
- setActualTrackWidth(width);
98
- }
23
+ const RangeTupleRoot = forwardRef(function RangeTuple2(props, ref) {
24
+ const {
25
+ children,
26
+ defaultValue,
27
+ value,
28
+ onChange,
29
+ onChangeStart,
30
+ onChangeEnd,
31
+ min = 0,
32
+ max = 100,
33
+ step = 1,
34
+ disabled = false,
35
+ readOnly = false,
36
+ className,
37
+ width: propsWidth = 256,
38
+ thumbSize: propsThumbSize = 14
39
+ } = props;
40
+ const {
41
+ hasCustomChildren,
42
+ hasCustomDot,
43
+ hasCustomConnects,
44
+ extractedThumbSize,
45
+ extractedTrackHeight
46
+ } = useMemo(() => {
47
+ const childArray = Children.toArray(children);
48
+ let hasCustom = false;
49
+ let hasDot = false;
50
+ let hasConnects = false;
51
+ let thumbSizeFromChild;
52
+ let trackHeightFromChild;
53
+ for (const child of childArray) {
54
+ if (isValidElement(child)) {
55
+ const type = child.type;
56
+ if (child.type === RangeTupleThumb || (type == null ? void 0 : type.displayName) === "RangeTupleThumb") {
57
+ hasCustom = true;
58
+ const childProps = child.props;
59
+ if (childProps.size !== void 0) {
60
+ thumbSizeFromChild = childProps.size;
99
61
  }
100
- };
101
- updateWidth();
102
- const resizeObserver = new ResizeObserver(() => {
103
- updateWidth();
104
- });
105
- resizeObserver.observe(sliderRef.current);
106
- return () => {
107
- resizeObserver.disconnect();
108
- };
109
- }
110
- }, [trackSize == null ? void 0 : trackSize.width]);
111
- useEffect(() => {
112
- const position0 = valueToPosition(currentValue[0]);
113
- const position1 = valueToPosition(currentValue[1]);
114
- const minTransform = 1;
115
- const maxTransform = (trackWidth ?? 0) - thumbSize - 1;
116
- const transformX0 = minTransform + position0 * (maxTransform - minTransform);
117
- const transformX1 = minTransform + position1 * (maxTransform - minTransform);
118
- setTransforms({
119
- minTransform,
120
- maxTransform,
121
- transformX0,
122
- transformX1
123
- });
124
- }, [currentValue, trackWidth, thumbSize, valueToPosition]);
125
- const dotsData = useMemo(() => {
126
- if (!step || step <= 1) return null;
127
- return Array.from({ length: Math.ceil((max - min) / step) + 1 }, (_, i) => {
128
- const dotValue = min + i * step;
129
- const dotPosition = valueToPosition(dotValue);
130
- return {
131
- value: dotValue,
132
- position: dotPosition
133
- };
134
- });
135
- }, [step, min, max, valueToPosition]);
136
- const defaultDotPositions = useMemo(() => {
137
- if (!normalizedDefaultValue || step > 1) return null;
138
- return normalizedDefaultValue.map((v) => valueToPosition(v));
139
- }, [normalizedDefaultValue, step, valueToPosition]);
140
- const updatePosition = useEventCallback(
141
- (clientX, thumbIndex, isEnd) => {
142
- var _a;
143
- if (readOnly) return;
144
- const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
145
- if (!rect) return;
146
- const newPosition = clamp((clientX - rect.left) / rect.width, 0, 1);
147
- const newValue = Math.round(positionToValue(newPosition) / step) * step;
148
- let clampedValue = clamp(newValue, min, max);
149
- if (normalizedDefaultValue && step === 1) {
150
- const snapThreshold = (max - min) * 0.05;
151
- for (const defVal of normalizedDefaultValue) {
152
- const distanceToDefault = Math.abs(clampedValue - defVal);
153
- if (distanceToDefault <= snapThreshold) {
154
- clampedValue = defVal;
155
- break;
156
- }
157
- }
158
- }
159
- const newTuple = [...currentValue];
160
- newTuple[thumbIndex] = clampedValue;
161
- if (newTuple[0] > newTuple[1]) {
162
- if (thumbIndex === 0) {
163
- newTuple[0] = newTuple[1];
164
- } else {
165
- newTuple[1] = newTuple[0];
62
+ } else if (child.type === RangeTupleContainer || (type == null ? void 0 : type.displayName) === "RangeTupleContainer") {
63
+ hasCustom = true;
64
+ const childProps = child.props;
65
+ if (childProps.height !== void 0) {
66
+ trackHeightFromChild = childProps.height;
166
67
  }
68
+ } else if (child.type === RangeTupleConnects || (type == null ? void 0 : type.displayName) === "RangeTupleConnects") {
69
+ hasCustom = true;
70
+ hasConnects = true;
71
+ } else if (child.type === RangeTupleDot || (type == null ? void 0 : type.displayName) === "RangeTupleDot") {
72
+ hasCustom = true;
73
+ hasDot = true;
167
74
  }
168
- if (isEnd) {
169
- isDragging.current = null;
170
- }
171
- if (value === void 0) {
172
- setInternalValue(newTuple);
173
- }
174
- onChange == null ? void 0 : onChange(newTuple);
175
- }
176
- );
177
- useEffect(() => {
178
- if (value !== void 0) {
179
- setInternalValue(normalizeTuple(value, min, max));
180
75
  }
181
- }, [value, min, max]);
182
- const handlePointerDown = useCallback(
183
- (e, thumbIndex) => {
184
- var _a;
185
- if (disabled || readOnly) return;
186
- e.preventDefault();
187
- e.stopPropagation();
188
- const thumb = thumbIndex === 0 ? thumb0Ref.current : thumb1Ref.current;
189
- const inputRef = thumbIndex === 0 ? input0Ref : input1Ref;
190
- if (!thumb) return;
191
- onChangeStart == null ? void 0 : onChangeStart();
192
- isDragging.current = thumbIndex;
193
- thumb.setPointerCapture(e.pointerId);
194
- updatePosition(e.clientX, thumbIndex);
195
- (_a = inputRef.current) == null ? void 0 : _a.focus();
196
- const handleMove = (e2) => {
197
- if (isDragging.current !== thumbIndex) return;
198
- e2.preventDefault();
199
- updatePosition(e2.clientX, thumbIndex);
200
- };
201
- const handleUp = (e2) => {
202
- var _a2;
203
- if (isDragging.current !== thumbIndex) return;
204
- e2.preventDefault();
205
- if (thumb.hasPointerCapture(e2.pointerId)) {
206
- thumb.releasePointerCapture(e2.pointerId);
76
+ }
77
+ return {
78
+ hasCustomChildren: hasCustom,
79
+ hasCustomDot: hasDot,
80
+ hasCustomConnects: hasConnects,
81
+ extractedThumbSize: thumbSizeFromChild,
82
+ extractedTrackHeight: trackHeightFromChild
83
+ };
84
+ }, [children]);
85
+ const thumbSize = extractedThumbSize ?? propsThumbSize;
86
+ const trackHeight = extractedTrackHeight ?? 16;
87
+ const safeStep = step > 0 ? step : 1;
88
+ const range = max - min || 1;
89
+ const [actualTrackWidth, setActualTrackWidth] = useState();
90
+ const valueToPosition = useCallback((val) => (val - min) / range, [min, range]);
91
+ const positionToValue = useCallback((position) => min + position * range, [min, range]);
92
+ const normalizedDefaultValue = useMemo(
93
+ () => defaultValue ? normalizeTuple(defaultValue, min, max) : void 0,
94
+ [defaultValue, min, max]
95
+ );
96
+ const defaultStepValue = useMemo(() => {
97
+ if (!normalizedDefaultValue) return null;
98
+ if (safeStep > 1) {
99
+ return normalizedDefaultValue.map(
100
+ (v) => Math.round((v - min) / safeStep) * safeStep + min
101
+ );
102
+ }
103
+ return normalizedDefaultValue;
104
+ }, [normalizedDefaultValue, safeStep, min]);
105
+ const sliderRef = useRef(null);
106
+ const thumb0Ref = useRef(null);
107
+ const thumb1Ref = useRef(null);
108
+ const input0Ref = useRef(null);
109
+ const input1Ref = useRef(null);
110
+ const isDragging = useRef(null);
111
+ const cleanupRef = useRef(null);
112
+ const [internalValue, setInternalValue] = useState(
113
+ normalizeTuple(value, min, max)
114
+ );
115
+ const currentValue = useMemo(
116
+ () => value ? normalizeTuple(value, min, max) : internalValue,
117
+ [value, min, max, internalValue]
118
+ );
119
+ const currentStepValue = useMemo(() => {
120
+ if (safeStep > 1) {
121
+ return currentValue.map((v) => Math.round(v / safeStep) * safeStep);
122
+ }
123
+ return currentValue;
124
+ }, [currentValue, safeStep]);
125
+ const trackWidth = typeof propsWidth === "number" ? propsWidth : actualTrackWidth;
126
+ useIsomorphicLayoutEffect(() => {
127
+ if (typeof propsWidth !== "number" && sliderRef.current) {
128
+ const updateWidth = () => {
129
+ if (sliderRef.current) {
130
+ const width = sliderRef.current.getBoundingClientRect().width;
131
+ if (width > 0) {
132
+ setActualTrackWidth(width);
207
133
  }
208
- updatePosition(e2.clientX, thumbIndex, true);
209
- isDragging.current = null;
210
- const rect = (_a2 = sliderRef.current) == null ? void 0 : _a2.getBoundingClientRect();
211
- if (rect) {
212
- const newPosition = clamp((e2.clientX - rect.left) / rect.width, 0, 1);
213
- const newValue = Math.round(positionToValue(newPosition) / step) * step;
214
- let clampedValue = clamp(newValue, min, max);
215
- if (normalizedDefaultValue && step === 1) {
216
- const snapThreshold = (max - min) * 0.05;
217
- for (const defVal of normalizedDefaultValue) {
218
- const distanceToDefault = Math.abs(clampedValue - defVal);
219
- if (distanceToDefault <= snapThreshold) {
220
- clampedValue = defVal;
221
- break;
222
- }
223
- }
224
- }
225
- const finalTuple = [...currentValue];
226
- finalTuple[thumbIndex] = clampedValue;
227
- if (finalTuple[0] > finalTuple[1]) {
228
- if (thumbIndex === 0) {
229
- finalTuple[0] = finalTuple[1];
230
- } else {
231
- finalTuple[1] = finalTuple[0];
232
- }
233
- }
234
- onChangeEnd == null ? void 0 : onChangeEnd(finalTuple);
134
+ }
135
+ };
136
+ updateWidth();
137
+ const resizeObserver = new ResizeObserver(() => {
138
+ updateWidth();
139
+ });
140
+ resizeObserver.observe(sliderRef.current);
141
+ return () => {
142
+ resizeObserver.disconnect();
143
+ };
144
+ }
145
+ }, [propsWidth]);
146
+ const transforms = useMemo(() => {
147
+ const position0 = valueToPosition(currentValue[0]);
148
+ const position1 = valueToPosition(currentValue[1]);
149
+ const minTransform = 1;
150
+ const maxTransform = (trackWidth ?? 0) - thumbSize - 1;
151
+ const transformX0 = minTransform + position0 * (maxTransform - minTransform);
152
+ const transformX1 = minTransform + position1 * (maxTransform - minTransform);
153
+ return { minTransform, maxTransform, transformX0, transformX1 };
154
+ }, [currentValue, trackWidth, thumbSize, valueToPosition]);
155
+ const dotsData = useMemo(() => {
156
+ if (safeStep <= 1) return null;
157
+ return Array.from({ length: Math.ceil((max - min) / safeStep) + 1 }, (_, i) => {
158
+ const dotValue = min + i * safeStep;
159
+ const dotPosition = valueToPosition(dotValue);
160
+ return {
161
+ value: dotValue,
162
+ position: dotPosition
163
+ };
164
+ });
165
+ }, [safeStep, min, max, valueToPosition]);
166
+ const defaultDotPositions = useMemo(() => {
167
+ if (!normalizedDefaultValue || safeStep > 1) return null;
168
+ return normalizedDefaultValue.map((v) => valueToPosition(v));
169
+ }, [normalizedDefaultValue, safeStep, valueToPosition]);
170
+ const updatePosition = useEventCallback(
171
+ (clientX, thumbIndex, isEnd) => {
172
+ var _a;
173
+ if (readOnly) return;
174
+ const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
175
+ if (!rect) return;
176
+ const newPosition = clamp((clientX - rect.left) / rect.width, 0, 1);
177
+ const newValue = Math.round(positionToValue(newPosition) / safeStep) * safeStep;
178
+ let clampedValue = clamp(newValue, min, max);
179
+ if (normalizedDefaultValue && safeStep <= 1) {
180
+ const snapThreshold = (max - min) * 0.05;
181
+ for (const defVal of normalizedDefaultValue) {
182
+ const distanceToDefault = Math.abs(clampedValue - defVal);
183
+ if (distanceToDefault <= snapThreshold) {
184
+ clampedValue = defVal;
185
+ break;
235
186
  }
236
- window.removeEventListener("pointermove", handleMove);
237
- window.removeEventListener("pointerup", handleUp);
238
- window.removeEventListener("pointercancel", handleUp);
239
- };
240
- window.addEventListener("pointermove", handleMove);
241
- window.addEventListener("pointerup", handleUp);
242
- window.addEventListener("pointercancel", handleUp);
243
- },
244
- [
245
- disabled,
246
- readOnly,
247
- onChangeEnd,
248
- onChangeStart,
249
- updatePosition,
250
- positionToValue,
251
- step,
252
- min,
253
- max,
254
- normalizedDefaultValue,
255
- currentValue
256
- ]
257
- );
258
- const handleSliderPointerDown = useCallback(
259
- (e) => {
260
- var _a;
261
- if (disabled || readOnly) return;
262
- if (e.target === thumb0Ref.current || e.target === thumb1Ref.current) return;
263
- const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
264
- if (!rect) return;
265
- const clickPosition = (e.clientX - rect.left) / rect.width;
266
- const clickValue = positionToValue(clickPosition);
267
- const dist0 = Math.abs(clickValue - currentValue[0]);
268
- const dist1 = Math.abs(clickValue - currentValue[1]);
269
- const thumbIndex = dist0 <= dist1 ? 0 : 1;
270
- handlePointerDown(e, thumbIndex);
271
- },
272
- [disabled, readOnly, handlePointerDown, currentValue, positionToValue]
273
- );
274
- const handleKeyDown = useEventCallback((e, thumbIndex) => {
275
- if (disabled || readOnly) return;
276
- const stepValue = e.shiftKey ? step * 10 : step;
277
- let newValue = currentValue[thumbIndex];
278
- switch (e.key) {
279
- case "ArrowLeft":
280
- case "ArrowDown":
281
- e.preventDefault();
282
- newValue = clamp(newValue - stepValue, min, max);
283
- break;
284
- case "ArrowRight":
285
- case "ArrowUp":
286
- e.preventDefault();
287
- newValue = clamp(newValue + stepValue, min, max);
288
- break;
289
- default:
290
- return;
187
+ }
291
188
  }
292
189
  const newTuple = [...currentValue];
293
- newTuple[thumbIndex] = newValue;
190
+ newTuple[thumbIndex] = clampedValue;
294
191
  if (newTuple[0] > newTuple[1]) {
295
192
  if (thumbIndex === 0) {
296
193
  newTuple[0] = newTuple[1];
@@ -298,184 +195,221 @@ const RangeTuple = forwardRef(
298
195
  newTuple[1] = newTuple[0];
299
196
  }
300
197
  }
301
- onChange == null ? void 0 : onChange(newTuple);
302
- });
303
- useEffect(() => {
304
- var _a, _b;
305
- if (disabled) {
306
- if (document.activeElement === input0Ref.current) {
307
- (_a = input0Ref.current) == null ? void 0 : _a.blur();
308
- }
309
- if (document.activeElement === input1Ref.current) {
310
- (_b = input1Ref.current) == null ? void 0 : _b.blur();
311
- }
312
- }
313
- }, [disabled]);
314
- const tv = useMemo(
315
- () => rangeTv({
316
- hasStepOrDefault: step > 1 || normalizedDefaultValue !== void 0,
317
- disabled
318
- }),
319
- [step, normalizedDefaultValue, disabled]
320
- );
321
- const connectsClass = useMemo(() => {
322
- if (disabled) return "bg-disabled-background";
323
- return connectsClassName.positive;
324
- }, [disabled, connectsClassName]);
325
- const connectStyle = useMemo(() => {
326
- return {
327
- left: `${transforms.transformX0 + thumbSize / 2}px`,
328
- right: `calc(100% - ${transforms.transformX1 + thumbSize / 2}px)`,
329
- height: trackSize == null ? void 0 : trackSize.height
330
- };
331
- }, [transforms.transformX0, transforms.transformX1, thumbSize, trackSize == null ? void 0 : trackSize.height]);
332
- const renderDots = useCallback(() => {
333
- if (dotsData) {
334
- return dotsData.map(({ value: dotValue, position: dotPosition }) => {
335
- const { minTransform, maxTransform } = transforms;
336
- const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
337
- const isWithinRange = dotValue >= currentValue[0] && dotValue <= currentValue[1];
338
- const isDefaultValue = defaultStepValue == null ? void 0 : defaultStepValue.includes(dotValue);
339
- const { dot } = rangeTv({
340
- defaultStepValue: isDefaultValue,
341
- overStepValue: isWithinRange
342
- });
343
- return /* @__PURE__ */ jsx(
344
- "div",
345
- {
346
- className: dot(),
347
- style: {
348
- left: dotTransform + thumbSize / 2
349
- }
350
- },
351
- dotValue
352
- );
353
- });
198
+ if (isEnd) {
199
+ isDragging.current = null;
354
200
  }
355
- if (defaultDotPositions) {
356
- return defaultDotPositions.map((position, idx) => /* @__PURE__ */ jsx(
357
- "div",
358
- {
359
- className: rangeTv({ defaultStepValue: true }).dot(),
360
- style: {
361
- left: transforms.minTransform + position * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
362
- }
363
- },
364
- `default-${idx}`
365
- ));
201
+ if (value === void 0) {
202
+ setInternalValue(newTuple);
366
203
  }
367
- return null;
368
- }, [dotsData, defaultDotPositions, defaultStepValue, transforms, thumbSize, currentValue]);
369
- useEffect(() => {
370
- const noop = () => {
204
+ onChange == null ? void 0 : onChange(newTuple);
205
+ }
206
+ );
207
+ useEffect(() => {
208
+ if (value !== void 0) {
209
+ setInternalValue(normalizeTuple(value, min, max));
210
+ }
211
+ }, [value, min, max]);
212
+ const latestValueRef = useRef(currentValue);
213
+ latestValueRef.current = currentValue;
214
+ const handlePointerDown = useCallback(
215
+ (e, thumbIndex) => {
216
+ var _a;
217
+ if (disabled || readOnly) return;
218
+ e.preventDefault();
219
+ e.stopPropagation();
220
+ const thumb = thumbIndex === 0 ? thumb0Ref.current : thumb1Ref.current;
221
+ const inputRef = thumbIndex === 0 ? input0Ref : input1Ref;
222
+ if (!thumb) return;
223
+ onChangeStart == null ? void 0 : onChangeStart();
224
+ isDragging.current = thumbIndex;
225
+ thumb.setPointerCapture(e.pointerId);
226
+ updatePosition(e.clientX, thumbIndex);
227
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
228
+ const handleMove = (e2) => {
229
+ if (isDragging.current !== thumbIndex) return;
230
+ e2.preventDefault();
231
+ updatePosition(e2.clientX, thumbIndex);
371
232
  };
372
- return () => {
373
- if (typeof window !== "undefined") {
374
- window.removeEventListener("pointermove", noop);
375
- window.removeEventListener("pointerup", noop);
376
- window.removeEventListener("pointercancel", noop);
233
+ const cleanup = () => {
234
+ window.removeEventListener("pointermove", handleMove);
235
+ window.removeEventListener("pointerup", handleUp);
236
+ window.removeEventListener("pointercancel", handleUp);
237
+ cleanupRef.current = null;
238
+ };
239
+ const handleUp = (e2) => {
240
+ if (isDragging.current !== thumbIndex) return;
241
+ e2.preventDefault();
242
+ if (thumb.hasPointerCapture(e2.pointerId)) {
243
+ thumb.releasePointerCapture(e2.pointerId);
377
244
  }
245
+ updatePosition(e2.clientX, thumbIndex, true);
246
+ isDragging.current = null;
247
+ onChangeEnd == null ? void 0 : onChangeEnd(latestValueRef.current);
248
+ cleanup();
378
249
  };
379
- }, []);
380
- const thumb0IsDefault = useMemo(() => {
381
- if (!defaultStepValue) return false;
382
- return currentStepValue[0] === defaultStepValue[0];
383
- }, [currentStepValue, defaultStepValue]);
384
- const thumb1IsDefault = useMemo(() => {
385
- if (!defaultStepValue) return false;
386
- return currentStepValue[1] === defaultStepValue[1];
387
- }, [currentStepValue, defaultStepValue]);
388
- const thumbTv0 = useMemo(
389
- () => rangeTv({
390
- currentDefaultValue: thumb0IsDefault,
391
- hasStepOrDefault: step > 1 || normalizedDefaultValue !== void 0,
392
- disabled
393
- }),
394
- [thumb0IsDefault, step, normalizedDefaultValue, disabled]
395
- );
396
- const thumbTv1 = useMemo(
397
- () => rangeTv({
398
- currentDefaultValue: thumb1IsDefault,
399
- hasStepOrDefault: step > 1 || normalizedDefaultValue !== void 0,
400
- disabled
401
- }),
402
- [thumb1IsDefault, step, normalizedDefaultValue, disabled]
403
- );
404
- return /* @__PURE__ */ jsxs(
405
- "div",
406
- {
407
- ref: mergeRefs(sliderRef, ref),
408
- onPointerDown: handleSliderPointerDown,
409
- className: tcx(tv.container(), className),
410
- style: {
411
- "--width": `${trackWidth}px`,
412
- "--height": `${(trackSize == null ? void 0 : trackSize.height) ?? 16}px`
413
- },
414
- children: [
415
- /* @__PURE__ */ jsx(
416
- "div",
417
- {
418
- className: tcx(tv.connect(), connectsClass),
419
- style: connectStyle
420
- }
421
- ),
422
- (step > 1 || normalizedDefaultValue !== void 0) && /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() }),
423
- /* @__PURE__ */ jsx(
424
- "div",
425
- {
426
- ref: thumb0Ref,
427
- onPointerDown: (e) => handlePointerDown(e, 0),
428
- className: thumbTv0.thumb(),
429
- style: {
430
- width: thumbSize,
431
- height: thumbSize,
432
- transform: `translate(${transforms.transformX0}px, -50%)`,
433
- willChange: isDragging.current === 0 ? "transform" : "auto"
434
- },
435
- children: /* @__PURE__ */ jsx(
436
- "input",
437
- {
438
- ref: input0Ref,
439
- type: "text",
440
- onKeyDown: (e) => handleKeyDown(e, 0),
441
- className: tv.input(),
442
- tabIndex: disabled || readOnly ? -1 : 0,
443
- readOnly: true
444
- }
445
- )
446
- }
447
- ),
448
- /* @__PURE__ */ jsx(
449
- "div",
450
- {
451
- ref: thumb1Ref,
452
- onPointerDown: (e) => handlePointerDown(e, 1),
453
- className: thumbTv1.thumb(),
454
- style: {
455
- width: thumbSize,
456
- height: thumbSize,
457
- transform: `translate(${transforms.transformX1}px, -50%)`,
458
- willChange: isDragging.current === 1 ? "transform" : "auto"
459
- },
460
- children: /* @__PURE__ */ jsx(
461
- "input",
462
- {
463
- ref: input1Ref,
464
- type: "text",
465
- onKeyDown: (e) => handleKeyDown(e, 1),
466
- className: tv.input(),
467
- tabIndex: disabled || readOnly ? -1 : 0,
468
- readOnly: true
469
- }
470
- )
471
- }
472
- )
473
- ]
250
+ cleanupRef.current = cleanup;
251
+ window.addEventListener("pointermove", handleMove);
252
+ window.addEventListener("pointerup", handleUp);
253
+ window.addEventListener("pointercancel", handleUp);
254
+ },
255
+ [disabled, readOnly, onChangeEnd, onChangeStart, updatePosition]
256
+ );
257
+ useEffect(() => {
258
+ return () => {
259
+ var _a;
260
+ (_a = cleanupRef.current) == null ? void 0 : _a.call(cleanupRef);
261
+ };
262
+ }, []);
263
+ const handleSliderPointerDown = useCallback(
264
+ (e) => {
265
+ var _a;
266
+ if (disabled || readOnly) return;
267
+ if (e.target === thumb0Ref.current || e.target === thumb1Ref.current) return;
268
+ const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
269
+ if (!rect) return;
270
+ const clickPosition = (e.clientX - rect.left) / rect.width;
271
+ const clickValue = positionToValue(clickPosition);
272
+ const dist0 = Math.abs(clickValue - currentValue[0]);
273
+ const dist1 = Math.abs(clickValue - currentValue[1]);
274
+ const thumbIndex = dist0 <= dist1 ? 0 : 1;
275
+ handlePointerDown(e, thumbIndex);
276
+ },
277
+ [disabled, readOnly, handlePointerDown, currentValue, positionToValue]
278
+ );
279
+ const handleKeyDown = useEventCallback((e, thumbIndex) => {
280
+ if (disabled || readOnly) return;
281
+ const stepValue = e.shiftKey ? safeStep * 10 : safeStep;
282
+ let newValue = currentValue[thumbIndex];
283
+ switch (e.key) {
284
+ case "ArrowLeft":
285
+ case "ArrowDown":
286
+ e.preventDefault();
287
+ newValue = clamp(newValue - stepValue, min, max);
288
+ break;
289
+ case "ArrowRight":
290
+ case "ArrowUp":
291
+ e.preventDefault();
292
+ newValue = clamp(newValue + stepValue, min, max);
293
+ break;
294
+ default:
295
+ return;
296
+ }
297
+ const newTuple = [...currentValue];
298
+ newTuple[thumbIndex] = newValue;
299
+ if (newTuple[0] > newTuple[1]) {
300
+ if (thumbIndex === 0) {
301
+ newTuple[0] = newTuple[1];
302
+ } else {
303
+ newTuple[1] = newTuple[0];
474
304
  }
475
- );
476
- }
477
- );
478
- RangeTuple.displayName = "RangeTuple";
305
+ }
306
+ onChange == null ? void 0 : onChange(newTuple);
307
+ });
308
+ useEffect(() => {
309
+ var _a, _b;
310
+ if (disabled) {
311
+ if (document.activeElement === input0Ref.current) {
312
+ (_a = input0Ref.current) == null ? void 0 : _a.blur();
313
+ }
314
+ if (document.activeElement === input1Ref.current) {
315
+ (_b = input1Ref.current) == null ? void 0 : _b.blur();
316
+ }
317
+ }
318
+ }, [disabled]);
319
+ const hasStepOrDefault = safeStep > 1 || normalizedDefaultValue !== void 0;
320
+ const tv = useMemo(() => rangeTv({ hasStepOrDefault, disabled }), [hasStepOrDefault, disabled]);
321
+ const thumb0IsDefault = defaultStepValue ? currentStepValue[0] === defaultStepValue[0] : false;
322
+ const thumb1IsDefault = defaultStepValue ? currentStepValue[1] === defaultStepValue[1] : false;
323
+ const thumbTv0 = useMemo(
324
+ () => rangeTv({ currentDefaultValue: thumb0IsDefault, hasStepOrDefault, disabled }),
325
+ [thumb0IsDefault, hasStepOrDefault, disabled]
326
+ );
327
+ const thumbTv1 = useMemo(
328
+ () => rangeTv({ currentDefaultValue: thumb1IsDefault, hasStepOrDefault, disabled }),
329
+ [thumb1IsDefault, hasStepOrDefault, disabled]
330
+ );
331
+ const contextValue = useMemo(
332
+ () => ({
333
+ currentValue,
334
+ disabled,
335
+ readOnly,
336
+ min,
337
+ max,
338
+ step: safeStep,
339
+ thumbSize,
340
+ trackHeight,
341
+ transforms,
342
+ defaultStepValue,
343
+ currentStepValue,
344
+ dotsData,
345
+ defaultDotPositions,
346
+ normalizedDefaultValue,
347
+ thumb0Ref,
348
+ thumb1Ref,
349
+ input0Ref,
350
+ input1Ref,
351
+ isDragging,
352
+ handlePointerDown,
353
+ handleKeyDown,
354
+ tv,
355
+ thumbTv0,
356
+ thumbTv1,
357
+ hasCustomDot,
358
+ hasCustomConnects,
359
+ isDefaultValue: thumb0IsDefault && thumb1IsDefault
360
+ }),
361
+ [
362
+ currentValue,
363
+ disabled,
364
+ readOnly,
365
+ min,
366
+ max,
367
+ safeStep,
368
+ thumbSize,
369
+ trackHeight,
370
+ transforms,
371
+ defaultStepValue,
372
+ currentStepValue,
373
+ dotsData,
374
+ defaultDotPositions,
375
+ normalizedDefaultValue,
376
+ handlePointerDown,
377
+ handleKeyDown,
378
+ tv,
379
+ thumbTv0,
380
+ thumbTv1,
381
+ hasCustomDot,
382
+ hasCustomConnects,
383
+ thumb0IsDefault,
384
+ thumb1IsDefault
385
+ ]
386
+ );
387
+ return /* @__PURE__ */ jsx(RangeTupleContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
388
+ "div",
389
+ {
390
+ ref: mergeRefs(sliderRef, ref),
391
+ onPointerDown: handleSliderPointerDown,
392
+ className: tcx(tv.container(), className),
393
+ style: {
394
+ "--width": `${trackWidth}px`,
395
+ "--height": `${trackHeight}px`,
396
+ "--thumb-size": `${thumbSize}px`
397
+ },
398
+ children: hasCustomChildren ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
399
+ /* @__PURE__ */ jsx(RangeTupleContainer, {}),
400
+ /* @__PURE__ */ jsx(RangeTupleThumb, { index: 0 }),
401
+ /* @__PURE__ */ jsx(RangeTupleThumb, { index: 1 })
402
+ ] })
403
+ }
404
+ ) });
405
+ });
406
+ RangeTupleRoot.displayName = "RangeTuple";
407
+ const RangeTuple = Object.assign(RangeTupleRoot, {
408
+ Container: RangeTupleContainer,
409
+ Connects: RangeTupleConnects,
410
+ Thumb: RangeTupleThumb,
411
+ Dot: RangeTupleDot
412
+ });
479
413
  export {
480
414
  RangeTuple
481
415
  };