@choice-ui/react 1.8.9 → 1.9.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,28 +1,10 @@
1
1
  import { clamp } from "es-toolkit";
2
- import { forwardRef, useMemo, Children, isValidElement, useState, useCallback, useRef, useEffect, createContext, useContext } from "react";
2
+ import { forwardRef, useState, useCallback, useMemo, useRef, useEffect } from "react";
3
3
  import { useEventCallback } from "usehooks-ts";
4
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useIsomorphicLayoutEffect } from "../../../shared/hooks/use-isomorphic-layout-effect/use-isomorphic-layout-effect.js";
6
6
  import { tcv, tcx } from "../../../shared/utils/tcx/tcx.js";
7
7
  import { mergeRefs } from "../../../shared/utils/merge-refs/merge-refs.js";
8
- var RangeContext = createContext(null);
9
- function useRangeContext() {
10
- const context = useContext(RangeContext);
11
- if (!context) {
12
- throw new Error("Range.Connects and Range.Thumb must be used within a Range component");
13
- }
14
- return context;
15
- }
16
- var RangeTupleContext = createContext(null);
17
- function useRangeTupleContext() {
18
- const context = useContext(RangeTupleContext);
19
- if (!context) {
20
- throw new Error(
21
- "RangeTuple.Connects and RangeTuple.Thumb must be used within a RangeTuple component"
22
- );
23
- }
24
- return context;
25
- }
26
8
  var rangeTv = tcv({
27
9
  slots: {
28
10
  container: [
@@ -31,19 +13,19 @@ var rangeTv = tcv({
31
13
  "bg-secondary-background shadow-inset-border rounded-full"
32
14
  ],
33
15
  connect: [
34
- "pointer-events-none absolute h-(--height)",
16
+ "pointer-events-none absolute",
35
17
  "after:absolute",
36
18
  "after:content-['']",
37
19
  "after:rounded-full",
38
- "after:[background:inherit]",
39
- "after:h-[inherit]",
20
+ "after:bg-inherit",
21
+ "after:h-[var(--height)]",
40
22
  "after:left-[calc(var(--height)/-2)]",
41
23
  "after:right-[calc(var(--height)/-2)]"
42
24
  ],
43
- thumbWrapper: ["absolute top-1/2 box-border origin-center z-2"],
44
25
  thumb: [
45
- "shadow-range-thumb border-2 border-white rounded-full size-(--thumb-size)",
46
- "bg-white absolute -translate-y-1/2 -translate-x-1/2 left-1/2 top-1/2"
26
+ "absolute top-1/2 box-border origin-center rounded-full",
27
+ "shadow-range-thumb border-2 border-white",
28
+ "bg-white"
47
29
  ],
48
30
  dotContainer: "pointer-events-none absolute inset-0",
49
31
  dot: ["size-1 rounded-full", "absolute top-1/2", "-translate-x-1/2 -translate-y-1/2"],
@@ -73,12 +55,9 @@ var rangeTv = tcv({
73
55
  },
74
56
  disabled: {
75
57
  true: {
76
- connect: "bg-disabled-background",
77
58
  thumb: "bg-secondary-background"
78
59
  },
79
- false: {
80
- connect: "bg-accent-background"
81
- }
60
+ false: {}
82
61
  }
83
62
  },
84
63
  compoundVariants: [
@@ -114,477 +93,8 @@ var rangeTv = tcv({
114
93
  disabled: false
115
94
  }
116
95
  });
117
- var RangeConnects = forwardRef(
118
- function RangeConnects2(props, ref) {
119
- const { className } = props;
120
- const { currentValue, disabled, min, transforms, thumbSize, trackHeight, tv } = useRangeContext();
121
- const connectsStatus = useMemo(() => {
122
- if (disabled) return "disabled";
123
- if (currentValue < 0) return "negative";
124
- return "positive";
125
- }, [disabled, currentValue]);
126
- const connectStyle = useMemo(() => {
127
- return {
128
- left: min < 0 ? currentValue < 0 ? `${transforms.transformX + thumbSize / 2}px` : "50%" : trackHeight / 2 + "px",
129
- right: min < 0 ? currentValue >= 0 ? `calc(100% - ${transforms.transformX + thumbSize / 2}px)` : "50%" : `calc(100% - ${transforms.transformX + thumbSize / 2}px)`,
130
- height: trackHeight
131
- };
132
- }, [min, currentValue, transforms.transformX, thumbSize, trackHeight]);
133
- return /* @__PURE__ */ jsx(
134
- "div",
135
- {
136
- ref,
137
- className: tcx(tv.connect(), disabled && "bg-disabled-background", className),
138
- "data-connect-status": connectsStatus,
139
- style: connectStyle
140
- }
141
- );
142
- }
143
- );
144
- RangeConnects.displayName = "RangeConnects";
145
- function RangeContainer(props) {
146
- const { children, className, height: _height, ...rest } = props;
147
- const {
148
- currentValue,
149
- step,
150
- transforms,
151
- thumbSize,
152
- defaultStepValue,
153
- dotsData,
154
- defaultDotPosition,
155
- defaultValue,
156
- tv,
157
- hasCustomDot,
158
- hasCustomConnects
159
- } = useRangeContext();
160
- const hasStepOrDefault = step > 1 || defaultValue !== void 0;
161
- const renderDots = () => {
162
- if (dotsData) {
163
- const { minTransform, maxTransform } = transforms;
164
- return dotsData.map(({ value: dotValue, position: dotPosition }) => {
165
- const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
166
- const { dot } = rangeTv({
167
- defaultStepValue: defaultStepValue === dotValue,
168
- overStepValue: dotValue <= currentValue
169
- });
170
- return /* @__PURE__ */ jsx(
171
- "div",
172
- {
173
- className: dot(),
174
- style: {
175
- left: dotTransform + thumbSize / 2
176
- }
177
- },
178
- dotValue
179
- );
180
- });
181
- }
182
- if (defaultDotPosition !== null && defaultDotPosition !== void 0) {
183
- return /* @__PURE__ */ jsx(
184
- "div",
185
- {
186
- className: rangeTv({ defaultStepValue: true }).dot(),
187
- style: {
188
- left: transforms.minTransform + defaultDotPosition * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
189
- }
190
- }
191
- );
192
- }
193
- return null;
194
- };
195
- return /* @__PURE__ */ jsxs(Fragment, { children: [
196
- !hasCustomConnects && /* @__PURE__ */ jsx(
197
- RangeConnects,
198
- {
199
- className: tcx(tv.connect(), className),
200
- ...rest
201
- }
202
- ),
203
- children,
204
- hasStepOrDefault && !hasCustomDot && /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() })
205
- ] });
206
- }
207
- RangeContainer.displayName = "RangeContainer";
208
- var RangeTupleConnects = forwardRef(
209
- function RangeTupleConnects2(props, ref) {
210
- const { className, ...rest } = props;
211
- const { transforms, thumbSize, trackHeight, tv } = useRangeTupleContext();
212
- const connectStyle = useMemo(() => {
213
- return {
214
- left: `${transforms.transformX0 + thumbSize / 2}px`,
215
- right: `calc(100% - ${transforms.transformX1 + thumbSize / 2}px)`,
216
- height: trackHeight
217
- };
218
- }, [transforms.transformX0, transforms.transformX1, thumbSize, trackHeight]);
219
- return /* @__PURE__ */ jsx(
220
- "div",
221
- {
222
- ref,
223
- className: tcx(tv.connect(), className),
224
- style: connectStyle,
225
- ...rest
226
- }
227
- );
228
- }
229
- );
230
- RangeTupleConnects.displayName = "RangeTupleConnects";
231
- function RangeTupleContainer(props) {
232
- const { children, className, height: _height, ...rest } = props;
233
- const {
234
- currentValue,
235
- step,
236
- transforms,
237
- thumbSize,
238
- defaultStepValue,
239
- dotsData,
240
- defaultDotPositions,
241
- normalizedDefaultValue,
242
- tv,
243
- hasCustomDot,
244
- hasCustomConnects
245
- } = useRangeTupleContext();
246
- const hasStepOrDefault = step > 1 || normalizedDefaultValue !== void 0;
247
- const renderDots = () => {
248
- if (dotsData) {
249
- const { minTransform, maxTransform } = transforms;
250
- return dotsData.map(({ value: dotValue, position: dotPosition }) => {
251
- const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
252
- const isWithinRange = dotValue >= currentValue[0] && dotValue <= currentValue[1];
253
- const isDefaultValue = defaultStepValue == null ? void 0 : defaultStepValue.includes(dotValue);
254
- const { dot } = rangeTv({
255
- defaultStepValue: isDefaultValue,
256
- overStepValue: isWithinRange
257
- });
258
- return /* @__PURE__ */ jsx(
259
- "div",
260
- {
261
- className: dot(),
262
- style: {
263
- left: dotTransform + thumbSize / 2
264
- }
265
- },
266
- dotValue
267
- );
268
- });
269
- }
270
- if (defaultDotPositions) {
271
- return defaultDotPositions.map((position, idx) => /* @__PURE__ */ jsx(
272
- "div",
273
- {
274
- className: rangeTv({ defaultStepValue: true }).dot(),
275
- style: {
276
- left: transforms.minTransform + position * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
277
- }
278
- },
279
- `default-${idx}`
280
- ));
281
- }
282
- return null;
283
- };
284
- return /* @__PURE__ */ jsxs(Fragment, { children: [
285
- !hasCustomConnects && /* @__PURE__ */ jsx(
286
- RangeTupleConnects,
287
- {
288
- className: tcx(tv.connect(), className),
289
- ...rest
290
- }
291
- ),
292
- children,
293
- hasStepOrDefault && !hasCustomDot && /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() })
294
- ] });
295
- }
296
- RangeTupleContainer.displayName = "RangeTupleContainer";
297
- var BaseThumb = forwardRef(function BaseThumb2(props, ref) {
298
- const {
299
- className,
300
- thumbRef,
301
- inputRef,
302
- thumbSize,
303
- transformX,
304
- isDragging,
305
- disabled,
306
- readOnly,
307
- tv,
308
- thumbTv,
309
- onPointerDown,
310
- onKeyDown,
311
- isDefaultValue
312
- } = props;
313
- const thumbStyle = useMemo(
314
- () => ({
315
- width: thumbSize,
316
- height: thumbSize,
317
- transform: `translate(${transformX}px, -50%)`,
318
- willChange: isDragging ? "transform" : "auto"
319
- }),
320
- [thumbSize, transformX, isDragging]
321
- );
322
- return /* @__PURE__ */ jsx(
323
- "div",
324
- {
325
- ref: (node) => {
326
- if (thumbRef && "current" in thumbRef) {
327
- thumbRef.current = node;
328
- }
329
- if (typeof ref === "function") {
330
- ref(node);
331
- } else if (ref) {
332
- ref.current = node;
333
- }
334
- },
335
- onPointerDown,
336
- className: tv.thumbWrapper(),
337
- style: thumbStyle,
338
- children: /* @__PURE__ */ jsx(
339
- "div",
340
- {
341
- className: tcx(thumbTv.thumb(), className),
342
- "data-status": isDefaultValue ? "default" : void 0,
343
- children: /* @__PURE__ */ jsx(
344
- "input",
345
- {
346
- ref: (node) => {
347
- if (inputRef && "current" in inputRef) {
348
- inputRef.current = node;
349
- }
350
- },
351
- type: "text",
352
- onKeyDown,
353
- className: tv.input(),
354
- tabIndex: disabled || readOnly ? -1 : 0,
355
- readOnly: true
356
- }
357
- )
358
- }
359
- )
360
- }
361
- );
362
- });
363
- var RangeThumb = forwardRef(
364
- function RangeThumb2(props, ref) {
365
- const { className, size: _size } = props;
366
- const {
367
- disabled,
368
- readOnly,
369
- transforms,
370
- thumbSize,
371
- thumbRef,
372
- inputRef,
373
- isDragging,
374
- isDefaultValue,
375
- handlePointerDown,
376
- handleKeyDown,
377
- tv
378
- } = useRangeContext();
379
- return /* @__PURE__ */ jsx(
380
- BaseThumb,
381
- {
382
- ref,
383
- className,
384
- thumbRef,
385
- inputRef,
386
- thumbSize,
387
- transformX: transforms.transformX,
388
- isDragging: isDragging.current,
389
- disabled,
390
- readOnly,
391
- tv,
392
- thumbTv: tv,
393
- onPointerDown: handlePointerDown,
394
- onKeyDown: handleKeyDown,
395
- isDefaultValue
396
- }
397
- );
398
- }
399
- );
400
- RangeThumb.displayName = "RangeThumb";
401
- var RangeTupleThumb = forwardRef(
402
- function RangeTupleThumb2(props, ref) {
403
- const { className, size: _size, index } = props;
404
- const {
405
- disabled,
406
- readOnly,
407
- transforms,
408
- thumbSize,
409
- thumb0Ref,
410
- thumb1Ref,
411
- input0Ref,
412
- input1Ref,
413
- isDragging,
414
- isDefaultValue,
415
- handlePointerDown,
416
- handleKeyDown,
417
- tv,
418
- thumbTv0,
419
- thumbTv1
420
- } = useRangeTupleContext();
421
- const thumbRef = index === 0 ? thumb0Ref : thumb1Ref;
422
- const inputRef = index === 0 ? input0Ref : input1Ref;
423
- const thumbTv = index === 0 ? thumbTv0 : thumbTv1;
424
- const transformX = index === 0 ? transforms.transformX0 : transforms.transformX1;
425
- return /* @__PURE__ */ jsx(
426
- BaseThumb,
427
- {
428
- ref,
429
- className,
430
- thumbRef,
431
- inputRef,
432
- thumbSize,
433
- transformX,
434
- isDragging: isDragging.current === index,
435
- disabled,
436
- readOnly,
437
- tv,
438
- thumbTv,
439
- onPointerDown: (e) => handlePointerDown(e, index),
440
- onKeyDown: (e) => handleKeyDown(e, index),
441
- isDefaultValue
442
- }
443
- );
444
- }
445
- );
446
- RangeTupleThumb.displayName = "RangeTupleThumb";
447
- function getDotStatus(isOver, isDefault) {
448
- if (isOver && isDefault) return "default-over";
449
- if (isOver) return "over";
450
- if (isDefault) return "default";
451
- return "under";
452
- }
453
- var RangeDot = forwardRef(function RangeDot2(props, ref) {
454
- const { className, ...rest } = props;
455
- const {
456
- currentValue,
457
- step,
458
- transforms,
459
- thumbSize,
460
- defaultStepValue,
461
- dotsData,
462
- defaultDotPosition,
463
- defaultValue,
464
- tv
465
- } = useRangeContext();
466
- const hasStepOrDefault = step > 1 || defaultValue !== void 0;
467
- if (!hasStepOrDefault) {
468
- return null;
469
- }
470
- const renderDots = () => {
471
- if (dotsData) {
472
- const { minTransform, maxTransform } = transforms;
473
- return dotsData.map(({ value: dotValue, position: dotPosition }, idx) => {
474
- const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
475
- const isDefault = defaultStepValue === dotValue;
476
- const isOver = dotValue <= currentValue;
477
- const { dot } = rangeTv({
478
- defaultStepValue: isDefault,
479
- overStepValue: isOver
480
- });
481
- return /* @__PURE__ */ jsx(
482
- "div",
483
- {
484
- ref: idx === 0 ? ref : void 0,
485
- className: tcx(dot(), className),
486
- "data-status": getDotStatus(isOver, isDefault),
487
- style: {
488
- left: dotTransform + thumbSize / 2
489
- },
490
- ...rest
491
- },
492
- dotValue
493
- );
494
- });
495
- }
496
- if (defaultDotPosition !== null && defaultDotPosition !== void 0 && defaultStepValue) {
497
- const isOver = defaultStepValue <= currentValue;
498
- return /* @__PURE__ */ jsx(
499
- "div",
500
- {
501
- ref,
502
- className: tcx(rangeTv({ defaultStepValue: true }).dot(), className),
503
- "data-status": isOver ? "over" : "default",
504
- style: {
505
- left: transforms.minTransform + defaultDotPosition * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
506
- },
507
- ...rest
508
- }
509
- );
510
- }
511
- return null;
512
- };
513
- return /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() });
514
- });
515
- RangeDot.displayName = "RangeDot";
516
- var RangeTupleDot = forwardRef(
517
- function RangeTupleDot2(props, ref) {
518
- const { className, ...rest } = props;
519
- const {
520
- currentValue,
521
- step,
522
- transforms,
523
- thumbSize,
524
- defaultStepValue,
525
- dotsData,
526
- defaultDotPositions,
527
- normalizedDefaultValue,
528
- tv
529
- } = useRangeTupleContext();
530
- const hasStepOrDefault = step > 1 || normalizedDefaultValue !== void 0;
531
- if (!hasStepOrDefault) {
532
- return null;
533
- }
534
- const renderDots = () => {
535
- if (dotsData) {
536
- const { minTransform, maxTransform } = transforms;
537
- return dotsData.map(({ value: dotValue, position: dotPosition }, idx) => {
538
- const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
539
- const isWithinRange = dotValue >= currentValue[0] && dotValue <= currentValue[1];
540
- const isDefaultValue = defaultStepValue == null ? void 0 : defaultStepValue.includes(dotValue);
541
- const { dot } = rangeTv({
542
- defaultStepValue: isDefaultValue,
543
- overStepValue: isWithinRange
544
- });
545
- return /* @__PURE__ */ jsx(
546
- "div",
547
- {
548
- ref: idx === 0 ? ref : void 0,
549
- className: tcx(dot(), className),
550
- "data-status": getDotStatus(isWithinRange, !!isDefaultValue),
551
- "data-position": idx === 0 ? "left" : "right",
552
- style: {
553
- left: dotTransform + thumbSize / 2
554
- },
555
- ...rest
556
- },
557
- dotValue
558
- );
559
- });
560
- }
561
- if (defaultDotPositions && defaultStepValue) {
562
- const leftIsOver = defaultStepValue[0] >= currentValue[0];
563
- const rightIsOver = defaultStepValue[1] <= currentValue[1];
564
- return defaultDotPositions.map((position, idx) => /* @__PURE__ */ jsx(
565
- "div",
566
- {
567
- ref: idx === 0 ? ref : void 0,
568
- className: tcx(rangeTv({ defaultStepValue: true }).dot(), className),
569
- "data-status": leftIsOver && idx === 0 ? "left-over" : rightIsOver && idx === 1 ? "right-over" : "default",
570
- "data-position": idx === 0 ? "left" : "right",
571
- style: {
572
- left: transforms.minTransform + position * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
573
- },
574
- ...rest
575
- },
576
- `default-${idx}`
577
- ));
578
- }
579
- return null;
580
- };
581
- return /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() });
582
- }
583
- );
584
- RangeTupleDot.displayName = "RangeTupleDot";
585
- var RangeRoot = forwardRef(function Range(props, ref) {
96
+ var Range = forwardRef(function Range2(props, ref) {
586
97
  const {
587
- children,
588
98
  defaultValue,
589
99
  value,
590
100
  onChange,
@@ -596,82 +106,52 @@ var RangeRoot = forwardRef(function Range(props, ref) {
596
106
  disabled = false,
597
107
  readOnly = false,
598
108
  className,
599
- width: propsWidth = 256,
600
- thumbSize: propsThumbSize = 14
109
+ connectsClassName = {
110
+ positive: "bg-accent-background",
111
+ negative: "bg-accent-background"
112
+ },
113
+ trackSize = {
114
+ width: 256,
115
+ height: 16
116
+ },
117
+ thumbSize = 14
601
118
  } = props;
602
- const {
603
- hasCustomChildren,
604
- hasCustomDot,
605
- hasCustomConnects,
606
- extractedThumbSize,
607
- extractedTrackHeight
608
- } = useMemo(() => {
609
- const childArray = Children.toArray(children);
610
- let hasCustom = false;
611
- let hasDot = false;
612
- let hasConnects = false;
613
- let thumbSizeFromChild;
614
- let trackHeightFromChild;
615
- for (const child of childArray) {
616
- if (isValidElement(child)) {
617
- const type = child.type;
618
- if (child.type === RangeThumb || (type == null ? void 0 : type.displayName) === "RangeThumb") {
619
- hasCustom = true;
620
- const childProps = child.props;
621
- if (childProps.size !== void 0) {
622
- thumbSizeFromChild = childProps.size;
623
- }
624
- } else if (child.type === RangeContainer || (type == null ? void 0 : type.displayName) === "RangeContainer") {
625
- hasCustom = true;
626
- const childProps = child.props;
627
- if (childProps.height !== void 0) {
628
- trackHeightFromChild = childProps.height;
629
- }
630
- } else if (child.type === RangeConnects || (type == null ? void 0 : type.displayName) === "RangeConnects") {
631
- hasCustom = true;
632
- hasConnects = true;
633
- } else if (child.type === RangeDot || (type == null ? void 0 : type.displayName) === "RangeDot") {
634
- hasCustom = true;
635
- hasDot = true;
636
- }
637
- }
638
- }
639
- return {
640
- hasCustomChildren: hasCustom,
641
- hasCustomDot: hasDot,
642
- hasCustomConnects: hasConnects,
643
- extractedThumbSize: thumbSizeFromChild,
644
- extractedTrackHeight: trackHeightFromChild
645
- };
646
- }, [children]);
647
- const thumbSize = extractedThumbSize ?? propsThumbSize;
648
- const trackHeight = extractedTrackHeight ?? 16;
649
- const safeStep = step > 0 ? step : 1;
650
- const range = max - min || 1;
651
119
  const [actualTrackWidth, setActualTrackWidth] = useState();
652
- const valueToPosition = useCallback((val) => (val - min) / range, [min, range]);
653
- const positionToValue = useCallback((position) => min + position * range, [min, range]);
120
+ const valueToPosition = useCallback((val) => (val - min) / (max - min), [min, max]);
121
+ const positionToValue = useCallback(
122
+ (position) => min + position * (max - min),
123
+ [min, max]
124
+ );
654
125
  const defaultStepValue = useMemo(() => {
655
126
  if (defaultValue === void 0 || defaultValue === null) return null;
656
- if (safeStep > 1) {
657
- return Math.round((defaultValue - min) / safeStep) * safeStep + min;
127
+ if (step > 1) {
128
+ return Math.round((defaultValue - min) / step) * step + min;
658
129
  }
659
130
  return defaultValue;
660
- }, [defaultValue, safeStep, min]);
131
+ }, [defaultValue, step, min]);
661
132
  const sliderRef = useRef(null);
662
133
  const thumbRef = useRef(null);
663
134
  const inputRef = useRef(null);
664
135
  const isDragging = useRef(false);
665
- const cleanupRef = useRef(null);
666
136
  const [internalValue, setInternalValue] = useState(value ?? min);
667
137
  const currentValue = value ?? internalValue;
668
138
  const currentStepValue = useMemo(
669
- () => safeStep > 1 ? Math.round(currentValue / safeStep) * safeStep : currentValue,
670
- [currentValue, safeStep]
139
+ () => step > 1 ? Math.round(currentValue / step) * step : currentValue,
140
+ [currentValue, step]
671
141
  );
672
- const trackWidth = typeof propsWidth === "number" ? propsWidth : actualTrackWidth;
142
+ const [transforms, setTransforms] = useState({
143
+ minTransform: 1,
144
+ maxTransform: 0,
145
+ transformX: 0
146
+ });
147
+ const trackWidth = useMemo(() => {
148
+ if ((trackSize == null ? void 0 : trackSize.width) === "auto") {
149
+ return actualTrackWidth;
150
+ }
151
+ return trackSize == null ? void 0 : trackSize.width;
152
+ }, [trackSize == null ? void 0 : trackSize.width, actualTrackWidth]);
673
153
  useIsomorphicLayoutEffect(() => {
674
- if (typeof propsWidth !== "number" && sliderRef.current) {
154
+ if ((trackSize == null ? void 0 : trackSize.width) === "auto" && sliderRef.current) {
675
155
  const updateWidth = () => {
676
156
  if (sliderRef.current) {
677
157
  const width = sliderRef.current.getBoundingClientRect().width;
@@ -689,39 +169,43 @@ var RangeRoot = forwardRef(function Range(props, ref) {
689
169
  resizeObserver.disconnect();
690
170
  };
691
171
  }
692
- }, [propsWidth]);
693
- const transforms = useMemo(() => {
172
+ }, [trackSize == null ? void 0 : trackSize.width]);
173
+ useEffect(() => {
694
174
  const position = valueToPosition(currentValue);
695
175
  const minTransform = 1;
696
- const maxTransform = (typeof trackWidth === "number" ? trackWidth : 0) - thumbSize - 1;
176
+ const maxTransform = (trackWidth ?? 0) - thumbSize - 1;
697
177
  const transformX = minTransform + position * (maxTransform - minTransform);
698
- return { minTransform, maxTransform, transformX };
178
+ setTransforms({
179
+ minTransform,
180
+ maxTransform,
181
+ transformX
182
+ });
699
183
  }, [currentValue, trackWidth, thumbSize, valueToPosition]);
700
184
  const dotsData = useMemo(() => {
701
- if (safeStep <= 1) return null;
702
- return Array.from({ length: Math.ceil((max - min) / safeStep) + 1 }, (_, i) => {
703
- const dotValue = min + i * safeStep;
185
+ if (!step || step <= 1) return null;
186
+ return Array.from({ length: Math.ceil((max - min) / step) + 1 }, (_, i) => {
187
+ const dotValue = min + i * step;
704
188
  const dotPosition = valueToPosition(dotValue);
705
189
  return {
706
190
  value: dotValue,
707
191
  position: dotPosition
708
192
  };
709
193
  });
710
- }, [safeStep, min, max, valueToPosition]);
194
+ }, [step, min, max, valueToPosition]);
711
195
  const defaultDotPosition = useMemo(() => {
712
- if (defaultValue === void 0 || defaultValue === null || safeStep > 1) return null;
196
+ if (defaultValue === void 0 || defaultValue === null || step > 1) return null;
713
197
  return valueToPosition(defaultValue);
714
- }, [defaultValue, safeStep, valueToPosition]);
198
+ }, [defaultValue, step, valueToPosition]);
715
199
  const updatePosition = useEventCallback((clientX, isEnd) => {
716
200
  var _a;
717
201
  if (readOnly) return;
718
202
  const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
719
203
  if (!rect) return;
720
204
  const newPosition = clamp((clientX - rect.left) / rect.width, 0, 1);
721
- const newValue = Math.round(positionToValue(newPosition) / safeStep) * safeStep;
205
+ const newValue = Math.round(positionToValue(newPosition) / step) * step;
722
206
  let clampedValue = clamp(newValue, min, max);
723
- if (defaultValue !== void 0 && defaultValue !== null && safeStep <= 1) {
724
- const snapThreshold = range * 0.05;
207
+ if (defaultValue !== void 0 && defaultValue !== null && step === 1) {
208
+ const snapThreshold = (max - min) * 0.05;
725
209
  const distanceToDefault = Math.abs(clampedValue - defaultValue);
726
210
  if (distanceToDefault <= snapThreshold) {
727
211
  clampedValue = defaultValue;
@@ -758,12 +242,6 @@ var RangeRoot = forwardRef(function Range(props, ref) {
758
242
  e2.preventDefault();
759
243
  updatePosition(e2.clientX);
760
244
  };
761
- const cleanup = () => {
762
- window.removeEventListener("pointermove", handleMove);
763
- window.removeEventListener("pointerup", handleUp);
764
- window.removeEventListener("pointercancel", handleUp);
765
- cleanupRef.current = null;
766
- };
767
245
  const handleUp = (e2) => {
768
246
  if (!isDragging.current) return;
769
247
  e2.preventDefault();
@@ -773,21 +251,16 @@ var RangeRoot = forwardRef(function Range(props, ref) {
773
251
  updatePosition(e2.clientX, true);
774
252
  isDragging.current = false;
775
253
  onChangeEnd == null ? void 0 : onChangeEnd();
776
- cleanup();
254
+ window.removeEventListener("pointermove", handleMove);
255
+ window.removeEventListener("pointerup", handleUp);
256
+ window.removeEventListener("pointercancel", handleUp);
777
257
  };
778
- cleanupRef.current = cleanup;
779
258
  window.addEventListener("pointermove", handleMove);
780
259
  window.addEventListener("pointerup", handleUp);
781
260
  window.addEventListener("pointercancel", handleUp);
782
261
  },
783
262
  [disabled, readOnly, onChangeEnd, onChangeStart, updatePosition]
784
263
  );
785
- useEffect(() => {
786
- return () => {
787
- var _a;
788
- (_a = cleanupRef.current) == null ? void 0 : _a.call(cleanupRef);
789
- };
790
- }, []);
791
264
  const handleSliderPointerDown = useCallback(
792
265
  (e) => {
793
266
  if (disabled || readOnly || e.target === thumbRef.current) return;
@@ -797,7 +270,7 @@ var RangeRoot = forwardRef(function Range(props, ref) {
797
270
  );
798
271
  const handleKeyDown = useEventCallback((e) => {
799
272
  if (disabled || readOnly) return;
800
- const stepValue = e.shiftKey ? safeStep * 10 : safeStep;
273
+ const stepValue = e.shiftKey ? step * 10 : step;
801
274
  let newValue = currentValue;
802
275
  switch (e.key) {
803
276
  case "ArrowLeft":
@@ -823,266 +296,405 @@ var RangeRoot = forwardRef(function Range(props, ref) {
823
296
  (_a = inputRef.current) == null ? void 0 : _a.blur();
824
297
  }
825
298
  }, [disabled]);
826
- const hasStepOrDefault = safeStep > 1 || defaultValue !== void 0;
827
299
  const tv = useMemo(
828
300
  () => rangeTv({
829
301
  currentDefaultValue: defaultStepValue === currentStepValue,
830
- hasStepOrDefault,
302
+ hasStepOrDefault: step > 1 || defaultValue !== void 0,
831
303
  disabled
832
304
  }),
833
- [defaultStepValue, currentStepValue, hasStepOrDefault, disabled]
834
- );
835
- const contextValue = useMemo(
836
- () => ({
837
- currentValue,
838
- disabled,
839
- readOnly,
840
- min,
841
- max,
842
- step: safeStep,
843
- thumbSize,
844
- trackHeight,
845
- transforms,
846
- defaultStepValue,
847
- currentStepValue,
848
- dotsData,
849
- defaultDotPosition,
850
- thumbRef,
851
- inputRef,
852
- isDragging,
853
- handlePointerDown,
854
- handleKeyDown,
855
- tv,
856
- defaultValue,
857
- hasCustomDot,
858
- hasCustomConnects,
859
- isDefaultValue: defaultStepValue === currentStepValue && hasStepOrDefault
860
- }),
861
- [
862
- currentValue,
863
- disabled,
864
- readOnly,
865
- min,
866
- max,
867
- safeStep,
868
- thumbSize,
869
- trackHeight,
870
- transforms,
871
- defaultStepValue,
872
- currentStepValue,
873
- dotsData,
874
- defaultDotPosition,
875
- handlePointerDown,
876
- handleKeyDown,
877
- tv,
878
- defaultValue,
879
- hasCustomDot,
880
- hasCustomConnects
881
- ]
305
+ [defaultStepValue, currentStepValue, step, defaultValue, disabled]
882
306
  );
883
- return /* @__PURE__ */ jsx(RangeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
884
- "div",
885
- {
886
- ref: mergeRefs(sliderRef, ref),
887
- onPointerDown: handleSliderPointerDown,
888
- className: tcx(tv.container(), className),
889
- style: {
890
- "--width": `${typeof trackWidth === "number" ? trackWidth : actualTrackWidth}px`,
891
- "--height": `${trackHeight}px`,
892
- "--thumb-size": `${thumbSize}px`
893
- },
894
- children: hasCustomChildren ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
895
- /* @__PURE__ */ jsx(RangeContainer, {}),
896
- /* @__PURE__ */ jsx(RangeThumb, {})
897
- ] })
898
- }
899
- ) });
900
- });
901
- RangeRoot.displayName = "Range";
902
- var Range2 = Object.assign(RangeRoot, {
903
- Container: RangeContainer,
904
- Connects: RangeConnects,
905
- Thumb: RangeThumb,
906
- Dot: RangeDot
907
- });
908
- function normalizeTuple(value, min, max) {
909
- if (value === void 0) {
910
- return [min, max];
911
- }
912
- if (Array.isArray(value)) {
913
- const [v0, v1] = value;
914
- return [clamp(Math.min(v0, v1), min, max), clamp(Math.max(v0, v1), min, max)];
915
- }
916
- return [clamp(value, min, max), max];
917
- }
918
- var RangeTupleRoot = forwardRef(function RangeTuple(props, ref) {
919
- const {
920
- children,
921
- defaultValue,
922
- value,
923
- onChange,
924
- onChangeStart,
925
- onChangeEnd,
926
- min = 0,
927
- max = 100,
928
- step = 1,
929
- disabled = false,
930
- readOnly = false,
931
- className,
932
- width: propsWidth = 256,
933
- thumbSize: propsThumbSize = 14
934
- } = props;
935
- const {
936
- hasCustomChildren,
937
- hasCustomDot,
938
- hasCustomConnects,
939
- extractedThumbSize,
940
- extractedTrackHeight
941
- } = useMemo(() => {
942
- const childArray = Children.toArray(children);
943
- let hasCustom = false;
944
- let hasDot = false;
945
- let hasConnects = false;
946
- let thumbSizeFromChild;
947
- let trackHeightFromChild;
948
- for (const child of childArray) {
949
- if (isValidElement(child)) {
950
- const type = child.type;
951
- if (child.type === RangeTupleThumb || (type == null ? void 0 : type.displayName) === "RangeTupleThumb") {
952
- hasCustom = true;
953
- const childProps = child.props;
954
- if (childProps.size !== void 0) {
955
- thumbSizeFromChild = childProps.size;
956
- }
957
- } else if (child.type === RangeTupleContainer || (type == null ? void 0 : type.displayName) === "RangeTupleContainer") {
958
- hasCustom = true;
959
- const childProps = child.props;
960
- if (childProps.height !== void 0) {
961
- trackHeightFromChild = childProps.height;
307
+ const connectsClass = useMemo(() => {
308
+ if (disabled) return "bg-disabled-background";
309
+ if (currentValue < 0) return connectsClassName.negative;
310
+ return connectsClassName.positive;
311
+ }, [disabled, currentValue, connectsClassName]);
312
+ const connectStyle = useMemo(() => {
313
+ return {
314
+ left: min < 0 ? currentValue < 0 ? `${transforms.transformX + thumbSize / 2}px` : "50%" : ((trackSize == null ? void 0 : trackSize.height) ?? 0) / 2 + "px",
315
+ right: min < 0 ? currentValue >= 0 ? `calc(100% - ${transforms.transformX + thumbSize / 2}px)` : "50%" : `calc(100% - ${transforms.transformX + thumbSize / 2}px)`,
316
+ height: trackSize == null ? void 0 : trackSize.height
317
+ };
318
+ }, [min, currentValue, transforms.transformX, thumbSize, trackSize == null ? void 0 : trackSize.height]);
319
+ const renderDots = useCallback(() => {
320
+ if (dotsData) {
321
+ return dotsData.map(({ value: dotValue, position: dotPosition }) => {
322
+ const { minTransform, maxTransform } = transforms;
323
+ const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
324
+ const { dot } = rangeTv({
325
+ defaultStepValue: defaultStepValue === dotValue,
326
+ overStepValue: dotValue <= currentValue
327
+ });
328
+ return /* @__PURE__ */ jsx(
329
+ "div",
330
+ {
331
+ className: dot(),
332
+ style: {
333
+ left: dotTransform + thumbSize / 2
334
+ }
335
+ },
336
+ dotValue
337
+ );
338
+ });
339
+ }
340
+ if (defaultDotPosition !== null && defaultDotPosition !== void 0) {
341
+ return /* @__PURE__ */ jsx(
342
+ "div",
343
+ {
344
+ className: rangeTv({ defaultStepValue: true }).dot(),
345
+ style: {
346
+ left: transforms.minTransform + defaultDotPosition * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
962
347
  }
963
- } else if (child.type === RangeTupleConnects || (type == null ? void 0 : type.displayName) === "RangeTupleConnects") {
964
- hasCustom = true;
965
- hasConnects = true;
966
- } else if (child.type === RangeTupleDot || (type == null ? void 0 : type.displayName) === "RangeTupleDot") {
967
- hasCustom = true;
968
- hasDot = true;
969
348
  }
970
- }
349
+ );
971
350
  }
972
- return {
973
- hasCustomChildren: hasCustom,
974
- hasCustomDot: hasDot,
975
- hasCustomConnects: hasConnects,
976
- extractedThumbSize: thumbSizeFromChild,
977
- extractedTrackHeight: trackHeightFromChild
351
+ return null;
352
+ }, [dotsData, defaultDotPosition, transforms, defaultStepValue, currentValue, thumbSize]);
353
+ useEffect(() => {
354
+ const noop = () => {
978
355
  };
979
- }, [children]);
980
- const thumbSize = extractedThumbSize ?? propsThumbSize;
981
- const trackHeight = extractedTrackHeight ?? 16;
982
- const safeStep = step > 0 ? step : 1;
983
- const range = max - min || 1;
984
- const [actualTrackWidth, setActualTrackWidth] = useState();
985
- const valueToPosition = useCallback((val) => (val - min) / range, [min, range]);
986
- const positionToValue = useCallback((position) => min + position * range, [min, range]);
987
- const normalizedDefaultValue = useMemo(
988
- () => defaultValue ? normalizeTuple(defaultValue, min, max) : void 0,
989
- [defaultValue, min, max]
990
- );
991
- const defaultStepValue = useMemo(() => {
992
- if (!normalizedDefaultValue) return null;
993
- if (safeStep > 1) {
994
- return normalizedDefaultValue.map(
995
- (v) => Math.round((v - min) / safeStep) * safeStep + min
996
- );
356
+ return () => {
357
+ if (typeof window !== "undefined") {
358
+ window.removeEventListener("pointermove", noop);
359
+ window.removeEventListener("pointerup", noop);
360
+ window.removeEventListener("pointercancel", noop);
361
+ }
362
+ };
363
+ }, []);
364
+ return /* @__PURE__ */ jsxs(
365
+ "div",
366
+ {
367
+ ref: mergeRefs(sliderRef, ref),
368
+ onPointerDown: handleSliderPointerDown,
369
+ className: tcx(tv.container(), className),
370
+ style: {
371
+ "--width": `${trackWidth}px`,
372
+ "--height": `${(trackSize == null ? void 0 : trackSize.height) ?? 16}px`
373
+ },
374
+ children: [
375
+ /* @__PURE__ */ jsx(
376
+ "div",
377
+ {
378
+ className: tcx(tv.connect(), connectsClass),
379
+ style: connectStyle
380
+ }
381
+ ),
382
+ step > 1 || defaultValue !== void 0 ? /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() }) : "",
383
+ /* @__PURE__ */ jsx(
384
+ "div",
385
+ {
386
+ ref: thumbRef,
387
+ onPointerDown: handlePointerDown,
388
+ className: tv.thumb(),
389
+ style: {
390
+ width: thumbSize,
391
+ height: thumbSize,
392
+ transform: `translate(${transforms.transformX}px, -50%)`,
393
+ willChange: isDragging.current ? "transform" : "auto"
394
+ },
395
+ children: /* @__PURE__ */ jsx(
396
+ "input",
397
+ {
398
+ ref: inputRef,
399
+ type: "text",
400
+ onKeyDown: handleKeyDown,
401
+ className: tv.input(),
402
+ tabIndex: disabled || readOnly ? -1 : 0,
403
+ readOnly: true
404
+ }
405
+ )
406
+ }
407
+ )
408
+ ]
997
409
  }
998
- return normalizedDefaultValue;
999
- }, [normalizedDefaultValue, safeStep, min]);
1000
- const sliderRef = useRef(null);
1001
- const thumb0Ref = useRef(null);
1002
- const thumb1Ref = useRef(null);
1003
- const input0Ref = useRef(null);
1004
- const input1Ref = useRef(null);
1005
- const isDragging = useRef(null);
1006
- const cleanupRef = useRef(null);
1007
- const [internalValue, setInternalValue] = useState(
1008
- normalizeTuple(value, min, max)
1009
410
  );
1010
- const currentValue = useMemo(
1011
- () => value ? normalizeTuple(value, min, max) : internalValue,
1012
- [value, min, max, internalValue]
1013
- );
1014
- const currentStepValue = useMemo(() => {
1015
- if (safeStep > 1) {
1016
- return currentValue.map((v) => Math.round(v / safeStep) * safeStep);
1017
- }
1018
- return currentValue;
1019
- }, [currentValue, safeStep]);
1020
- const trackWidth = typeof propsWidth === "number" ? propsWidth : actualTrackWidth;
1021
- useIsomorphicLayoutEffect(() => {
1022
- if (typeof propsWidth !== "number" && sliderRef.current) {
1023
- const updateWidth = () => {
1024
- if (sliderRef.current) {
1025
- const width = sliderRef.current.getBoundingClientRect().width;
1026
- if (width > 0) {
1027
- setActualTrackWidth(width);
411
+ });
412
+ Range.displayName = "Range";
413
+ function normalizeTuple(value, min, max) {
414
+ if (value === void 0) {
415
+ return [min, max];
416
+ }
417
+ if (Array.isArray(value)) {
418
+ const [v0, v1] = value;
419
+ return [clamp(Math.min(v0, v1), min, max), clamp(Math.max(v0, v1), min, max)];
420
+ }
421
+ return [clamp(value, min, max), max];
422
+ }
423
+ var RangeTuple = forwardRef(
424
+ function RangeTuple2(props, ref) {
425
+ const {
426
+ defaultValue,
427
+ value,
428
+ onChange,
429
+ onChangeStart,
430
+ onChangeEnd,
431
+ min = 0,
432
+ max = 100,
433
+ step = 1,
434
+ disabled = false,
435
+ readOnly = false,
436
+ className,
437
+ connectsClassName = {
438
+ positive: "bg-accent-background",
439
+ negative: "bg-accent-background"
440
+ },
441
+ trackSize = {
442
+ width: 256,
443
+ height: 16
444
+ },
445
+ thumbSize = 14
446
+ } = props;
447
+ const [actualTrackWidth, setActualTrackWidth] = useState();
448
+ const valueToPosition = useCallback((val) => (val - min) / (max - min), [min, max]);
449
+ const positionToValue = useCallback(
450
+ (position) => min + position * (max - min),
451
+ [min, max]
452
+ );
453
+ const normalizedDefaultValue = useMemo(
454
+ () => defaultValue ? normalizeTuple(defaultValue, min, max) : void 0,
455
+ [defaultValue, min, max]
456
+ );
457
+ const defaultStepValue = useMemo(() => {
458
+ if (!normalizedDefaultValue) return null;
459
+ if (step > 1) {
460
+ return normalizedDefaultValue.map((v) => Math.round((v - min) / step) * step + min);
461
+ }
462
+ return normalizedDefaultValue;
463
+ }, [normalizedDefaultValue, step, min]);
464
+ const sliderRef = useRef(null);
465
+ const thumb0Ref = useRef(null);
466
+ const thumb1Ref = useRef(null);
467
+ const input0Ref = useRef(null);
468
+ const input1Ref = useRef(null);
469
+ const isDragging = useRef(null);
470
+ const [internalValue, setInternalValue] = useState(
471
+ normalizeTuple(value, min, max)
472
+ );
473
+ const currentValue = useMemo(
474
+ () => value ? normalizeTuple(value, min, max) : internalValue,
475
+ [value, min, max, internalValue]
476
+ );
477
+ const currentStepValue = useMemo(() => {
478
+ if (step > 1) {
479
+ return currentValue.map((v) => Math.round(v / step) * step);
480
+ }
481
+ return currentValue;
482
+ }, [currentValue, step]);
483
+ const [transforms, setTransforms] = useState({
484
+ minTransform: 1,
485
+ maxTransform: 0,
486
+ transformX0: 0,
487
+ transformX1: 0
488
+ });
489
+ const trackWidth = useMemo(() => {
490
+ if ((trackSize == null ? void 0 : trackSize.width) === "auto") {
491
+ return actualTrackWidth;
492
+ }
493
+ return trackSize == null ? void 0 : trackSize.width;
494
+ }, [trackSize == null ? void 0 : trackSize.width, actualTrackWidth]);
495
+ useIsomorphicLayoutEffect(() => {
496
+ if ((trackSize == null ? void 0 : trackSize.width) === "auto" && sliderRef.current) {
497
+ const updateWidth = () => {
498
+ if (sliderRef.current) {
499
+ const width = sliderRef.current.getBoundingClientRect().width;
500
+ if (width > 0) {
501
+ setActualTrackWidth(width);
502
+ }
1028
503
  }
1029
- }
1030
- };
1031
- updateWidth();
1032
- const resizeObserver = new ResizeObserver(() => {
504
+ };
1033
505
  updateWidth();
506
+ const resizeObserver = new ResizeObserver(() => {
507
+ updateWidth();
508
+ });
509
+ resizeObserver.observe(sliderRef.current);
510
+ return () => {
511
+ resizeObserver.disconnect();
512
+ };
513
+ }
514
+ }, [trackSize == null ? void 0 : trackSize.width]);
515
+ useEffect(() => {
516
+ const position0 = valueToPosition(currentValue[0]);
517
+ const position1 = valueToPosition(currentValue[1]);
518
+ const minTransform = 1;
519
+ const maxTransform = (trackWidth ?? 0) - thumbSize - 1;
520
+ const transformX0 = minTransform + position0 * (maxTransform - minTransform);
521
+ const transformX1 = minTransform + position1 * (maxTransform - minTransform);
522
+ setTransforms({
523
+ minTransform,
524
+ maxTransform,
525
+ transformX0,
526
+ transformX1
1034
527
  });
1035
- resizeObserver.observe(sliderRef.current);
1036
- return () => {
1037
- resizeObserver.disconnect();
1038
- };
1039
- }
1040
- }, [propsWidth]);
1041
- const transforms = useMemo(() => {
1042
- const position0 = valueToPosition(currentValue[0]);
1043
- const position1 = valueToPosition(currentValue[1]);
1044
- const minTransform = 1;
1045
- const maxTransform = (trackWidth ?? 0) - thumbSize - 1;
1046
- const transformX0 = minTransform + position0 * (maxTransform - minTransform);
1047
- const transformX1 = minTransform + position1 * (maxTransform - minTransform);
1048
- return { minTransform, maxTransform, transformX0, transformX1 };
1049
- }, [currentValue, trackWidth, thumbSize, valueToPosition]);
1050
- const dotsData = useMemo(() => {
1051
- if (safeStep <= 1) return null;
1052
- return Array.from({ length: Math.ceil((max - min) / safeStep) + 1 }, (_, i) => {
1053
- const dotValue = min + i * safeStep;
1054
- const dotPosition = valueToPosition(dotValue);
1055
- return {
1056
- value: dotValue,
1057
- position: dotPosition
1058
- };
1059
- });
1060
- }, [safeStep, min, max, valueToPosition]);
1061
- const defaultDotPositions = useMemo(() => {
1062
- if (!normalizedDefaultValue || safeStep > 1) return null;
1063
- return normalizedDefaultValue.map((v) => valueToPosition(v));
1064
- }, [normalizedDefaultValue, safeStep, valueToPosition]);
1065
- const updatePosition = useEventCallback(
1066
- (clientX, thumbIndex, isEnd) => {
1067
- var _a;
1068
- if (readOnly) return;
1069
- const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
1070
- if (!rect) return;
1071
- const newPosition = clamp((clientX - rect.left) / rect.width, 0, 1);
1072
- const newValue = Math.round(positionToValue(newPosition) / safeStep) * safeStep;
1073
- let clampedValue = clamp(newValue, min, max);
1074
- if (normalizedDefaultValue && safeStep <= 1) {
1075
- const snapThreshold = (max - min) * 0.05;
1076
- for (const defVal of normalizedDefaultValue) {
1077
- const distanceToDefault = Math.abs(clampedValue - defVal);
1078
- if (distanceToDefault <= snapThreshold) {
1079
- clampedValue = defVal;
1080
- break;
528
+ }, [currentValue, trackWidth, thumbSize, valueToPosition]);
529
+ const dotsData = useMemo(() => {
530
+ if (!step || step <= 1) return null;
531
+ return Array.from({ length: Math.ceil((max - min) / step) + 1 }, (_, i) => {
532
+ const dotValue = min + i * step;
533
+ const dotPosition = valueToPosition(dotValue);
534
+ return {
535
+ value: dotValue,
536
+ position: dotPosition
537
+ };
538
+ });
539
+ }, [step, min, max, valueToPosition]);
540
+ const defaultDotPositions = useMemo(() => {
541
+ if (!normalizedDefaultValue || step > 1) return null;
542
+ return normalizedDefaultValue.map((v) => valueToPosition(v));
543
+ }, [normalizedDefaultValue, step, valueToPosition]);
544
+ const updatePosition = useEventCallback(
545
+ (clientX, thumbIndex, isEnd) => {
546
+ var _a;
547
+ if (readOnly) return;
548
+ const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
549
+ if (!rect) return;
550
+ const newPosition = clamp((clientX - rect.left) / rect.width, 0, 1);
551
+ const newValue = Math.round(positionToValue(newPosition) / step) * step;
552
+ let clampedValue = clamp(newValue, min, max);
553
+ if (normalizedDefaultValue && step === 1) {
554
+ const snapThreshold = (max - min) * 0.05;
555
+ for (const defVal of normalizedDefaultValue) {
556
+ const distanceToDefault = Math.abs(clampedValue - defVal);
557
+ if (distanceToDefault <= snapThreshold) {
558
+ clampedValue = defVal;
559
+ break;
560
+ }
561
+ }
562
+ }
563
+ const newTuple = [...currentValue];
564
+ newTuple[thumbIndex] = clampedValue;
565
+ if (newTuple[0] > newTuple[1]) {
566
+ if (thumbIndex === 0) {
567
+ newTuple[0] = newTuple[1];
568
+ } else {
569
+ newTuple[1] = newTuple[0];
1081
570
  }
1082
571
  }
572
+ if (isEnd) {
573
+ isDragging.current = null;
574
+ }
575
+ if (value === void 0) {
576
+ setInternalValue(newTuple);
577
+ }
578
+ onChange == null ? void 0 : onChange(newTuple);
579
+ }
580
+ );
581
+ useEffect(() => {
582
+ if (value !== void 0) {
583
+ setInternalValue(normalizeTuple(value, min, max));
584
+ }
585
+ }, [value, min, max]);
586
+ const handlePointerDown = useCallback(
587
+ (e, thumbIndex) => {
588
+ var _a;
589
+ if (disabled || readOnly) return;
590
+ e.preventDefault();
591
+ e.stopPropagation();
592
+ const thumb = thumbIndex === 0 ? thumb0Ref.current : thumb1Ref.current;
593
+ const inputRef = thumbIndex === 0 ? input0Ref : input1Ref;
594
+ if (!thumb) return;
595
+ onChangeStart == null ? void 0 : onChangeStart();
596
+ isDragging.current = thumbIndex;
597
+ thumb.setPointerCapture(e.pointerId);
598
+ updatePosition(e.clientX, thumbIndex);
599
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
600
+ const handleMove = (e2) => {
601
+ if (isDragging.current !== thumbIndex) return;
602
+ e2.preventDefault();
603
+ updatePosition(e2.clientX, thumbIndex);
604
+ };
605
+ const handleUp = (e2) => {
606
+ var _a2;
607
+ if (isDragging.current !== thumbIndex) return;
608
+ e2.preventDefault();
609
+ if (thumb.hasPointerCapture(e2.pointerId)) {
610
+ thumb.releasePointerCapture(e2.pointerId);
611
+ }
612
+ updatePosition(e2.clientX, thumbIndex, true);
613
+ isDragging.current = null;
614
+ const rect = (_a2 = sliderRef.current) == null ? void 0 : _a2.getBoundingClientRect();
615
+ if (rect) {
616
+ const newPosition = clamp((e2.clientX - rect.left) / rect.width, 0, 1);
617
+ const newValue = Math.round(positionToValue(newPosition) / step) * step;
618
+ let clampedValue = clamp(newValue, min, max);
619
+ if (normalizedDefaultValue && step === 1) {
620
+ const snapThreshold = (max - min) * 0.05;
621
+ for (const defVal of normalizedDefaultValue) {
622
+ const distanceToDefault = Math.abs(clampedValue - defVal);
623
+ if (distanceToDefault <= snapThreshold) {
624
+ clampedValue = defVal;
625
+ break;
626
+ }
627
+ }
628
+ }
629
+ const finalTuple = [...currentValue];
630
+ finalTuple[thumbIndex] = clampedValue;
631
+ if (finalTuple[0] > finalTuple[1]) {
632
+ if (thumbIndex === 0) {
633
+ finalTuple[0] = finalTuple[1];
634
+ } else {
635
+ finalTuple[1] = finalTuple[0];
636
+ }
637
+ }
638
+ onChangeEnd == null ? void 0 : onChangeEnd(finalTuple);
639
+ }
640
+ window.removeEventListener("pointermove", handleMove);
641
+ window.removeEventListener("pointerup", handleUp);
642
+ window.removeEventListener("pointercancel", handleUp);
643
+ };
644
+ window.addEventListener("pointermove", handleMove);
645
+ window.addEventListener("pointerup", handleUp);
646
+ window.addEventListener("pointercancel", handleUp);
647
+ },
648
+ [
649
+ disabled,
650
+ readOnly,
651
+ onChangeEnd,
652
+ onChangeStart,
653
+ updatePosition,
654
+ positionToValue,
655
+ step,
656
+ min,
657
+ max,
658
+ normalizedDefaultValue,
659
+ currentValue
660
+ ]
661
+ );
662
+ const handleSliderPointerDown = useCallback(
663
+ (e) => {
664
+ var _a;
665
+ if (disabled || readOnly) return;
666
+ if (e.target === thumb0Ref.current || e.target === thumb1Ref.current) return;
667
+ const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
668
+ if (!rect) return;
669
+ const clickPosition = (e.clientX - rect.left) / rect.width;
670
+ const clickValue = positionToValue(clickPosition);
671
+ const dist0 = Math.abs(clickValue - currentValue[0]);
672
+ const dist1 = Math.abs(clickValue - currentValue[1]);
673
+ const thumbIndex = dist0 <= dist1 ? 0 : 1;
674
+ handlePointerDown(e, thumbIndex);
675
+ },
676
+ [disabled, readOnly, handlePointerDown, currentValue, positionToValue]
677
+ );
678
+ const handleKeyDown = useEventCallback((e, thumbIndex) => {
679
+ if (disabled || readOnly) return;
680
+ const stepValue = e.shiftKey ? step * 10 : step;
681
+ let newValue = currentValue[thumbIndex];
682
+ switch (e.key) {
683
+ case "ArrowLeft":
684
+ case "ArrowDown":
685
+ e.preventDefault();
686
+ newValue = clamp(newValue - stepValue, min, max);
687
+ break;
688
+ case "ArrowRight":
689
+ case "ArrowUp":
690
+ e.preventDefault();
691
+ newValue = clamp(newValue + stepValue, min, max);
692
+ break;
693
+ default:
694
+ return;
1083
695
  }
1084
696
  const newTuple = [...currentValue];
1085
- newTuple[thumbIndex] = clampedValue;
697
+ newTuple[thumbIndex] = newValue;
1086
698
  if (newTuple[0] > newTuple[1]) {
1087
699
  if (thumbIndex === 0) {
1088
700
  newTuple[0] = newTuple[1];
@@ -1090,225 +702,185 @@ var RangeTupleRoot = forwardRef(function RangeTuple(props, ref) {
1090
702
  newTuple[1] = newTuple[0];
1091
703
  }
1092
704
  }
1093
- if (isEnd) {
1094
- isDragging.current = null;
1095
- }
1096
- if (value === void 0) {
1097
- setInternalValue(newTuple);
1098
- }
1099
705
  onChange == null ? void 0 : onChange(newTuple);
1100
- }
1101
- );
1102
- useEffect(() => {
1103
- if (value !== void 0) {
1104
- setInternalValue(normalizeTuple(value, min, max));
1105
- }
1106
- }, [value, min, max]);
1107
- const latestValueRef = useRef(currentValue);
1108
- latestValueRef.current = currentValue;
1109
- const handlePointerDown = useCallback(
1110
- (e, thumbIndex) => {
1111
- var _a;
1112
- if (disabled || readOnly) return;
1113
- e.preventDefault();
1114
- e.stopPropagation();
1115
- const thumb = thumbIndex === 0 ? thumb0Ref.current : thumb1Ref.current;
1116
- const inputRef = thumbIndex === 0 ? input0Ref : input1Ref;
1117
- if (!thumb) return;
1118
- onChangeStart == null ? void 0 : onChangeStart();
1119
- isDragging.current = thumbIndex;
1120
- thumb.setPointerCapture(e.pointerId);
1121
- updatePosition(e.clientX, thumbIndex);
1122
- (_a = inputRef.current) == null ? void 0 : _a.focus();
1123
- const handleMove = (e2) => {
1124
- if (isDragging.current !== thumbIndex) return;
1125
- e2.preventDefault();
1126
- updatePosition(e2.clientX, thumbIndex);
1127
- };
1128
- const cleanup = () => {
1129
- window.removeEventListener("pointermove", handleMove);
1130
- window.removeEventListener("pointerup", handleUp);
1131
- window.removeEventListener("pointercancel", handleUp);
1132
- cleanupRef.current = null;
1133
- };
1134
- const handleUp = (e2) => {
1135
- if (isDragging.current !== thumbIndex) return;
1136
- e2.preventDefault();
1137
- if (thumb.hasPointerCapture(e2.pointerId)) {
1138
- thumb.releasePointerCapture(e2.pointerId);
706
+ });
707
+ useEffect(() => {
708
+ var _a, _b;
709
+ if (disabled) {
710
+ if (document.activeElement === input0Ref.current) {
711
+ (_a = input0Ref.current) == null ? void 0 : _a.blur();
712
+ }
713
+ if (document.activeElement === input1Ref.current) {
714
+ (_b = input1Ref.current) == null ? void 0 : _b.blur();
1139
715
  }
1140
- updatePosition(e2.clientX, thumbIndex, true);
1141
- isDragging.current = null;
1142
- onChangeEnd == null ? void 0 : onChangeEnd(latestValueRef.current);
1143
- cleanup();
716
+ }
717
+ }, [disabled]);
718
+ const tv = useMemo(
719
+ () => rangeTv({
720
+ hasStepOrDefault: step > 1 || normalizedDefaultValue !== void 0,
721
+ disabled
722
+ }),
723
+ [step, normalizedDefaultValue, disabled]
724
+ );
725
+ const connectsClass = useMemo(() => {
726
+ if (disabled) return "bg-disabled-background";
727
+ return connectsClassName.positive;
728
+ }, [disabled, connectsClassName]);
729
+ const connectStyle = useMemo(() => {
730
+ return {
731
+ left: `${transforms.transformX0 + thumbSize / 2}px`,
732
+ right: `calc(100% - ${transforms.transformX1 + thumbSize / 2}px)`,
733
+ height: trackSize == null ? void 0 : trackSize.height
1144
734
  };
1145
- cleanupRef.current = cleanup;
1146
- window.addEventListener("pointermove", handleMove);
1147
- window.addEventListener("pointerup", handleUp);
1148
- window.addEventListener("pointercancel", handleUp);
1149
- },
1150
- [disabled, readOnly, onChangeEnd, onChangeStart, updatePosition]
1151
- );
1152
- useEffect(() => {
1153
- return () => {
1154
- var _a;
1155
- (_a = cleanupRef.current) == null ? void 0 : _a.call(cleanupRef);
1156
- };
1157
- }, []);
1158
- const handleSliderPointerDown = useCallback(
1159
- (e) => {
1160
- var _a;
1161
- if (disabled || readOnly) return;
1162
- if (e.target === thumb0Ref.current || e.target === thumb1Ref.current) return;
1163
- const rect = (_a = sliderRef.current) == null ? void 0 : _a.getBoundingClientRect();
1164
- if (!rect) return;
1165
- const clickPosition = (e.clientX - rect.left) / rect.width;
1166
- const clickValue = positionToValue(clickPosition);
1167
- const dist0 = Math.abs(clickValue - currentValue[0]);
1168
- const dist1 = Math.abs(clickValue - currentValue[1]);
1169
- const thumbIndex = dist0 <= dist1 ? 0 : 1;
1170
- handlePointerDown(e, thumbIndex);
1171
- },
1172
- [disabled, readOnly, handlePointerDown, currentValue, positionToValue]
1173
- );
1174
- const handleKeyDown = useEventCallback((e, thumbIndex) => {
1175
- if (disabled || readOnly) return;
1176
- const stepValue = e.shiftKey ? safeStep * 10 : safeStep;
1177
- let newValue = currentValue[thumbIndex];
1178
- switch (e.key) {
1179
- case "ArrowLeft":
1180
- case "ArrowDown":
1181
- e.preventDefault();
1182
- newValue = clamp(newValue - stepValue, min, max);
1183
- break;
1184
- case "ArrowRight":
1185
- case "ArrowUp":
1186
- e.preventDefault();
1187
- newValue = clamp(newValue + stepValue, min, max);
1188
- break;
1189
- default:
1190
- return;
1191
- }
1192
- const newTuple = [...currentValue];
1193
- newTuple[thumbIndex] = newValue;
1194
- if (newTuple[0] > newTuple[1]) {
1195
- if (thumbIndex === 0) {
1196
- newTuple[0] = newTuple[1];
1197
- } else {
1198
- newTuple[1] = newTuple[0];
735
+ }, [transforms.transformX0, transforms.transformX1, thumbSize, trackSize == null ? void 0 : trackSize.height]);
736
+ const renderDots = useCallback(() => {
737
+ if (dotsData) {
738
+ return dotsData.map(({ value: dotValue, position: dotPosition }) => {
739
+ const { minTransform, maxTransform } = transforms;
740
+ const dotTransform = minTransform + dotPosition * (maxTransform - minTransform);
741
+ const isWithinRange = dotValue >= currentValue[0] && dotValue <= currentValue[1];
742
+ const isDefaultValue = defaultStepValue == null ? void 0 : defaultStepValue.includes(dotValue);
743
+ const { dot } = rangeTv({
744
+ defaultStepValue: isDefaultValue,
745
+ overStepValue: isWithinRange
746
+ });
747
+ return /* @__PURE__ */ jsx(
748
+ "div",
749
+ {
750
+ className: dot(),
751
+ style: {
752
+ left: dotTransform + thumbSize / 2
753
+ }
754
+ },
755
+ dotValue
756
+ );
757
+ });
1199
758
  }
1200
- }
1201
- onChange == null ? void 0 : onChange(newTuple);
1202
- });
1203
- useEffect(() => {
1204
- var _a, _b;
1205
- if (disabled) {
1206
- if (document.activeElement === input0Ref.current) {
1207
- (_a = input0Ref.current) == null ? void 0 : _a.blur();
759
+ if (defaultDotPositions) {
760
+ return defaultDotPositions.map((position, idx) => /* @__PURE__ */ jsx(
761
+ "div",
762
+ {
763
+ className: rangeTv({ defaultStepValue: true }).dot(),
764
+ style: {
765
+ left: transforms.minTransform + position * (transforms.maxTransform - transforms.minTransform) + thumbSize / 2
766
+ }
767
+ },
768
+ `default-${idx}`
769
+ ));
1208
770
  }
1209
- if (document.activeElement === input1Ref.current) {
1210
- (_b = input1Ref.current) == null ? void 0 : _b.blur();
771
+ return null;
772
+ }, [dotsData, defaultDotPositions, defaultStepValue, transforms, thumbSize, currentValue]);
773
+ useEffect(() => {
774
+ const noop = () => {
775
+ };
776
+ return () => {
777
+ if (typeof window !== "undefined") {
778
+ window.removeEventListener("pointermove", noop);
779
+ window.removeEventListener("pointerup", noop);
780
+ window.removeEventListener("pointercancel", noop);
781
+ }
782
+ };
783
+ }, []);
784
+ const thumb0IsDefault = useMemo(() => {
785
+ if (!defaultStepValue) return false;
786
+ return currentStepValue[0] === defaultStepValue[0];
787
+ }, [currentStepValue, defaultStepValue]);
788
+ const thumb1IsDefault = useMemo(() => {
789
+ if (!defaultStepValue) return false;
790
+ return currentStepValue[1] === defaultStepValue[1];
791
+ }, [currentStepValue, defaultStepValue]);
792
+ const thumbTv0 = useMemo(
793
+ () => rangeTv({
794
+ currentDefaultValue: thumb0IsDefault,
795
+ hasStepOrDefault: step > 1 || normalizedDefaultValue !== void 0,
796
+ disabled
797
+ }),
798
+ [thumb0IsDefault, step, normalizedDefaultValue, disabled]
799
+ );
800
+ const thumbTv1 = useMemo(
801
+ () => rangeTv({
802
+ currentDefaultValue: thumb1IsDefault,
803
+ hasStepOrDefault: step > 1 || normalizedDefaultValue !== void 0,
804
+ disabled
805
+ }),
806
+ [thumb1IsDefault, step, normalizedDefaultValue, disabled]
807
+ );
808
+ return /* @__PURE__ */ jsxs(
809
+ "div",
810
+ {
811
+ ref: mergeRefs(sliderRef, ref),
812
+ onPointerDown: handleSliderPointerDown,
813
+ className: tcx(tv.container(), className),
814
+ style: {
815
+ "--width": `${trackWidth}px`,
816
+ "--height": `${(trackSize == null ? void 0 : trackSize.height) ?? 16}px`
817
+ },
818
+ children: [
819
+ /* @__PURE__ */ jsx(
820
+ "div",
821
+ {
822
+ className: tcx(tv.connect(), connectsClass),
823
+ style: connectStyle
824
+ }
825
+ ),
826
+ (step > 1 || normalizedDefaultValue !== void 0) && /* @__PURE__ */ jsx("div", { className: tv.dotContainer(), children: renderDots() }),
827
+ /* @__PURE__ */ jsx(
828
+ "div",
829
+ {
830
+ ref: thumb0Ref,
831
+ onPointerDown: (e) => handlePointerDown(e, 0),
832
+ className: thumbTv0.thumb(),
833
+ style: {
834
+ width: thumbSize,
835
+ height: thumbSize,
836
+ transform: `translate(${transforms.transformX0}px, -50%)`,
837
+ willChange: isDragging.current === 0 ? "transform" : "auto"
838
+ },
839
+ children: /* @__PURE__ */ jsx(
840
+ "input",
841
+ {
842
+ ref: input0Ref,
843
+ type: "text",
844
+ onKeyDown: (e) => handleKeyDown(e, 0),
845
+ className: tv.input(),
846
+ tabIndex: disabled || readOnly ? -1 : 0,
847
+ readOnly: true
848
+ }
849
+ )
850
+ }
851
+ ),
852
+ /* @__PURE__ */ jsx(
853
+ "div",
854
+ {
855
+ ref: thumb1Ref,
856
+ onPointerDown: (e) => handlePointerDown(e, 1),
857
+ className: thumbTv1.thumb(),
858
+ style: {
859
+ width: thumbSize,
860
+ height: thumbSize,
861
+ transform: `translate(${transforms.transformX1}px, -50%)`,
862
+ willChange: isDragging.current === 1 ? "transform" : "auto"
863
+ },
864
+ children: /* @__PURE__ */ jsx(
865
+ "input",
866
+ {
867
+ ref: input1Ref,
868
+ type: "text",
869
+ onKeyDown: (e) => handleKeyDown(e, 1),
870
+ className: tv.input(),
871
+ tabIndex: disabled || readOnly ? -1 : 0,
872
+ readOnly: true
873
+ }
874
+ )
875
+ }
876
+ )
877
+ ]
1211
878
  }
1212
- }
1213
- }, [disabled]);
1214
- const hasStepOrDefault = safeStep > 1 || normalizedDefaultValue !== void 0;
1215
- const tv = useMemo(() => rangeTv({ hasStepOrDefault, disabled }), [hasStepOrDefault, disabled]);
1216
- const thumb0IsDefault = defaultStepValue ? currentStepValue[0] === defaultStepValue[0] : false;
1217
- const thumb1IsDefault = defaultStepValue ? currentStepValue[1] === defaultStepValue[1] : false;
1218
- const thumbTv0 = useMemo(
1219
- () => rangeTv({ currentDefaultValue: thumb0IsDefault, hasStepOrDefault, disabled }),
1220
- [thumb0IsDefault, hasStepOrDefault, disabled]
1221
- );
1222
- const thumbTv1 = useMemo(
1223
- () => rangeTv({ currentDefaultValue: thumb1IsDefault, hasStepOrDefault, disabled }),
1224
- [thumb1IsDefault, hasStepOrDefault, disabled]
1225
- );
1226
- const contextValue = useMemo(
1227
- () => ({
1228
- currentValue,
1229
- disabled,
1230
- readOnly,
1231
- min,
1232
- max,
1233
- step: safeStep,
1234
- thumbSize,
1235
- trackHeight,
1236
- transforms,
1237
- defaultStepValue,
1238
- currentStepValue,
1239
- dotsData,
1240
- defaultDotPositions,
1241
- normalizedDefaultValue,
1242
- thumb0Ref,
1243
- thumb1Ref,
1244
- input0Ref,
1245
- input1Ref,
1246
- isDragging,
1247
- handlePointerDown,
1248
- handleKeyDown,
1249
- tv,
1250
- thumbTv0,
1251
- thumbTv1,
1252
- hasCustomDot,
1253
- hasCustomConnects,
1254
- isDefaultValue: thumb0IsDefault && thumb1IsDefault
1255
- }),
1256
- [
1257
- currentValue,
1258
- disabled,
1259
- readOnly,
1260
- min,
1261
- max,
1262
- safeStep,
1263
- thumbSize,
1264
- trackHeight,
1265
- transforms,
1266
- defaultStepValue,
1267
- currentStepValue,
1268
- dotsData,
1269
- defaultDotPositions,
1270
- normalizedDefaultValue,
1271
- handlePointerDown,
1272
- handleKeyDown,
1273
- tv,
1274
- thumbTv0,
1275
- thumbTv1,
1276
- hasCustomDot,
1277
- hasCustomConnects,
1278
- thumb0IsDefault,
1279
- thumb1IsDefault
1280
- ]
1281
- );
1282
- return /* @__PURE__ */ jsx(RangeTupleContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
1283
- "div",
1284
- {
1285
- ref: mergeRefs(sliderRef, ref),
1286
- onPointerDown: handleSliderPointerDown,
1287
- className: tcx(tv.container(), className),
1288
- style: {
1289
- "--width": `${trackWidth}px`,
1290
- "--height": `${trackHeight}px`,
1291
- "--thumb-size": `${thumbSize}px`
1292
- },
1293
- children: hasCustomChildren ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
1294
- /* @__PURE__ */ jsx(RangeTupleContainer, {}),
1295
- /* @__PURE__ */ jsx(RangeTupleThumb, { index: 0 }),
1296
- /* @__PURE__ */ jsx(RangeTupleThumb, { index: 1 })
1297
- ] })
1298
- }
1299
- ) });
1300
- });
1301
- RangeTupleRoot.displayName = "RangeTuple";
1302
- Object.assign(RangeTupleRoot, {
1303
- Container: RangeTupleContainer,
1304
- Connects: RangeTupleConnects,
1305
- Thumb: RangeTupleThumb,
1306
- Dot: RangeTupleDot
1307
- });
879
+ );
880
+ }
881
+ );
882
+ RangeTuple.displayName = "RangeTuple";
1308
883
  export {
1309
- Range2 as Range,
1310
- RangeContext,
1311
- RangeTupleContext,
1312
- useRangeContext,
1313
- useRangeTupleContext
884
+ Range,
885
+ RangeTuple
1314
886
  };