@underverse-ui/underverse 0.2.107 → 0.2.108

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.
package/dist/index.cjs CHANGED
@@ -7110,6 +7110,14 @@ function WheelColumn({
7110
7110
  const wheelDeltaRef = React25.useRef(0);
7111
7111
  const scrollEndTimeoutRef = React25.useRef(null);
7112
7112
  const suppressScrollSelectUntilRef = React25.useRef(0);
7113
+ const suppressItemClickUntilRef = React25.useRef(0);
7114
+ const dragRef = React25.useRef(null);
7115
+ const draggingRef = React25.useRef(false);
7116
+ const inertialRef = React25.useRef(false);
7117
+ const inertiaRafRef = React25.useRef(null);
7118
+ const inertiaVelocityRef = React25.useRef(0);
7119
+ const inertiaLastTimeRef = React25.useRef(0);
7120
+ const moveSamplesRef = React25.useRef([]);
7113
7121
  const loop = true;
7114
7122
  const ui = React25.useMemo(() => {
7115
7123
  if (size === "sm") {
@@ -7180,6 +7188,10 @@ function WheelColumn({
7180
7188
  window.clearTimeout(scrollEndTimeoutRef.current);
7181
7189
  scrollEndTimeoutRef.current = null;
7182
7190
  }
7191
+ if (inertiaRafRef.current != null) {
7192
+ cancelAnimationFrame(inertiaRafRef.current);
7193
+ inertiaRafRef.current = null;
7194
+ }
7183
7195
  cancelAnimationFrame(rafRef.current);
7184
7196
  };
7185
7197
  }, [animate, baseOffset, extendedItems.length, getNearestVirtualIndex, itemHeight, loop, scrollRef, valueIndex]);
@@ -7277,6 +7289,138 @@ function WheelColumn({
7277
7289
  const from = lastVirtualIndexRef.current ?? fallback;
7278
7290
  return getNearestVirtualIndex(valueIndex, from);
7279
7291
  }, [baseOffset, getNearestVirtualIndex, items.length, loop, valueIndex]);
7292
+ const commitFromScrollTop = React25.useCallback(
7293
+ (behavior) => {
7294
+ const el = scrollRef.current;
7295
+ if (!el) return;
7296
+ if (items.length <= 0) return;
7297
+ const len = items.length;
7298
+ const maxVirtual = Math.max(0, extendedItems.length - 1);
7299
+ const idxVirtual = clamp3(Math.round(el.scrollTop / itemHeight), 0, maxVirtual);
7300
+ const realIndex = (idxVirtual % len + len) % len;
7301
+ const snappedVirtual = loop ? getNearestVirtualIndex(realIndex, idxVirtual) : realIndex;
7302
+ lastVirtualIndexRef.current = snappedVirtual;
7303
+ suppressScrollSelectUntilRef.current = Date.now() + 350;
7304
+ if (behavior === "auto") el.scrollTop = snappedVirtual * itemHeight;
7305
+ else el.scrollTo({ top: snappedVirtual * itemHeight, behavior });
7306
+ onSelect(items[realIndex]);
7307
+ if (loop) {
7308
+ const min = len;
7309
+ const max = len * 2 - 1;
7310
+ let centered = snappedVirtual;
7311
+ if (centered < min) centered += len;
7312
+ if (centered > max) centered -= len;
7313
+ if (centered !== snappedVirtual) {
7314
+ lastVirtualIndexRef.current = centered;
7315
+ el.scrollTop = centered * itemHeight;
7316
+ }
7317
+ }
7318
+ },
7319
+ [extendedItems.length, getNearestVirtualIndex, itemHeight, items, loop, onSelect, scrollRef]
7320
+ );
7321
+ const onPointerDown = (e) => {
7322
+ if (e.pointerType !== "mouse") return;
7323
+ if (e.button !== 0) return;
7324
+ const el = scrollRef.current;
7325
+ if (!el) return;
7326
+ e.preventDefault();
7327
+ setFocusedColumn(column);
7328
+ draggingRef.current = true;
7329
+ inertialRef.current = false;
7330
+ if (inertiaRafRef.current != null) {
7331
+ cancelAnimationFrame(inertiaRafRef.current);
7332
+ inertiaRafRef.current = null;
7333
+ }
7334
+ if (scrollEndTimeoutRef.current != null) {
7335
+ window.clearTimeout(scrollEndTimeoutRef.current);
7336
+ scrollEndTimeoutRef.current = null;
7337
+ }
7338
+ dragRef.current = { pointerId: e.pointerId, startY: e.clientY, startScrollTop: el.scrollTop, moved: false };
7339
+ moveSamplesRef.current = [{ t: performance.now(), top: el.scrollTop }];
7340
+ try {
7341
+ el.setPointerCapture(e.pointerId);
7342
+ } catch {
7343
+ }
7344
+ };
7345
+ const onPointerMove = (e) => {
7346
+ const el = scrollRef.current;
7347
+ const drag = dragRef.current;
7348
+ if (!el || !drag) return;
7349
+ if (e.pointerId !== drag.pointerId) return;
7350
+ e.preventDefault();
7351
+ const dy = e.clientY - drag.startY;
7352
+ if (!drag.moved && Math.abs(dy) < 4) return;
7353
+ if (!drag.moved) {
7354
+ drag.moved = true;
7355
+ suppressItemClickUntilRef.current = Date.now() + 400;
7356
+ }
7357
+ suppressScrollSelectUntilRef.current = Date.now() + 500;
7358
+ el.scrollTop = drag.startScrollTop - dy;
7359
+ const now = performance.now();
7360
+ const samples = moveSamplesRef.current;
7361
+ samples.push({ t: now, top: el.scrollTop });
7362
+ while (samples.length > 6 && samples[0] && now - samples[0].t > 120) samples.shift();
7363
+ while (samples.length > 8) samples.shift();
7364
+ if (samples.length >= 2) {
7365
+ const oldest = samples[0];
7366
+ const dt = now - oldest.t;
7367
+ if (dt > 0) inertiaVelocityRef.current = (el.scrollTop - oldest.top) / dt;
7368
+ }
7369
+ };
7370
+ const startInertia = React25.useCallback(() => {
7371
+ const el = scrollRef.current;
7372
+ if (!el) return;
7373
+ if (items.length <= 0) return;
7374
+ inertialRef.current = true;
7375
+ suppressItemClickUntilRef.current = Date.now() + 600;
7376
+ inertiaLastTimeRef.current = performance.now();
7377
+ const len = items.length;
7378
+ const cycle = len * itemHeight;
7379
+ const frictionPerFrame = 0.92;
7380
+ const tick = () => {
7381
+ const now = performance.now();
7382
+ const last = inertiaLastTimeRef.current || now;
7383
+ const dt = Math.min(48, Math.max(0, now - last));
7384
+ inertiaLastTimeRef.current = now;
7385
+ let v = inertiaVelocityRef.current;
7386
+ el.scrollTop += v * dt;
7387
+ if (loop && cycle > 0) {
7388
+ if (el.scrollTop < cycle * 0.5) el.scrollTop += cycle;
7389
+ else if (el.scrollTop > cycle * 2.5) el.scrollTop -= cycle;
7390
+ }
7391
+ const decay = Math.pow(frictionPerFrame, dt / 16);
7392
+ v *= decay;
7393
+ inertiaVelocityRef.current = v;
7394
+ if (Math.abs(v) < 0.03) {
7395
+ inertialRef.current = false;
7396
+ inertiaRafRef.current = null;
7397
+ commitFromScrollTop("smooth");
7398
+ return;
7399
+ }
7400
+ inertiaRafRef.current = requestAnimationFrame(tick);
7401
+ };
7402
+ inertiaRafRef.current = requestAnimationFrame(tick);
7403
+ }, [commitFromScrollTop, itemHeight, items.length, loop, scrollRef]);
7404
+ const endDrag = (pointerId) => {
7405
+ const el = scrollRef.current;
7406
+ const drag = dragRef.current;
7407
+ if (!el || !drag) return;
7408
+ if (pointerId !== drag.pointerId) return;
7409
+ dragRef.current = null;
7410
+ draggingRef.current = false;
7411
+ try {
7412
+ el.releasePointerCapture(pointerId);
7413
+ } catch {
7414
+ }
7415
+ const v = inertiaVelocityRef.current;
7416
+ const shouldFlick = drag.moved && Math.abs(v) >= 0.35;
7417
+ if (shouldFlick) {
7418
+ startInertia();
7419
+ } else {
7420
+ inertialRef.current = false;
7421
+ commitFromScrollTop("smooth");
7422
+ }
7423
+ };
7280
7424
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: cn("flex-1", ui.columnWidth), children: [
7281
7425
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn(ui.label, "font-bold uppercase tracking-wider text-muted-foreground/70 text-center"), children: labelText }),
7282
7426
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "relative rounded-xl bg-muted/30 overflow-hidden", style: { height }, children: [
@@ -7296,6 +7440,7 @@ function WheelColumn({
7296
7440
  className: cn(
7297
7441
  "h-full overflow-y-auto overscroll-contain snap-y snap-mandatory",
7298
7442
  "scrollbar-none",
7443
+ "select-none cursor-grab active:cursor-grabbing",
7299
7444
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-xl"
7300
7445
  ),
7301
7446
  style: { paddingTop: paddingY, paddingBottom: paddingY },
@@ -7304,7 +7449,15 @@ function WheelColumn({
7304
7449
  tabIndex: focused ? 0 : -1,
7305
7450
  onKeyDown: (e) => onKeyDown(e, column),
7306
7451
  onFocus: () => setFocusedColumn(column),
7307
- onScroll: handleScroll,
7452
+ onScroll: () => {
7453
+ if (draggingRef.current) return;
7454
+ if (inertialRef.current) return;
7455
+ handleScroll();
7456
+ },
7457
+ onPointerDown,
7458
+ onPointerMove,
7459
+ onPointerUp: (e) => endDrag(e.pointerId),
7460
+ onPointerCancel: (e) => endDrag(e.pointerId),
7308
7461
  children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: extendedItems.map((n, index) => {
7309
7462
  const dist = Math.abs(index - currentVirtual);
7310
7463
  const distForVisual = Math.min(dist, 2);
@@ -7329,6 +7482,7 @@ function WheelColumn({
7329
7482
  opacity
7330
7483
  },
7331
7484
  onClick: () => {
7485
+ if (Date.now() < suppressItemClickUntilRef.current) return;
7332
7486
  const el = scrollRef.current;
7333
7487
  if (!el) return;
7334
7488
  suppressScrollSelectUntilRef.current = Date.now() + 350;
@@ -11320,16 +11474,22 @@ var SIZE_STYLES = {
11320
11474
  container: "py-3"
11321
11475
  }
11322
11476
  };
11477
+ var clamp5 = (n, min, max) => Math.min(max, Math.max(min, n));
11323
11478
  var Slider = React34.forwardRef(
11324
11479
  ({
11325
11480
  className,
11481
+ mode = "single",
11326
11482
  value,
11327
11483
  defaultValue = 0,
11484
+ rangeValue,
11485
+ defaultRangeValue,
11328
11486
  min = 0,
11329
11487
  max = 100,
11330
11488
  step = 1,
11331
11489
  onChange,
11332
11490
  onValueChange,
11491
+ onRangeChange,
11492
+ onRangeValueChange,
11333
11493
  onMouseUp,
11334
11494
  onTouchEnd,
11335
11495
  label,
@@ -11346,20 +11506,125 @@ var Slider = React34.forwardRef(
11346
11506
  noFocus = true,
11347
11507
  ...props
11348
11508
  }, ref) => {
11509
+ const isRange = mode === "range";
11510
+ const trackRef = React34.useRef(null);
11349
11511
  const [internalValue, setInternalValue] = React34.useState(defaultValue);
11512
+ const [internalRange, setInternalRange] = React34.useState(() => {
11513
+ if (defaultRangeValue) return defaultRangeValue;
11514
+ const v = clamp5(defaultValue, min, max);
11515
+ return [min, v];
11516
+ });
11517
+ const [activeThumb, setActiveThumb] = React34.useState(null);
11518
+ const dragRef = React34.useRef(null);
11350
11519
  const isControlled = value !== void 0;
11351
11520
  const currentValue = isControlled ? value : internalValue;
11352
- const handleChange = (e) => {
11353
- const newValue = Number(e.target.value);
11354
- if (!isControlled) {
11355
- setInternalValue(newValue);
11521
+ const isRangeControlled = rangeValue !== void 0;
11522
+ const currentRange = isRangeControlled ? rangeValue : internalRange;
11523
+ const rangeMin = clamp5(currentRange[0] ?? min, min, max);
11524
+ const rangeMax = clamp5(currentRange[1] ?? max, min, max);
11525
+ const normalizedRange = rangeMin <= rangeMax ? [rangeMin, rangeMax] : [rangeMax, rangeMin];
11526
+ const handleSingleChange = React34.useCallback(
11527
+ (e) => {
11528
+ const newValue = Number(e.target.value);
11529
+ if (!isControlled) {
11530
+ setInternalValue(newValue);
11531
+ }
11532
+ onChange?.(newValue);
11533
+ onValueChange?.(newValue);
11534
+ },
11535
+ [isControlled, onChange, onValueChange]
11536
+ );
11537
+ const emitRange = React34.useCallback(
11538
+ (next) => {
11539
+ onRangeChange?.(next);
11540
+ onRangeValueChange?.(next);
11541
+ },
11542
+ [onRangeChange, onRangeValueChange]
11543
+ );
11544
+ const handleRangeChange = React34.useCallback(
11545
+ (thumb) => (e) => {
11546
+ const nextVal = Number(e.target.value);
11547
+ const [curMin, curMax] = normalizedRange;
11548
+ const next = thumb === "min" ? [Math.min(nextVal, curMax), curMax] : [curMin, Math.max(nextVal, curMin)];
11549
+ if (!isRangeControlled) setInternalRange(next);
11550
+ emitRange(next);
11551
+ },
11552
+ [emitRange, isRangeControlled, normalizedRange]
11553
+ );
11554
+ const denom = Math.max(1e-9, max - min);
11555
+ const percentage = (currentValue - min) / denom * 100;
11556
+ const rangeStartPct = (normalizedRange[0] - min) / denom * 100;
11557
+ const rangeEndPct = (normalizedRange[1] - min) / denom * 100;
11558
+ const sizeStyles8 = SIZE_STYLES[size];
11559
+ const displayValue = React34.useMemo(() => {
11560
+ if (isRange) {
11561
+ const a = formatValue ? formatValue(normalizedRange[0]) : normalizedRange[0].toString();
11562
+ const b = formatValue ? formatValue(normalizedRange[1]) : normalizedRange[1].toString();
11563
+ return `${a} \u2013 ${b}`;
11564
+ }
11565
+ return formatValue ? formatValue(currentValue) : currentValue.toString();
11566
+ }, [currentValue, formatValue, isRange, normalizedRange]);
11567
+ const quantize = React34.useCallback(
11568
+ (v) => {
11569
+ const stepped = Math.round((v - min) / step) * step + min;
11570
+ const fixed = Number(stepped.toFixed(10));
11571
+ return clamp5(fixed, min, max);
11572
+ },
11573
+ [max, min, step]
11574
+ );
11575
+ const valueFromClientX = React34.useCallback(
11576
+ (clientX) => {
11577
+ const el = trackRef.current;
11578
+ if (!el) return min;
11579
+ const rect = el.getBoundingClientRect();
11580
+ const x = clamp5(clientX - rect.left, 0, rect.width);
11581
+ const ratio = rect.width <= 0 ? 0 : x / rect.width;
11582
+ return quantize(min + ratio * (max - min));
11583
+ },
11584
+ [max, min, quantize]
11585
+ );
11586
+ const startRangeDrag = (e) => {
11587
+ if (!isRange) return;
11588
+ if (disabled) return;
11589
+ if (orientation !== "horizontal") return;
11590
+ if (e.button !== 0) return;
11591
+ const nextValue = valueFromClientX(e.clientX);
11592
+ const [curMin, curMax] = normalizedRange;
11593
+ const distToMin = Math.abs(nextValue - curMin);
11594
+ const distToMax = Math.abs(nextValue - curMax);
11595
+ const thumb = distToMin <= distToMax ? "min" : "max";
11596
+ setActiveThumb(thumb);
11597
+ dragRef.current = { pointerId: e.pointerId, thumb };
11598
+ try {
11599
+ e.currentTarget.setPointerCapture(e.pointerId);
11600
+ } catch {
11601
+ }
11602
+ const next = thumb === "min" ? [Math.min(nextValue, curMax), curMax] : [curMin, Math.max(nextValue, curMin)];
11603
+ if (!isRangeControlled) setInternalRange(next);
11604
+ emitRange(next);
11605
+ };
11606
+ const moveRangeDrag = (e) => {
11607
+ const drag = dragRef.current;
11608
+ if (!drag) return;
11609
+ if (e.pointerId !== drag.pointerId) return;
11610
+ const nextValue = valueFromClientX(e.clientX);
11611
+ const [curMin, curMax] = normalizedRange;
11612
+ const next = drag.thumb === "min" ? [Math.min(nextValue, curMax), curMax] : [curMin, Math.max(nextValue, curMin)];
11613
+ if (!isRangeControlled) setInternalRange(next);
11614
+ emitRange(next);
11615
+ };
11616
+ const endRangeDrag = (e) => {
11617
+ const drag = dragRef.current;
11618
+ if (!drag) return;
11619
+ if (e.pointerId !== drag.pointerId) return;
11620
+ dragRef.current = null;
11621
+ onMouseUp?.();
11622
+ onTouchEnd?.();
11623
+ try {
11624
+ e.currentTarget.releasePointerCapture(e.pointerId);
11625
+ } catch {
11356
11626
  }
11357
- onChange?.(newValue);
11358
- onValueChange?.(newValue);
11359
11627
  };
11360
- const percentage = (currentValue - min) / (max - min) * 100;
11361
- const sizeStyles8 = SIZE_STYLES[size];
11362
- const displayValue = formatValue ? formatValue(currentValue) : currentValue.toString();
11363
11628
  if (orientation === "vertical") {
11364
11629
  }
11365
11630
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cn("w-full space-y-2", containerClassName), children: [
@@ -11367,65 +11632,129 @@ var Slider = React34.forwardRef(
11367
11632
  label && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: cn("text-sm font-medium text-foreground", labelClassName), children: label }),
11368
11633
  showValue && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: cn("text-xs font-mono text-muted-foreground min-w-8 text-right", valueClassName), children: displayValue })
11369
11634
  ] }),
11370
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cn("relative flex items-center", sizeStyles8.container), children: [
11371
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: cn("w-full rounded-full bg-secondary relative overflow-hidden", sizeStyles8.track, trackClassName), children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "absolute left-0 top-0 h-full bg-primary rounded-full", style: { width: `${percentage}%` } }) }),
11372
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11373
- "input",
11635
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { ref: trackRef, className: cn("relative flex items-center", sizeStyles8.container), children: [
11636
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: cn("w-full rounded-full bg-secondary relative overflow-hidden", sizeStyles8.track, trackClassName), children: isRange ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11637
+ "div",
11374
11638
  {
11375
- ref,
11376
- type: "range",
11377
- min,
11378
- max,
11379
- step,
11380
- value: currentValue,
11381
- onChange: handleChange,
11382
- onMouseUp,
11383
- onTouchEnd,
11384
- disabled,
11385
- className: cn(
11386
- // Base styles
11387
- "absolute w-full h-full appearance-none bg-transparent cursor-pointer",
11388
- !noFocus && "focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-background rounded-full",
11389
- noFocus && "outline-none ring-0 focus:outline-none focus:ring-0 focus-visible:outline-none",
11390
- // Webkit styles for thumb
11391
- "[&::-webkit-slider-thumb]:appearance-none",
11392
- "[&::-webkit-slider-thumb]:bg-primary",
11393
- "[&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background",
11394
- "[&::-webkit-slider-thumb]:rounded-full",
11395
- "[&::-webkit-slider-thumb]:shadow-md",
11396
- "[&::-webkit-slider-thumb]:cursor-pointer",
11397
- "[&::-webkit-slider-thumb]:transition-all [&::-webkit-slider-thumb]:duration-150",
11398
- size === "sm" && "[&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3",
11399
- size === "md" && "[&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4",
11400
- size === "lg" && "[&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5",
11401
- // Firefox styles for thumb
11402
- "[&::-moz-range-thumb]:bg-primary",
11403
- "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background",
11404
- "[&::-moz-range-thumb]:rounded-full",
11405
- "[&::-moz-range-thumb]:shadow-md",
11406
- "[&::-moz-range-thumb]:cursor-pointer",
11407
- "[&::-moz-range-thumb]:transition-all [&::-moz-range-thumb]:duration-150",
11408
- size === "sm" && "[&::-moz-range-thumb]:w-3 [&::-moz-range-thumb]:h-3",
11409
- size === "md" && "[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4",
11410
- size === "lg" && "[&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5",
11411
- // Remove default track in Firefox
11412
- "[&::-moz-range-track]:bg-transparent",
11413
- "[&::-moz-range-track]:border-transparent",
11414
- // Hover effects
11415
- "hover:[&::-webkit-slider-thumb]:scale-110 hover:[&::-webkit-slider-thumb]:shadow-lg",
11416
- "hover:[&::-moz-range-thumb]:scale-110 hover:[&::-moz-range-thumb]:shadow-lg",
11417
- // Disabled styles
11418
- disabled && [
11419
- "cursor-not-allowed opacity-50",
11420
- "[&::-webkit-slider-thumb]:cursor-not-allowed [&::-webkit-slider-thumb]:opacity-50",
11421
- "[&::-moz-range-thumb]:cursor-not-allowed [&::-moz-range-thumb]:opacity-50"
11422
- ],
11423
- className,
11424
- thumbClassName
11425
- ),
11426
- ...props
11639
+ className: "absolute top-0 h-full bg-primary rounded-full",
11640
+ style: { left: `${rangeStartPct}%`, width: `${Math.max(0, rangeEndPct - rangeStartPct)}%` }
11427
11641
  }
11428
- )
11642
+ ) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "absolute left-0 top-0 h-full bg-primary rounded-full", style: { width: `${percentage}%` } }) }),
11643
+ (() => {
11644
+ const baseInputClassName = cn(
11645
+ // Base styles
11646
+ "absolute w-full h-full appearance-none bg-transparent cursor-pointer",
11647
+ !noFocus && "focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-background rounded-full",
11648
+ noFocus && "outline-none ring-0 focus:outline-none focus:ring-0 focus-visible:outline-none",
11649
+ // Webkit styles for thumb
11650
+ "[&::-webkit-slider-thumb]:appearance-none",
11651
+ "[&::-webkit-slider-thumb]:bg-primary",
11652
+ "[&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background",
11653
+ "[&::-webkit-slider-thumb]:rounded-full",
11654
+ "[&::-webkit-slider-thumb]:shadow-md",
11655
+ "[&::-webkit-slider-thumb]:cursor-pointer",
11656
+ "[&::-webkit-slider-thumb]:transition-all [&::-webkit-slider-thumb]:duration-150",
11657
+ size === "sm" && "[&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3",
11658
+ size === "md" && "[&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4",
11659
+ size === "lg" && "[&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5",
11660
+ // Firefox styles for thumb
11661
+ "[&::-moz-range-thumb]:bg-primary",
11662
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background",
11663
+ "[&::-moz-range-thumb]:rounded-full",
11664
+ "[&::-moz-range-thumb]:shadow-md",
11665
+ "[&::-moz-range-thumb]:cursor-pointer",
11666
+ "[&::-moz-range-thumb]:transition-all [&::-moz-range-thumb]:duration-150",
11667
+ size === "sm" && "[&::-moz-range-thumb]:w-3 [&::-moz-range-thumb]:h-3",
11668
+ size === "md" && "[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4",
11669
+ size === "lg" && "[&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5",
11670
+ // Remove default track in Firefox
11671
+ "[&::-moz-range-track]:bg-transparent",
11672
+ "[&::-moz-range-track]:border-transparent",
11673
+ // Hover effects
11674
+ "hover:[&::-webkit-slider-thumb]:scale-110 hover:[&::-webkit-slider-thumb]:shadow-lg",
11675
+ "hover:[&::-moz-range-thumb]:scale-110 hover:[&::-moz-range-thumb]:shadow-lg",
11676
+ // Disabled styles
11677
+ disabled && [
11678
+ "cursor-not-allowed opacity-50",
11679
+ "[&::-webkit-slider-thumb]:cursor-not-allowed [&::-webkit-slider-thumb]:opacity-50",
11680
+ "[&::-moz-range-thumb]:cursor-not-allowed [&::-moz-range-thumb]:opacity-50"
11681
+ ],
11682
+ className,
11683
+ thumbClassName
11684
+ );
11685
+ if (!isRange) {
11686
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11687
+ "input",
11688
+ {
11689
+ ref,
11690
+ type: "range",
11691
+ min,
11692
+ max,
11693
+ step,
11694
+ value: currentValue,
11695
+ onChange: handleSingleChange,
11696
+ onMouseUp,
11697
+ onTouchEnd,
11698
+ disabled,
11699
+ className: baseInputClassName,
11700
+ ...props
11701
+ }
11702
+ );
11703
+ }
11704
+ const minZ = activeThumb === "min" ? "z-20" : "z-10";
11705
+ const maxZ = activeThumb === "max" ? "z-20" : "z-10";
11706
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
11707
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11708
+ "div",
11709
+ {
11710
+ className: cn("absolute inset-0 z-30", disabled ? "cursor-not-allowed" : "cursor-pointer"),
11711
+ onPointerDown: startRangeDrag,
11712
+ onPointerMove: moveRangeDrag,
11713
+ onPointerUp: endRangeDrag,
11714
+ onPointerCancel: endRangeDrag
11715
+ }
11716
+ ),
11717
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11718
+ "input",
11719
+ {
11720
+ ref,
11721
+ type: "range",
11722
+ min,
11723
+ max,
11724
+ step,
11725
+ value: normalizedRange[0],
11726
+ onChange: handleRangeChange("min"),
11727
+ onMouseUp,
11728
+ onTouchEnd,
11729
+ disabled,
11730
+ "aria-label": "Minimum value",
11731
+ onPointerDown: () => setActiveThumb("min"),
11732
+ onFocus: () => setActiveThumb("min"),
11733
+ className: cn(baseInputClassName, minZ, "pointer-events-none"),
11734
+ ...props
11735
+ }
11736
+ ),
11737
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11738
+ "input",
11739
+ {
11740
+ type: "range",
11741
+ min,
11742
+ max,
11743
+ step,
11744
+ value: normalizedRange[1],
11745
+ onChange: handleRangeChange("max"),
11746
+ onMouseUp,
11747
+ onTouchEnd,
11748
+ disabled,
11749
+ "aria-label": "Maximum value",
11750
+ onPointerDown: () => setActiveThumb("max"),
11751
+ onFocus: () => setActiveThumb("max"),
11752
+ className: cn(baseInputClassName, maxZ, "pointer-events-none"),
11753
+ ...props
11754
+ }
11755
+ )
11756
+ ] });
11757
+ })()
11429
11758
  ] })
11430
11759
  ] });
11431
11760
  }
@@ -13691,7 +14020,7 @@ var Timeline_default = Timeline;
13691
14020
  var React42 = __toESM(require("react"), 1);
13692
14021
  var import_lucide_react29 = require("lucide-react");
13693
14022
  var import_jsx_runtime50 = require("react/jsx-runtime");
13694
- var clamp5 = (n, min, max) => Math.max(min, Math.min(max, n));
14023
+ var clamp6 = (n, min, max) => Math.max(min, Math.min(max, n));
13695
14024
  function hexToRgb(hex) {
13696
14025
  const str = hex.replace(/^#/, "").trim();
13697
14026
  if (str.length === 3) {
@@ -13709,7 +14038,7 @@ function hexToRgb(hex) {
13709
14038
  return null;
13710
14039
  }
13711
14040
  function rgbToHex(r, g, b) {
13712
- return `#${[r, g, b].map((v) => clamp5(Math.round(v), 0, 255).toString(16).padStart(2, "0")).join("")}`;
14041
+ return `#${[r, g, b].map((v) => clamp6(Math.round(v), 0, 255).toString(16).padStart(2, "0")).join("")}`;
13713
14042
  }
13714
14043
  function rgbToHsl(r, g, b) {
13715
14044
  r /= 255;
@@ -13770,10 +14099,10 @@ function parseAnyColor(input) {
13770
14099
  }
13771
14100
  const rgbaMatch = s.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(\d*\.?\d+))?\s*\)/i);
13772
14101
  if (rgbaMatch) {
13773
- const r = clamp5(parseInt(rgbaMatch[1], 10), 0, 255);
13774
- const g = clamp5(parseInt(rgbaMatch[2], 10), 0, 255);
13775
- const b = clamp5(parseInt(rgbaMatch[3], 10), 0, 255);
13776
- const a = rgbaMatch[4] != null ? clamp5(parseFloat(rgbaMatch[4]), 0, 1) : 1;
14102
+ const r = clamp6(parseInt(rgbaMatch[1], 10), 0, 255);
14103
+ const g = clamp6(parseInt(rgbaMatch[2], 10), 0, 255);
14104
+ const b = clamp6(parseInt(rgbaMatch[3], 10), 0, 255);
14105
+ const a = rgbaMatch[4] != null ? clamp6(parseFloat(rgbaMatch[4]), 0, 1) : 1;
13777
14106
  return { r, g, b, a };
13778
14107
  }
13779
14108
  const hslaMatch = s.match(/hsla?\(\s*(\d{1,3}(?:\.\d+)?)\s*,?\s*(\d{1,3}(?:\.\d+)?)%?\s*,?\s*(\d{1,3}(?:\.\d+)?)%?(?:\s*,?\s*(\d*\.?\d+))?\s*\)/i);
@@ -13781,7 +14110,7 @@ function parseAnyColor(input) {
13781
14110
  const h = parseFloat(hslaMatch[1]);
13782
14111
  const sl = parseFloat(hslaMatch[2]);
13783
14112
  const l = parseFloat(hslaMatch[3]);
13784
- const a = hslaMatch[4] != null ? clamp5(parseFloat(hslaMatch[4]), 0, 1) : 1;
14113
+ const a = hslaMatch[4] != null ? clamp6(parseFloat(hslaMatch[4]), 0, 1) : 1;
13785
14114
  const rgb = hslToRgb(h, sl, l);
13786
14115
  return { ...rgb, a };
13787
14116
  }
@@ -13794,12 +14123,12 @@ function formatOutput({ r, g, b, a }, withAlpha, format) {
13794
14123
  if (format === "hsl" || format === "hsla") {
13795
14124
  const hsl = rgbToHsl(r, g, b);
13796
14125
  if (format === "hsla" || withAlpha) {
13797
- return `hsla(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%, ${clamp5(a, 0, 1)})`;
14126
+ return `hsla(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%, ${clamp6(a, 0, 1)})`;
13798
14127
  }
13799
14128
  return `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%)`;
13800
14129
  }
13801
14130
  if (withAlpha || a !== 1) {
13802
- return `rgba(${clamp5(r, 0, 255)}, ${clamp5(g, 0, 255)}, ${clamp5(b, 0, 255)}, ${clamp5(a, 0, 1)})`;
14131
+ return `rgba(${clamp6(r, 0, 255)}, ${clamp6(g, 0, 255)}, ${clamp6(b, 0, 255)}, ${clamp6(a, 0, 1)})`;
13803
14132
  }
13804
14133
  return rgbToHex(r, g, b);
13805
14134
  }
@@ -13933,7 +14262,7 @@ function ColorPicker({
13933
14262
  emit(next);
13934
14263
  };
13935
14264
  const setAlpha = (aPct) => {
13936
- const a = clamp5(aPct / 100, 0, 1);
14265
+ const a = clamp6(aPct / 100, 0, 1);
13937
14266
  const next = { ...rgba, a };
13938
14267
  setRgba(next);
13939
14268
  emit(next);
@@ -18594,7 +18923,7 @@ function toNullableNumber(value) {
18594
18923
  }
18595
18924
  return null;
18596
18925
  }
18597
- function clamp6(value, min, max) {
18926
+ function clamp7(value, min, max) {
18598
18927
  return Math.min(max, Math.max(min, value));
18599
18928
  }
18600
18929
  function ResizableImageNodeView(props) {
@@ -18656,18 +18985,18 @@ function ResizableImageNodeView(props) {
18656
18985
  let nextH = drag.startH;
18657
18986
  if (event.ctrlKey) {
18658
18987
  if (Math.abs(dx) >= Math.abs(dy)) {
18659
- nextW = clamp6(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18660
- nextH = clamp6(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18988
+ nextW = clamp7(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18989
+ nextH = clamp7(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18661
18990
  } else {
18662
- nextH = clamp6(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18663
- nextW = clamp6(nextH * drag.aspect, MIN_IMAGE_SIZE_PX, drag.maxW);
18991
+ nextH = clamp7(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18992
+ nextW = clamp7(nextH * drag.aspect, MIN_IMAGE_SIZE_PX, drag.maxW);
18664
18993
  }
18665
18994
  } else {
18666
18995
  if (!drag.axis && (Math.abs(dx) > AXIS_LOCK_THRESHOLD_PX || Math.abs(dy) > AXIS_LOCK_THRESHOLD_PX)) {
18667
18996
  drag.axis = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
18668
18997
  }
18669
- if (drag.axis === "x") nextW = clamp6(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18670
- if (drag.axis === "y") nextH = clamp6(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18998
+ if (drag.axis === "x") nextW = clamp7(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18999
+ if (drag.axis === "y") nextH = clamp7(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18671
19000
  }
18672
19001
  drag.lastW = nextW;
18673
19002
  drag.lastH = nextH;