@underverse-ui/underverse 0.2.106 → 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") {
@@ -7159,17 +7167,20 @@ function WheelColumn({
7159
7167
  },
7160
7168
  [items.length, loop]
7161
7169
  );
7162
- React25.useEffect(() => {
7170
+ React25.useLayoutEffect(() => {
7163
7171
  const el = scrollRef.current;
7164
7172
  if (!el) return;
7165
7173
  const maxVirtual = Math.max(0, extendedItems.length - 1);
7166
7174
  const currentVirtual2 = clamp3(Math.round(el.scrollTop / itemHeight), 0, maxVirtual);
7167
- const desiredVirtual = loop ? getNearestVirtualIndex(valueIndex, currentVirtual2) : valueIndex;
7175
+ const desiredVirtual = loop && lastVirtualIndexRef.current == null ? baseOffset + valueIndex : loop ? getNearestVirtualIndex(valueIndex, currentVirtual2) : valueIndex;
7168
7176
  const nextTop = desiredVirtual * itemHeight;
7169
7177
  const delta = Math.abs(el.scrollTop - nextTop);
7170
7178
  if (delta > 1) {
7171
- const behavior = animate && delta <= itemHeight * 1.5 ? "smooth" : "auto";
7172
- el.scrollTo({ top: nextTop, behavior });
7179
+ if (animate && delta <= itemHeight * 1.5) {
7180
+ el.scrollTo({ top: nextTop, behavior: "smooth" });
7181
+ } else {
7182
+ el.scrollTop = nextTop;
7183
+ }
7173
7184
  }
7174
7185
  lastVirtualIndexRef.current = desiredVirtual;
7175
7186
  return () => {
@@ -7177,9 +7188,13 @@ function WheelColumn({
7177
7188
  window.clearTimeout(scrollEndTimeoutRef.current);
7178
7189
  scrollEndTimeoutRef.current = null;
7179
7190
  }
7191
+ if (inertiaRafRef.current != null) {
7192
+ cancelAnimationFrame(inertiaRafRef.current);
7193
+ inertiaRafRef.current = null;
7194
+ }
7180
7195
  cancelAnimationFrame(rafRef.current);
7181
7196
  };
7182
- }, [animate, extendedItems.length, getNearestVirtualIndex, itemHeight, loop, scrollRef, valueIndex]);
7197
+ }, [animate, baseOffset, extendedItems.length, getNearestVirtualIndex, itemHeight, loop, scrollRef, valueIndex]);
7183
7198
  React25.useEffect(() => {
7184
7199
  const el = scrollRef.current;
7185
7200
  if (!el) return;
@@ -7262,7 +7277,7 @@ function WheelColumn({
7262
7277
  if (centered > max) centered -= len;
7263
7278
  if (centered !== snappedVirtual) {
7264
7279
  lastVirtualIndexRef.current = centered;
7265
- el.scrollTo({ top: centered * itemHeight, behavior: "auto" });
7280
+ el.scrollTop = centered * itemHeight;
7266
7281
  }
7267
7282
  }
7268
7283
  }, 120);
@@ -7274,6 +7289,138 @@ function WheelColumn({
7274
7289
  const from = lastVirtualIndexRef.current ?? fallback;
7275
7290
  return getNearestVirtualIndex(valueIndex, from);
7276
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
+ };
7277
7424
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: cn("flex-1", ui.columnWidth), children: [
7278
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 }),
7279
7426
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "relative rounded-xl bg-muted/30 overflow-hidden", style: { height }, children: [
@@ -7291,8 +7438,9 @@ function WheelColumn({
7291
7438
  {
7292
7439
  ref: scrollRef,
7293
7440
  className: cn(
7294
- "h-full overflow-y-auto overscroll-contain snap-y snap-mandatory scroll-smooth",
7441
+ "h-full overflow-y-auto overscroll-contain snap-y snap-mandatory",
7295
7442
  "scrollbar-none",
7443
+ "select-none cursor-grab active:cursor-grabbing",
7296
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"
7297
7445
  ),
7298
7446
  style: { paddingTop: paddingY, paddingBottom: paddingY },
@@ -7301,7 +7449,15 @@ function WheelColumn({
7301
7449
  tabIndex: focused ? 0 : -1,
7302
7450
  onKeyDown: (e) => onKeyDown(e, column),
7303
7451
  onFocus: () => setFocusedColumn(column),
7304
- 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),
7305
7461
  children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: extendedItems.map((n, index) => {
7306
7462
  const dist = Math.abs(index - currentVirtual);
7307
7463
  const distForVisual = Math.min(dist, 2);
@@ -7326,6 +7482,7 @@ function WheelColumn({
7326
7482
  opacity
7327
7483
  },
7328
7484
  onClick: () => {
7485
+ if (Date.now() < suppressItemClickUntilRef.current) return;
7329
7486
  const el = scrollRef.current;
7330
7487
  if (!el) return;
7331
7488
  suppressScrollSelectUntilRef.current = Date.now() + 350;
@@ -7686,7 +7843,7 @@ function TimePicker({
7686
7843
  emit(next);
7687
7844
  };
7688
7845
  const timePickerContent = /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: panelSz.stackGap, children: [
7689
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex items-center justify-center py-2 px-3 rounded-xl bg-linear-to-r from-primary/10 via-primary/5 to-primary/10 border border-primary/20", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: cn(panelSz.timeText, "font-bold tabular-nums tracking-wide text-foreground"), children: display }) }),
7846
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: cn(panelSz.timeText, "font-bold tabular-nums tracking-wide text-foreground underline underline-offset-8 decoration-primary/60"), children: display }) }),
7690
7847
  allowManualInput && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "relative", children: [
7691
7848
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7692
7849
  Input_default,
@@ -11317,16 +11474,22 @@ var SIZE_STYLES = {
11317
11474
  container: "py-3"
11318
11475
  }
11319
11476
  };
11477
+ var clamp5 = (n, min, max) => Math.min(max, Math.max(min, n));
11320
11478
  var Slider = React34.forwardRef(
11321
11479
  ({
11322
11480
  className,
11481
+ mode = "single",
11323
11482
  value,
11324
11483
  defaultValue = 0,
11484
+ rangeValue,
11485
+ defaultRangeValue,
11325
11486
  min = 0,
11326
11487
  max = 100,
11327
11488
  step = 1,
11328
11489
  onChange,
11329
11490
  onValueChange,
11491
+ onRangeChange,
11492
+ onRangeValueChange,
11330
11493
  onMouseUp,
11331
11494
  onTouchEnd,
11332
11495
  label,
@@ -11343,20 +11506,125 @@ var Slider = React34.forwardRef(
11343
11506
  noFocus = true,
11344
11507
  ...props
11345
11508
  }, ref) => {
11509
+ const isRange = mode === "range";
11510
+ const trackRef = React34.useRef(null);
11346
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);
11347
11519
  const isControlled = value !== void 0;
11348
11520
  const currentValue = isControlled ? value : internalValue;
11349
- const handleChange = (e) => {
11350
- const newValue = Number(e.target.value);
11351
- if (!isControlled) {
11352
- 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 {
11353
11626
  }
11354
- onChange?.(newValue);
11355
- onValueChange?.(newValue);
11356
11627
  };
11357
- const percentage = (currentValue - min) / (max - min) * 100;
11358
- const sizeStyles8 = SIZE_STYLES[size];
11359
- const displayValue = formatValue ? formatValue(currentValue) : currentValue.toString();
11360
11628
  if (orientation === "vertical") {
11361
11629
  }
11362
11630
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cn("w-full space-y-2", containerClassName), children: [
@@ -11364,65 +11632,129 @@ var Slider = React34.forwardRef(
11364
11632
  label && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: cn("text-sm font-medium text-foreground", labelClassName), children: label }),
11365
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 })
11366
11634
  ] }),
11367
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: cn("relative flex items-center", sizeStyles8.container), children: [
11368
- /* @__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}%` } }) }),
11369
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11370
- "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",
11371
11638
  {
11372
- ref,
11373
- type: "range",
11374
- min,
11375
- max,
11376
- step,
11377
- value: currentValue,
11378
- onChange: handleChange,
11379
- onMouseUp,
11380
- onTouchEnd,
11381
- disabled,
11382
- className: cn(
11383
- // Base styles
11384
- "absolute w-full h-full appearance-none bg-transparent cursor-pointer",
11385
- !noFocus && "focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-background rounded-full",
11386
- noFocus && "outline-none ring-0 focus:outline-none focus:ring-0 focus-visible:outline-none",
11387
- // Webkit styles for thumb
11388
- "[&::-webkit-slider-thumb]:appearance-none",
11389
- "[&::-webkit-slider-thumb]:bg-primary",
11390
- "[&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-background",
11391
- "[&::-webkit-slider-thumb]:rounded-full",
11392
- "[&::-webkit-slider-thumb]:shadow-md",
11393
- "[&::-webkit-slider-thumb]:cursor-pointer",
11394
- "[&::-webkit-slider-thumb]:transition-all [&::-webkit-slider-thumb]:duration-150",
11395
- size === "sm" && "[&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3",
11396
- size === "md" && "[&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4",
11397
- size === "lg" && "[&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5",
11398
- // Firefox styles for thumb
11399
- "[&::-moz-range-thumb]:bg-primary",
11400
- "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-background",
11401
- "[&::-moz-range-thumb]:rounded-full",
11402
- "[&::-moz-range-thumb]:shadow-md",
11403
- "[&::-moz-range-thumb]:cursor-pointer",
11404
- "[&::-moz-range-thumb]:transition-all [&::-moz-range-thumb]:duration-150",
11405
- size === "sm" && "[&::-moz-range-thumb]:w-3 [&::-moz-range-thumb]:h-3",
11406
- size === "md" && "[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4",
11407
- size === "lg" && "[&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5",
11408
- // Remove default track in Firefox
11409
- "[&::-moz-range-track]:bg-transparent",
11410
- "[&::-moz-range-track]:border-transparent",
11411
- // Hover effects
11412
- "hover:[&::-webkit-slider-thumb]:scale-110 hover:[&::-webkit-slider-thumb]:shadow-lg",
11413
- "hover:[&::-moz-range-thumb]:scale-110 hover:[&::-moz-range-thumb]:shadow-lg",
11414
- // Disabled styles
11415
- disabled && [
11416
- "cursor-not-allowed opacity-50",
11417
- "[&::-webkit-slider-thumb]:cursor-not-allowed [&::-webkit-slider-thumb]:opacity-50",
11418
- "[&::-moz-range-thumb]:cursor-not-allowed [&::-moz-range-thumb]:opacity-50"
11419
- ],
11420
- className,
11421
- thumbClassName
11422
- ),
11423
- ...props
11639
+ className: "absolute top-0 h-full bg-primary rounded-full",
11640
+ style: { left: `${rangeStartPct}%`, width: `${Math.max(0, rangeEndPct - rangeStartPct)}%` }
11424
11641
  }
11425
- )
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
+ })()
11426
11758
  ] })
11427
11759
  ] });
11428
11760
  }
@@ -13688,7 +14020,7 @@ var Timeline_default = Timeline;
13688
14020
  var React42 = __toESM(require("react"), 1);
13689
14021
  var import_lucide_react29 = require("lucide-react");
13690
14022
  var import_jsx_runtime50 = require("react/jsx-runtime");
13691
- 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));
13692
14024
  function hexToRgb(hex) {
13693
14025
  const str = hex.replace(/^#/, "").trim();
13694
14026
  if (str.length === 3) {
@@ -13706,7 +14038,7 @@ function hexToRgb(hex) {
13706
14038
  return null;
13707
14039
  }
13708
14040
  function rgbToHex(r, g, b) {
13709
- 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("")}`;
13710
14042
  }
13711
14043
  function rgbToHsl(r, g, b) {
13712
14044
  r /= 255;
@@ -13767,10 +14099,10 @@ function parseAnyColor(input) {
13767
14099
  }
13768
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);
13769
14101
  if (rgbaMatch) {
13770
- const r = clamp5(parseInt(rgbaMatch[1], 10), 0, 255);
13771
- const g = clamp5(parseInt(rgbaMatch[2], 10), 0, 255);
13772
- const b = clamp5(parseInt(rgbaMatch[3], 10), 0, 255);
13773
- 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;
13774
14106
  return { r, g, b, a };
13775
14107
  }
13776
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);
@@ -13778,7 +14110,7 @@ function parseAnyColor(input) {
13778
14110
  const h = parseFloat(hslaMatch[1]);
13779
14111
  const sl = parseFloat(hslaMatch[2]);
13780
14112
  const l = parseFloat(hslaMatch[3]);
13781
- 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;
13782
14114
  const rgb = hslToRgb(h, sl, l);
13783
14115
  return { ...rgb, a };
13784
14116
  }
@@ -13791,12 +14123,12 @@ function formatOutput({ r, g, b, a }, withAlpha, format) {
13791
14123
  if (format === "hsl" || format === "hsla") {
13792
14124
  const hsl = rgbToHsl(r, g, b);
13793
14125
  if (format === "hsla" || withAlpha) {
13794
- 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)})`;
13795
14127
  }
13796
14128
  return `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%)`;
13797
14129
  }
13798
14130
  if (withAlpha || a !== 1) {
13799
- 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)})`;
13800
14132
  }
13801
14133
  return rgbToHex(r, g, b);
13802
14134
  }
@@ -13930,7 +14262,7 @@ function ColorPicker({
13930
14262
  emit(next);
13931
14263
  };
13932
14264
  const setAlpha = (aPct) => {
13933
- const a = clamp5(aPct / 100, 0, 1);
14265
+ const a = clamp6(aPct / 100, 0, 1);
13934
14266
  const next = { ...rgba, a };
13935
14267
  setRgba(next);
13936
14268
  emit(next);
@@ -18591,7 +18923,7 @@ function toNullableNumber(value) {
18591
18923
  }
18592
18924
  return null;
18593
18925
  }
18594
- function clamp6(value, min, max) {
18926
+ function clamp7(value, min, max) {
18595
18927
  return Math.min(max, Math.max(min, value));
18596
18928
  }
18597
18929
  function ResizableImageNodeView(props) {
@@ -18653,18 +18985,18 @@ function ResizableImageNodeView(props) {
18653
18985
  let nextH = drag.startH;
18654
18986
  if (event.ctrlKey) {
18655
18987
  if (Math.abs(dx) >= Math.abs(dy)) {
18656
- nextW = clamp6(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18657
- 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);
18658
18990
  } else {
18659
- nextH = clamp6(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18660
- 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);
18661
18993
  }
18662
18994
  } else {
18663
18995
  if (!drag.axis && (Math.abs(dx) > AXIS_LOCK_THRESHOLD_PX || Math.abs(dy) > AXIS_LOCK_THRESHOLD_PX)) {
18664
18996
  drag.axis = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
18665
18997
  }
18666
- if (drag.axis === "x") nextW = clamp6(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18667
- 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);
18668
19000
  }
18669
19001
  drag.lastW = nextW;
18670
19002
  drag.lastH = nextH;