@underverse-ui/underverse 0.2.104 → 0.2.106

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
@@ -7082,6 +7082,7 @@ var React25 = __toESM(require("react"), 1);
7082
7082
  var import_lucide_react16 = require("lucide-react");
7083
7083
  var import_jsx_runtime31 = require("react/jsx-runtime");
7084
7084
  var pad = (n) => n.toString().padStart(2, "0");
7085
+ var clamp3 = (n, min, max) => Math.min(max, Math.max(min, n));
7085
7086
  var WHEEL_ITEM_HEIGHT = {
7086
7087
  sm: 30,
7087
7088
  md: 34,
@@ -7096,6 +7097,7 @@ function WheelColumn({
7096
7097
  onSelect,
7097
7098
  scrollRef,
7098
7099
  itemHeight,
7100
+ size,
7099
7101
  animate,
7100
7102
  focused,
7101
7103
  setFocusedColumn,
@@ -7104,18 +7106,72 @@ function WheelColumn({
7104
7106
  const height = itemHeight * WHEEL_VISIBLE_ITEMS;
7105
7107
  const paddingY = (height - itemHeight) / 2;
7106
7108
  const rafRef = React25.useRef(0);
7107
- const lastIndexRef = React25.useRef(null);
7109
+ const lastVirtualIndexRef = React25.useRef(null);
7108
7110
  const wheelDeltaRef = React25.useRef(0);
7109
7111
  const scrollEndTimeoutRef = React25.useRef(null);
7110
7112
  const suppressScrollSelectUntilRef = React25.useRef(0);
7113
+ const loop = true;
7114
+ const ui = React25.useMemo(() => {
7115
+ if (size === "sm") {
7116
+ return {
7117
+ columnWidth: "min-w-[64px] max-w-[84px]",
7118
+ label: "text-[9px] mb-2",
7119
+ selectedText: "text-base",
7120
+ unselectedText: "text-sm",
7121
+ fadeHeight: "h-10"
7122
+ };
7123
+ }
7124
+ if (size === "lg") {
7125
+ return {
7126
+ columnWidth: "min-w-[80px] max-w-[110px]",
7127
+ label: "text-[11px] mb-3",
7128
+ selectedText: "text-xl",
7129
+ unselectedText: "text-lg",
7130
+ fadeHeight: "h-14"
7131
+ };
7132
+ }
7133
+ return {
7134
+ columnWidth: "min-w-[70px] max-w-[90px]",
7135
+ label: "text-[10px] mb-3",
7136
+ selectedText: "text-lg",
7137
+ unselectedText: "text-base",
7138
+ fadeHeight: "h-12"
7139
+ };
7140
+ }, [size]);
7141
+ const baseOffset = React25.useMemo(() => loop ? items.length : 0, [items.length, loop]);
7142
+ const extendedItems = React25.useMemo(() => loop ? [...items, ...items, ...items] : items, [items, loop]);
7143
+ const getNearestVirtualIndex = React25.useCallback(
7144
+ (realIndex, fromVirtual) => {
7145
+ const len = items.length;
7146
+ if (len <= 0) return 0;
7147
+ if (!loop) return clamp3(realIndex, 0, Math.max(0, len - 1));
7148
+ const candidates = [realIndex, realIndex + len, realIndex + 2 * len];
7149
+ let best = candidates[0];
7150
+ let bestDist = Math.abs(best - fromVirtual);
7151
+ for (const c of candidates) {
7152
+ const dist = Math.abs(c - fromVirtual);
7153
+ if (dist < bestDist) {
7154
+ best = c;
7155
+ bestDist = dist;
7156
+ }
7157
+ }
7158
+ return best;
7159
+ },
7160
+ [items.length, loop]
7161
+ );
7111
7162
  React25.useEffect(() => {
7112
7163
  const el = scrollRef.current;
7113
7164
  if (!el) return;
7114
- const nextTop = Math.max(0, valueIndex) * itemHeight;
7115
- if (Math.abs(el.scrollTop - nextTop) > 1) {
7116
- el.scrollTo({ top: nextTop, behavior: animate ? "smooth" : "auto" });
7117
- }
7118
- lastIndexRef.current = valueIndex;
7165
+ const maxVirtual = Math.max(0, extendedItems.length - 1);
7166
+ const currentVirtual2 = clamp3(Math.round(el.scrollTop / itemHeight), 0, maxVirtual);
7167
+ const desiredVirtual = loop ? getNearestVirtualIndex(valueIndex, currentVirtual2) : valueIndex;
7168
+ const nextTop = desiredVirtual * itemHeight;
7169
+ const delta = Math.abs(el.scrollTop - nextTop);
7170
+ if (delta > 1) {
7171
+ const behavior = animate && delta <= itemHeight * 1.5 ? "smooth" : "auto";
7172
+ el.scrollTo({ top: nextTop, behavior });
7173
+ }
7174
+ lastVirtualIndexRef.current = desiredVirtual;
7119
7175
  return () => {
7120
7176
  if (scrollEndTimeoutRef.current != null) {
7121
7177
  window.clearTimeout(scrollEndTimeoutRef.current);
@@ -7123,7 +7179,7 @@ function WheelColumn({
7123
7179
  }
7124
7180
  cancelAnimationFrame(rafRef.current);
7125
7181
  };
7126
- }, [animate, itemHeight, scrollRef, valueIndex]);
7182
+ }, [animate, extendedItems.length, getNearestVirtualIndex, itemHeight, loop, scrollRef, valueIndex]);
7127
7183
  React25.useEffect(() => {
7128
7184
  const el = scrollRef.current;
7129
7185
  if (!el) return;
@@ -7155,19 +7211,29 @@ function WheelColumn({
7155
7211
  step = Math.sign(wheelDeltaRef.current);
7156
7212
  wheelDeltaRef.current = 0;
7157
7213
  }
7158
- const fromIndex = lastIndexRef.current ?? valueIndex;
7159
- const nextIndex = Math.max(0, Math.min(items.length - 1, fromIndex + step));
7160
- if (nextIndex === fromIndex) return;
7214
+ if (items.length <= 0) return;
7215
+ const fromVirtual = lastVirtualIndexRef.current ?? (loop ? baseOffset + valueIndex : valueIndex);
7216
+ let nextVirtual = fromVirtual + step;
7217
+ if (!loop) {
7218
+ nextVirtual = clamp3(nextVirtual, 0, Math.max(0, items.length - 1));
7219
+ } else {
7220
+ const len = items.length;
7221
+ const maxVirtual = Math.max(0, len * 3 - 1);
7222
+ if (nextVirtual < 0) nextVirtual += len;
7223
+ if (nextVirtual > maxVirtual) nextVirtual -= len;
7224
+ }
7225
+ if (nextVirtual === fromVirtual) return;
7161
7226
  lastStepAtRef.current = Date.now();
7162
7227
  lastStepSignRef.current = step;
7163
- lastIndexRef.current = nextIndex;
7228
+ lastVirtualIndexRef.current = nextVirtual;
7164
7229
  suppressScrollSelectUntilRef.current = Date.now() + 350;
7165
- el.scrollTo({ top: nextIndex * itemHeight, behavior: animate ? "smooth" : "auto" });
7166
- onSelect(items[nextIndex]);
7230
+ el.scrollTo({ top: nextVirtual * itemHeight, behavior: animate ? "smooth" : "auto" });
7231
+ const realIndex = (nextVirtual % items.length + items.length) % items.length;
7232
+ onSelect(items[realIndex]);
7167
7233
  };
7168
7234
  el.addEventListener("wheel", onWheel, { passive: false });
7169
7235
  return () => el.removeEventListener("wheel", onWheel);
7170
- }, [animate, column, itemHeight, items, onSelect, scrollRef, setFocusedColumn, valueIndex]);
7236
+ }, [animate, baseOffset, column, itemHeight, items, loop, onSelect, scrollRef, setFocusedColumn, valueIndex]);
7171
7237
  const handleScroll = () => {
7172
7238
  const el = scrollRef.current;
7173
7239
  if (!el) return;
@@ -7178,17 +7244,38 @@ function WheelColumn({
7178
7244
  window.clearTimeout(scrollEndTimeoutRef.current);
7179
7245
  }
7180
7246
  scrollEndTimeoutRef.current = window.setTimeout(() => {
7181
- const idx = Math.max(0, Math.min(items.length - 1, Math.round(el.scrollTop / itemHeight)));
7182
- if (lastIndexRef.current === idx) return;
7183
- lastIndexRef.current = idx;
7247
+ if (items.length <= 0) return;
7248
+ const len = items.length;
7249
+ const maxVirtual = Math.max(0, extendedItems.length - 1);
7250
+ const idxVirtual = clamp3(Math.round(el.scrollTop / itemHeight), 0, maxVirtual);
7251
+ const realIndex = (idxVirtual % len + len) % len;
7252
+ const snappedVirtual = loop ? getNearestVirtualIndex(realIndex, idxVirtual) : realIndex;
7253
+ if (lastVirtualIndexRef.current !== snappedVirtual) lastVirtualIndexRef.current = snappedVirtual;
7184
7254
  suppressScrollSelectUntilRef.current = Date.now() + 350;
7185
- el.scrollTo({ top: idx * itemHeight, behavior: animate ? "smooth" : "auto" });
7186
- onSelect(items[idx]);
7255
+ el.scrollTo({ top: snappedVirtual * itemHeight, behavior: animate ? "smooth" : "auto" });
7256
+ onSelect(items[realIndex]);
7257
+ if (loop) {
7258
+ const min = len;
7259
+ const max = len * 2 - 1;
7260
+ let centered = snappedVirtual;
7261
+ if (centered < min) centered += len;
7262
+ if (centered > max) centered -= len;
7263
+ if (centered !== snappedVirtual) {
7264
+ lastVirtualIndexRef.current = centered;
7265
+ el.scrollTo({ top: centered * itemHeight, behavior: "auto" });
7266
+ }
7267
+ }
7187
7268
  }, 120);
7188
7269
  });
7189
7270
  };
7190
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex-1 min-w-[70px] max-w-[90px]", children: [
7191
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70 mb-3 text-center", children: labelText }),
7271
+ const currentVirtual = React25.useMemo(() => {
7272
+ if (!loop || items.length <= 0) return valueIndex;
7273
+ const fallback = baseOffset + valueIndex;
7274
+ const from = lastVirtualIndexRef.current ?? fallback;
7275
+ return getNearestVirtualIndex(valueIndex, from);
7276
+ }, [baseOffset, getNearestVirtualIndex, items.length, loop, valueIndex]);
7277
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: cn("flex-1", ui.columnWidth), children: [
7278
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn(ui.label, "font-bold uppercase tracking-wider text-muted-foreground/70 text-center"), children: labelText }),
7192
7279
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "relative rounded-xl bg-muted/30 overflow-hidden", style: { height }, children: [
7193
7280
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7194
7281
  "div",
@@ -7197,8 +7284,8 @@ function WheelColumn({
7197
7284
  style: { height: itemHeight }
7198
7285
  }
7199
7286
  ),
7200
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "pointer-events-none absolute inset-x-0 top-0 h-12 bg-linear-to-b from-background/95 via-background/60 to-transparent z-10" }),
7201
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-linear-to-t from-background/95 via-background/60 to-transparent z-10" }),
7287
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn("pointer-events-none absolute inset-x-0 top-0 bg-linear-to-b from-muted/20 via-muted/5 to-transparent z-10", ui.fadeHeight) }),
7288
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn("pointer-events-none absolute inset-x-0 bottom-0 bg-linear-to-t from-muted/20 via-muted/5 to-transparent z-10", ui.fadeHeight) }),
7202
7289
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7203
7290
  "div",
7204
7291
  {
@@ -7215,11 +7302,14 @@ function WheelColumn({
7215
7302
  onKeyDown: (e) => onKeyDown(e, column),
7216
7303
  onFocus: () => setFocusedColumn(column),
7217
7304
  onScroll: handleScroll,
7218
- children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: items.map((n, index) => {
7219
- const dist = Math.abs(index - valueIndex);
7220
- const scale = 1 - Math.min(dist * 0.12, 0.36);
7221
- const opacity = 1 - Math.min(dist * 0.22, 0.75);
7222
- const isSelected = index === valueIndex;
7305
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: extendedItems.map((n, index) => {
7306
+ const dist = Math.abs(index - currentVirtual);
7307
+ const distForVisual = Math.min(dist, 2);
7308
+ const t = distForVisual / 2;
7309
+ const ease = t * t;
7310
+ const scale = 1 - ease * 0.18;
7311
+ const opacity = 1 - ease * 0.55;
7312
+ const isSelected = index === currentVirtual;
7223
7313
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7224
7314
  "button",
7225
7315
  {
@@ -7228,7 +7318,7 @@ function WheelColumn({
7228
7318
  "aria-selected": isSelected,
7229
7319
  className: cn(
7230
7320
  "w-full snap-center flex items-center justify-center rounded-lg transition-all duration-200 font-bold tabular-nums",
7231
- isSelected ? "text-primary text-lg" : "text-muted-foreground hover:text-foreground/70"
7321
+ isSelected ? cn("text-primary", ui.selectedText) : cn("text-muted-foreground hover:text-foreground/70", ui.unselectedText)
7232
7322
  ),
7233
7323
  style: {
7234
7324
  height: itemHeight,
@@ -7237,13 +7327,16 @@ function WheelColumn({
7237
7327
  },
7238
7328
  onClick: () => {
7239
7329
  const el = scrollRef.current;
7330
+ if (!el) return;
7240
7331
  suppressScrollSelectUntilRef.current = Date.now() + 350;
7241
- el?.scrollTo({ top: index * itemHeight, behavior: animate ? "smooth" : "auto" });
7242
- onSelect(n);
7332
+ lastVirtualIndexRef.current = index;
7333
+ el.scrollTo({ top: index * itemHeight, behavior: animate ? "smooth" : "auto" });
7334
+ const realIndex = (index % items.length + items.length) % items.length;
7335
+ onSelect(items[realIndex]);
7243
7336
  },
7244
7337
  children: pad(n)
7245
7338
  },
7246
- n
7339
+ `${column}_${index}`
7247
7340
  );
7248
7341
  }) })
7249
7342
  }
@@ -7303,6 +7396,7 @@ function TimePicker({
7303
7396
  secondStep = 1,
7304
7397
  clearable = true,
7305
7398
  variant = "default",
7399
+ matchTriggerWidth = true,
7306
7400
  showNow = false,
7307
7401
  showPresets = false,
7308
7402
  allowManualInput = false,
@@ -7473,12 +7567,60 @@ function TimePicker({
7473
7567
  const hours = format === "24" ? Array.from({ length: 24 }, (_, i) => i) : Array.from({ length: 12 }, (_, i) => i + 1);
7474
7568
  const minutes = Array.from({ length: Math.ceil(60 / minuteStep) }, (_, i) => Math.min(59, i * minuteStep));
7475
7569
  const seconds = Array.from({ length: Math.ceil(60 / secondStep) }, (_, i) => Math.min(59, i * secondStep));
7570
+ const panelSizeClasses = {
7571
+ sm: {
7572
+ contentPadding: "p-4",
7573
+ stackGap: "space-y-3",
7574
+ timeText: "text-xl",
7575
+ inputSize: "sm",
7576
+ icon: "w-3 h-3",
7577
+ separatorPad: "pt-6",
7578
+ presetText: "text-[11px]",
7579
+ presetPadding: "px-3 py-2",
7580
+ actionText: "text-[11px]",
7581
+ actionPadding: "px-3 py-2",
7582
+ periodLabel: "text-[9px] mb-2",
7583
+ periodGap: "gap-1.5",
7584
+ periodButton: "px-3 py-2 text-xs"
7585
+ },
7586
+ md: {
7587
+ contentPadding: "p-5",
7588
+ stackGap: "space-y-4",
7589
+ timeText: "text-2xl",
7590
+ inputSize: "sm",
7591
+ icon: "w-3.5 h-3.5",
7592
+ separatorPad: "pt-8",
7593
+ presetText: "text-xs",
7594
+ presetPadding: "px-3 py-2.5",
7595
+ actionText: "text-xs",
7596
+ actionPadding: "px-4 py-2.5",
7597
+ periodLabel: "text-[10px] mb-3",
7598
+ periodGap: "gap-2",
7599
+ periodButton: "px-4 py-3 text-sm"
7600
+ },
7601
+ lg: {
7602
+ contentPadding: "p-6",
7603
+ stackGap: "space-y-5",
7604
+ timeText: "text-3xl",
7605
+ inputSize: "md",
7606
+ icon: "w-4 h-4",
7607
+ separatorPad: "pt-9",
7608
+ presetText: "text-sm",
7609
+ presetPadding: "px-4 py-3",
7610
+ actionText: "text-sm",
7611
+ actionPadding: "px-5 py-3",
7612
+ periodLabel: "text-[11px] mb-3",
7613
+ periodGap: "gap-2",
7614
+ periodButton: "px-5 py-3.5 text-base"
7615
+ }
7616
+ };
7476
7617
  const sizeClasses2 = {
7477
7618
  sm: { label: "text-xs", height: "h-8", padding: "px-2.5 py-1.5", text: "text-xs", icon: "w-3.5 h-3.5" },
7478
7619
  md: { label: "text-sm", height: "h-10", padding: "px-3 py-2", text: "text-sm", icon: "w-4 h-4" },
7479
7620
  lg: { label: "text-base", height: "h-12", padding: "px-4 py-3", text: "text-base", icon: "w-5 h-5" }
7480
7621
  };
7481
7622
  const sz = sizeClasses2[size];
7623
+ const panelSz = panelSizeClasses[size];
7482
7624
  const display = formatTime(parts, format, includeSeconds);
7483
7625
  const trigger = variant === "inline" ? null : /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
7484
7626
  "button",
@@ -7509,8 +7651,8 @@ function TimePicker({
7509
7651
  "div",
7510
7652
  {
7511
7653
  className: cn(
7512
- "flex items-center justify-center rounded-md p-1.5 transition-all duration-300",
7513
- error ? "bg-destructive/10 text-destructive" : success ? "bg-success/10 text-success" : open ? "bg-primary/15 text-primary" : "bg-muted/50 text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary"
7654
+ "flex items-center justify-center transition-colors duration-300",
7655
+ error ? "text-destructive" : success ? "text-success" : open ? "text-primary" : "text-muted-foreground group-hover:text-primary"
7514
7656
  ),
7515
7657
  children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.Clock, { className: cn(sz.icon, "transition-transform duration-300", open && "rotate-12") })
7516
7658
  }
@@ -7543,8 +7685,8 @@ function TimePicker({
7543
7685
  setParts(next);
7544
7686
  emit(next);
7545
7687
  };
7546
- const timePickerContent = /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "space-y-4", children: [
7547
- /* @__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: "text-2xl font-bold tabular-nums tracking-wide text-foreground", children: display }) }),
7688
+ 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 }) }),
7548
7690
  allowManualInput && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "relative", children: [
7549
7691
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7550
7692
  Input_default,
@@ -7552,12 +7694,12 @@ function TimePicker({
7552
7694
  placeholder: format === "12" ? "02:30 PM" : "14:30",
7553
7695
  value: manualInput || display,
7554
7696
  onChange: (e) => handleManualInput(e.target.value),
7555
- size: "sm",
7697
+ size: panelSz.inputSize,
7556
7698
  variant: "outlined",
7557
7699
  className: "pl-9"
7558
7700
  }
7559
7701
  ),
7560
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.Clock, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" })
7702
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.Clock, { className: cn("absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", panelSz.icon) })
7561
7703
  ] }),
7562
7704
  showPresets && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "grid grid-cols-2 gap-2", children: Object.keys(PRESETS).map((preset) => {
7563
7705
  const { icon: Icon, label: label2, color } = PRESETS[preset];
@@ -7566,7 +7708,9 @@ function TimePicker({
7566
7708
  {
7567
7709
  type: "button",
7568
7710
  className: cn(
7569
- "group relative px-3 py-2.5 text-xs font-medium rounded-xl border border-border/50 overflow-hidden",
7711
+ "group relative font-medium rounded-xl border border-border/50 overflow-hidden",
7712
+ panelSz.presetPadding,
7713
+ panelSz.presetText,
7570
7714
  "bg-linear-to-br from-background to-muted/30",
7571
7715
  "hover:border-primary/40 hover:shadow-md transition-all duration-300",
7572
7716
  animate && "hover:scale-[1.02] active:scale-[0.98]"
@@ -7582,7 +7726,7 @@ function TimePicker({
7582
7726
  }
7583
7727
  ),
7584
7728
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "relative flex items-center gap-2", children: [
7585
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Icon, { className: "w-3.5 h-3.5 text-muted-foreground group-hover:text-primary transition-colors" }),
7729
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Icon, { className: cn("text-muted-foreground group-hover:text-primary transition-colors", panelSz.icon) }),
7586
7730
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-foreground/80 group-hover:text-foreground transition-colors", children: label2 })
7587
7731
  ] })
7588
7732
  ]
@@ -7595,7 +7739,9 @@ function TimePicker({
7595
7739
  {
7596
7740
  type: "button",
7597
7741
  className: cn(
7598
- "px-3 py-2.5 text-xs font-medium rounded-xl border border-border/50",
7742
+ "font-medium rounded-xl border border-border/50",
7743
+ panelSz.presetPadding,
7744
+ panelSz.presetText,
7599
7745
  "bg-linear-to-br from-background to-muted/30",
7600
7746
  "hover:border-primary/40 hover:bg-primary/5 hover:shadow-md transition-all duration-300",
7601
7747
  animate && "hover:scale-[1.02] active:scale-[0.98]"
@@ -7620,6 +7766,7 @@ function TimePicker({
7620
7766
  onSelect: setHourFromDisplay,
7621
7767
  scrollRef: hourScrollRef,
7622
7768
  itemHeight,
7769
+ size,
7623
7770
  animate,
7624
7771
  focused: focusedColumn === "hour",
7625
7772
  setFocusedColumn: (col) => setFocusedColumn(col),
@@ -7627,7 +7774,7 @@ function TimePicker({
7627
7774
  }
7628
7775
  );
7629
7776
  })(),
7630
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col items-center justify-center pt-8", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-2", children: [
7777
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn("flex flex-col items-center justify-center", panelSz.separatorPad), children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-2", children: [
7631
7778
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-primary/60" }),
7632
7779
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-primary/60" })
7633
7780
  ] }) }),
@@ -7647,6 +7794,7 @@ function TimePicker({
7647
7794
  },
7648
7795
  scrollRef: minuteScrollRef,
7649
7796
  itemHeight,
7797
+ size,
7650
7798
  animate,
7651
7799
  focused: focusedColumn === "minute",
7652
7800
  setFocusedColumn: (col) => setFocusedColumn(col),
@@ -7655,7 +7803,7 @@ function TimePicker({
7655
7803
  );
7656
7804
  })(),
7657
7805
  includeSeconds && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
7658
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col items-center justify-center pt-8", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-2", children: [
7806
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn("flex flex-col items-center justify-center", panelSz.separatorPad), children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-2", children: [
7659
7807
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-primary/60" }),
7660
7808
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-primary/60" })
7661
7809
  ] }) }),
@@ -7675,6 +7823,7 @@ function TimePicker({
7675
7823
  },
7676
7824
  scrollRef: secondScrollRef,
7677
7825
  itemHeight,
7826
+ size,
7678
7827
  animate,
7679
7828
  focused: focusedColumn === "second",
7680
7829
  setFocusedColumn: (col) => setFocusedColumn(col),
@@ -7683,52 +7832,62 @@ function TimePicker({
7683
7832
  );
7684
7833
  })()
7685
7834
  ] }),
7686
- format === "12" && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex-1 min-w-[70px] max-w-[90px]", children: [
7687
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70 mb-3 text-center", children: "Period" }),
7688
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7689
- "div",
7690
- {
7691
- className: "flex flex-col gap-2 p-1 rounded-xl bg-muted/30",
7692
- role: "radiogroup",
7693
- "aria-label": "Select AM or PM",
7694
- tabIndex: focusedColumn === "period" ? 0 : -1,
7695
- onKeyDown: (e) => handleKeyDown(e, "period"),
7696
- onFocus: () => setFocusedColumn("period"),
7697
- children: ["AM", "PM"].map((p) => {
7698
- const isSelected = parts.p === p;
7699
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
7700
- "button",
7701
- {
7702
- type: "button",
7703
- role: "radio",
7704
- "aria-checked": isSelected,
7705
- className: cn(
7706
- "relative px-4 py-3 rounded-lg transition-all duration-300 text-sm font-bold overflow-hidden",
7707
- "focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-1",
7708
- isSelected && "bg-linear-to-r from-primary to-primary/80 text-primary-foreground shadow-lg shadow-primary/25",
7709
- !isSelected && "text-muted-foreground hover:text-foreground hover:bg-muted/50",
7710
- animate && "hover:scale-[1.02] active:scale-[0.98]"
7711
- ),
7712
- onClick: () => {
7713
- const pVal = p;
7714
- let hour = parts.h;
7715
- if (pVal === "AM" && hour >= 12) hour -= 12;
7716
- if (pVal === "PM" && hour < 12) hour += 12;
7717
- const next = { ...parts, p: pVal, h: hour };
7718
- setParts(next);
7719
- emit(next);
7720
- },
7721
- children: [
7722
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "absolute inset-0 bg-linear-to-tr from-white/20 to-transparent" }),
7723
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "relative", children: p })
7724
- ]
7725
- },
7726
- p
7727
- );
7728
- })
7729
- }
7730
- )
7731
- ] })
7835
+ format === "12" && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
7836
+ "div",
7837
+ {
7838
+ className: cn(
7839
+ "flex-1",
7840
+ size === "sm" ? "min-w-[64px] max-w-[84px]" : size === "lg" ? "min-w-[80px] max-w-[110px]" : "min-w-[70px] max-w-[90px]"
7841
+ ),
7842
+ children: [
7843
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn(panelSz.periodLabel, "font-bold uppercase tracking-wider text-muted-foreground/70 text-center"), children: "Period" }),
7844
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
7845
+ "div",
7846
+ {
7847
+ className: cn("flex flex-col p-1 rounded-xl bg-muted/30", panelSz.periodGap),
7848
+ role: "radiogroup",
7849
+ "aria-label": "Select AM or PM",
7850
+ tabIndex: focusedColumn === "period" ? 0 : -1,
7851
+ onKeyDown: (e) => handleKeyDown(e, "period"),
7852
+ onFocus: () => setFocusedColumn("period"),
7853
+ children: ["AM", "PM"].map((p) => {
7854
+ const isSelected = parts.p === p;
7855
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
7856
+ "button",
7857
+ {
7858
+ type: "button",
7859
+ role: "radio",
7860
+ "aria-checked": isSelected,
7861
+ className: cn(
7862
+ "relative rounded-lg transition-all duration-300 font-bold overflow-hidden",
7863
+ panelSz.periodButton,
7864
+ "focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-1",
7865
+ isSelected && "bg-linear-to-r from-primary to-primary/80 text-primary-foreground shadow-lg shadow-primary/25",
7866
+ !isSelected && "text-muted-foreground hover:text-foreground hover:bg-muted/50",
7867
+ animate && "hover:scale-[1.02] active:scale-[0.98]"
7868
+ ),
7869
+ onClick: () => {
7870
+ const pVal = p;
7871
+ let hour = parts.h;
7872
+ if (pVal === "AM" && hour >= 12) hour -= 12;
7873
+ if (pVal === "PM" && hour < 12) hour += 12;
7874
+ const next = { ...parts, p: pVal, h: hour };
7875
+ setParts(next);
7876
+ emit(next);
7877
+ },
7878
+ children: [
7879
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "absolute inset-0 bg-linear-to-tr from-white/20 to-transparent" }),
7880
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "relative", children: p })
7881
+ ]
7882
+ },
7883
+ p
7884
+ );
7885
+ })
7886
+ }
7887
+ )
7888
+ ]
7889
+ }
7890
+ )
7732
7891
  ] }),
7733
7892
  (showNow || clearable) && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex items-center gap-2 pt-3 border-t border-border/50", children: [
7734
7893
  showNow && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
@@ -7736,7 +7895,9 @@ function TimePicker({
7736
7895
  {
7737
7896
  type: "button",
7738
7897
  className: cn(
7739
- "flex-1 px-4 py-2.5 text-xs font-semibold rounded-xl",
7898
+ "flex-1 font-semibold rounded-xl",
7899
+ panelSz.actionPadding,
7900
+ panelSz.actionText,
7740
7901
  "bg-linear-to-r from-primary/10 to-primary/5 border border-primary/30",
7741
7902
  "text-primary hover:from-primary/20 hover:to-primary/10 hover:border-primary/50",
7742
7903
  "transition-all duration-300 flex items-center justify-center gap-2",
@@ -7748,7 +7909,7 @@ function TimePicker({
7748
7909
  },
7749
7910
  "aria-label": "Set current time",
7750
7911
  children: [
7751
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.Clock, { className: "w-3.5 h-3.5" }),
7912
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.Clock, { className: panelSz.icon }),
7752
7913
  "Now"
7753
7914
  ]
7754
7915
  }
@@ -7758,7 +7919,9 @@ function TimePicker({
7758
7919
  {
7759
7920
  type: "button",
7760
7921
  className: cn(
7761
- "flex-1 px-4 py-2.5 text-xs font-semibold rounded-xl",
7922
+ "flex-1 font-semibold rounded-xl",
7923
+ panelSz.actionPadding,
7924
+ panelSz.actionText,
7762
7925
  "bg-linear-to-r from-destructive/10 to-destructive/5 border border-destructive/30",
7763
7926
  "text-destructive hover:from-destructive/20 hover:to-destructive/10 hover:border-destructive/50",
7764
7927
  "transition-all duration-300 flex items-center justify-center gap-2",
@@ -7771,7 +7934,7 @@ function TimePicker({
7771
7934
  },
7772
7935
  "aria-label": "Clear selected time",
7773
7936
  children: [
7774
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.X, { className: "w-3.5 h-3.5" }),
7937
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react16.X, { className: panelSz.icon }),
7775
7938
  "Clear"
7776
7939
  ]
7777
7940
  }
@@ -7784,7 +7947,7 @@ function TimePicker({
7784
7947
  label,
7785
7948
  required && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-destructive ml-1", children: "*" })
7786
7949
  ] }) }),
7787
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn("p-5 rounded-2xl border border-border/60 bg-card/95 backdrop-blur-sm shadow-xl", className), children: timePickerContent })
7950
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn(panelSz.contentPadding, "rounded-2xl border border-border/60 bg-card/95 backdrop-blur-sm shadow-xl", className), children: timePickerContent })
7788
7951
  ] });
7789
7952
  }
7790
7953
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "w-full", ...rest, children: [
@@ -7811,9 +7974,11 @@ function TimePicker({
7811
7974
  open,
7812
7975
  onOpenChange: handleOpenChange,
7813
7976
  placement: "bottom-start",
7814
- contentWidth,
7977
+ matchTriggerWidth,
7978
+ contentWidth: matchTriggerWidth ? void 0 : contentWidth,
7815
7979
  contentClassName: cn(
7816
- "p-5 rounded-2xl border bg-popover/95 backdrop-blur-xl shadow-2xl",
7980
+ panelSz.contentPadding,
7981
+ "rounded-2xl border bg-popover/98 backdrop-blur-md shadow-2xl",
7817
7982
  error && "border-destructive/40",
7818
7983
  success && "border-success/40",
7819
7984
  !error && !success && "border-border/60",
@@ -8165,7 +8330,7 @@ function getZonedWeekday(date, timeZone) {
8165
8330
  }
8166
8331
 
8167
8332
  // ../../components/ui/CalendarTimeline/layout.ts
8168
- function clamp3(n, min, max) {
8333
+ function clamp4(n, min, max) {
8169
8334
  return Math.max(min, Math.min(max, n));
8170
8335
  }
8171
8336
  function binarySearchFirstGE(arr, target) {
@@ -8304,7 +8469,7 @@ function useVirtualVariableRows(args) {
8304
8469
  const endPos = Math.max(0, Math.min(scrollTop + viewportHeight, total));
8305
8470
  let startIndex = Math.max(0, lowerBound(prefix, startPos) - 1);
8306
8471
  let endIndex = Math.min(itemCount, lowerBound(prefix, endPos) + overscan);
8307
- startIndex = clamp3(startIndex - overscan, 0, itemCount);
8472
+ startIndex = clamp4(startIndex - overscan, 0, itemCount);
8308
8473
  const topSpacer = prefix[startIndex] ?? 0;
8309
8474
  const bottomSpacer = total - (prefix[endIndex] ?? total);
8310
8475
  return { startIndex, endIndex, topSpacer, bottomSpacer, totalHeight: total };
@@ -8456,11 +8621,11 @@ function computeSlotStarts(args) {
8456
8621
  const step = Math.max(5, Math.min(240, Math.trunc(dayTimeStepMinutes)));
8457
8622
  const stepMs = step * 6e4;
8458
8623
  const hours = workHours ?? { startHour: 8, endHour: 17 };
8459
- const boundedStartHour = clamp3(Math.trunc(hours.startHour), 0, 23);
8460
- const boundedEndHour = clamp3(Math.trunc(hours.endHour), 1, 24);
8624
+ const boundedStartHour = clamp4(Math.trunc(hours.startHour), 0, 23);
8625
+ const boundedEndHour = clamp4(Math.trunc(hours.endHour), 1, 24);
8461
8626
  const isWork = dayRangeMode === "work";
8462
8627
  const start2 = isWork ? zonedDateAtTime(baseDayStart, timeZone, { hour: boundedStartHour }) : start;
8463
- const end2 = isWork ? boundedEndHour === 24 ? addZonedDays(baseDayStart, 1, timeZone) : zonedDateAtTime(baseDayStart, timeZone, { hour: clamp3(boundedEndHour, 0, 23) }) : addZonedDays(start, 1, timeZone);
8628
+ const end2 = isWork ? boundedEndHour === 24 ? addZonedDays(baseDayStart, 1, timeZone) : zonedDateAtTime(baseDayStart, timeZone, { hour: clamp4(boundedEndHour, 0, 23) }) : addZonedDays(start, 1, timeZone);
8464
8629
  const end3 = end2.getTime() > start2.getTime() ? end2 : addZonedDays(start2, 1, timeZone);
8465
8630
  const slotStarts2 = [];
8466
8631
  for (let cur2 = start2.getTime(), guard2 = 0; cur2 < end3.getTime() && guard2++ < 2e3; cur2 += stepMs) {
@@ -8489,8 +8654,8 @@ function normalizeEvents(args) {
8489
8654
  const ns = view === "day" ? start : startOfZonedDay(start, timeZone);
8490
8655
  let ne = view === "day" ? end : startOfZonedDay(end, timeZone);
8491
8656
  if (ne.getTime() <= ns.getTime()) ne = view === "day" ? new Date(ns.getTime() + 6e4) : addZonedDays(ns, 1, timeZone);
8492
- const cs = new Date(clamp3(ns.getTime(), rangeStart, rangeEnd));
8493
- const ce = new Date(clamp3(ne.getTime(), rangeStart, rangeEnd));
8657
+ const cs = new Date(clamp4(ns.getTime(), rangeStart, rangeEnd));
8658
+ const ce = new Date(clamp4(ne.getTime(), rangeStart, rangeEnd));
8494
8659
  if (ce.getTime() <= rangeStart || cs.getTime() >= rangeEnd) return null;
8495
8660
  return { ...e, _start: cs, _end: ce };
8496
8661
  }).filter(Boolean);
@@ -8852,12 +9017,12 @@ function useDayHeaderMarks(args) {
8852
9017
  for (const ev of normalizedEvents) {
8853
9018
  const startIdx = binarySearchLastLE(slotStarts, ev._start);
8854
9019
  const endIdxRaw = binarySearchFirstGE(slotStarts, ev._end);
8855
- const endIdx = clamp3(endIdxRaw, 0, n - 1);
9020
+ const endIdx = clamp4(endIdxRaw, 0, n - 1);
8856
9021
  if (startIdx >= 0 && startIdx < n) showTime[startIdx] = true;
8857
9022
  if (endIdx >= 0 && endIdx < n) showTime[endIdx] = true;
8858
9023
  const span = endIdx - startIdx;
8859
9024
  if (span >= 3) {
8860
- const mid = clamp3(Math.floor((startIdx + endIdx) / 2), 0, n - 1);
9025
+ const mid = clamp4(Math.floor((startIdx + endIdx) / 2), 0, n - 1);
8861
9026
  if (!showTime[mid]) showEllipsis[mid] = true;
8862
9027
  }
8863
9028
  }
@@ -8896,7 +9061,7 @@ function useSlotMetrics(args) {
8896
9061
  lefts2[0] = 0;
8897
9062
  for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
8898
9063
  const gridWidth2 = lefts2[n] ?? 0;
8899
- const xToSlotIdx2 = (x) => clamp3(Math.floor(x / Math.max(1, fixedSlotWidth)), 0, Math.max(0, n - 1));
9064
+ const xToSlotIdx2 = (x) => clamp4(Math.floor(x / Math.max(1, fixedSlotWidth)), 0, Math.max(0, n - 1));
8900
9065
  return { slotWidths: widths, slotLefts: lefts2, slotHasEvent: null, gridWidth: gridWidth2, xToSlotIdx: xToSlotIdx2 };
8901
9066
  }
8902
9067
  const cfg = typeof adaptiveCfg === "object" ? adaptiveCfg : {};
@@ -8922,7 +9087,7 @@ function useSlotMetrics(args) {
8922
9087
  };
8923
9088
  if (dayAnchorCompression) {
8924
9089
  const hasEvent2 = dayHeaderMarks.anchor.slice(0, n);
8925
- const compressedEmptySlotWidth = clamp3(emptySlotWidth, 12, 20);
9090
+ const compressedEmptySlotWidth = clamp4(emptySlotWidth, 12, 20);
8926
9091
  for (let i = 0; i < n; i++) widths[i] = hasEvent2[i] ? fixedSlotWidth : compressedEmptySlotWidth;
8927
9092
  maybeFillToContainer(hasEvent2);
8928
9093
  const lefts2 = new Array(n + 1);
@@ -8930,7 +9095,7 @@ function useSlotMetrics(args) {
8930
9095
  for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
8931
9096
  const gridWidth2 = lefts2[n] ?? 0;
8932
9097
  const xToSlotIdx2 = (x) => {
8933
- const xc = clamp3(x, 0, Math.max(0, gridWidth2 - 1e-3));
9098
+ const xc = clamp4(x, 0, Math.max(0, gridWidth2 - 1e-3));
8934
9099
  let lo = 0;
8935
9100
  let hi = n - 1;
8936
9101
  while (lo <= hi) {
@@ -8941,14 +9106,14 @@ function useSlotMetrics(args) {
8941
9106
  else if (xc >= right) lo = mid + 1;
8942
9107
  else return mid;
8943
9108
  }
8944
- return clamp3(lo, 0, Math.max(0, n - 1));
9109
+ return clamp4(lo, 0, Math.max(0, n - 1));
8945
9110
  };
8946
9111
  return { slotWidths: widths, slotLefts: lefts2, slotHasEvent: hasEvent2, gridWidth: gridWidth2, xToSlotIdx: xToSlotIdx2 };
8947
9112
  }
8948
9113
  const diff = new Array(n + 1).fill(0);
8949
9114
  for (const ev of normalizedEvents) {
8950
9115
  const startIdx = binarySearchLastLE(slotStarts, ev._start);
8951
- const endIdx = clamp3(binarySearchFirstGE(slotStarts, ev._end), startIdx + 1, n);
9116
+ const endIdx = clamp4(binarySearchFirstGE(slotStarts, ev._end), startIdx + 1, n);
8952
9117
  diff[startIdx] = (diff[startIdx] ?? 0) + 1;
8953
9118
  diff[endIdx] = (diff[endIdx] ?? 0) - 1;
8954
9119
  }
@@ -8966,7 +9131,7 @@ function useSlotMetrics(args) {
8966
9131
  lefts2[0] = 0;
8967
9132
  for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
8968
9133
  const gridWidth2 = lefts2[n] ?? 0;
8969
- const xToSlotIdx2 = (x) => clamp3(Math.floor(x / Math.max(1, fixedSlotWidth)), 0, Math.max(0, n - 1));
9134
+ const xToSlotIdx2 = (x) => clamp4(Math.floor(x / Math.max(1, fixedSlotWidth)), 0, Math.max(0, n - 1));
8970
9135
  return { slotWidths: widths, slotLefts: lefts2, slotHasEvent: null, gridWidth: gridWidth2, xToSlotIdx: xToSlotIdx2 };
8971
9136
  }
8972
9137
  const emptyCount = n - eventCount;
@@ -8976,7 +9141,7 @@ function useSlotMetrics(args) {
8976
9141
  const remaining = Math.max(0, targetTotal - emptyCount * emptySlotWidth);
8977
9142
  const raw = remaining / Math.max(1, eventCount);
8978
9143
  const maxEventSlotWidth = cfg.maxEventSlotWidth ?? fixedSlotWidth * 2.5;
8979
- eventSlotWidth = clamp3(raw, fixedSlotWidth, Math.max(fixedSlotWidth, maxEventSlotWidth));
9144
+ eventSlotWidth = clamp4(raw, fixedSlotWidth, Math.max(fixedSlotWidth, maxEventSlotWidth));
8980
9145
  }
8981
9146
  for (let i = 0; i < n; i++) widths[i] = hasEvent[i] ? eventSlotWidth : emptySlotWidth;
8982
9147
  maybeFillToContainer(hasEvent);
@@ -8985,7 +9150,7 @@ function useSlotMetrics(args) {
8985
9150
  for (let i = 0; i < n; i++) lefts[i + 1] = lefts[i] + widths[i];
8986
9151
  const gridWidth = lefts[n] ?? 0;
8987
9152
  const xToSlotIdx = (x) => {
8988
- const xc = clamp3(x, 0, Math.max(0, gridWidth - 1e-3));
9153
+ const xc = clamp4(x, 0, Math.max(0, gridWidth - 1e-3));
8989
9154
  let lo = 0;
8990
9155
  let hi = n - 1;
8991
9156
  while (lo <= hi) {
@@ -8996,7 +9161,7 @@ function useSlotMetrics(args) {
8996
9161
  else if (xc >= right) lo = mid + 1;
8997
9162
  else return mid;
8998
9163
  }
8999
- return clamp3(lo, 0, Math.max(0, n - 1));
9164
+ return clamp4(lo, 0, Math.max(0, n - 1));
9000
9165
  };
9001
9166
  return { slotWidths: widths, slotLefts: lefts, slotHasEvent: hasEvent, gridWidth, xToSlotIdx };
9002
9167
  }, [
@@ -9023,7 +9188,7 @@ function useLayoutsByResource(args) {
9023
9188
  const s = isPreview ? preview.start : ev._start;
9024
9189
  const e = isPreview ? preview.end : ev._end;
9025
9190
  const startIdx = binarySearchLastLE(slotStarts, s);
9026
- const endIdx = clamp3(binarySearchFirstGE(slotStarts, e), startIdx + 1, slotsLength);
9191
+ const endIdx = clamp4(binarySearchFirstGE(slotStarts, e), startIdx + 1, slotsLength);
9027
9192
  return { ev: { ...ev, _start: s, _end: e }, startIdx, endIdx };
9028
9193
  });
9029
9194
  const { packed, laneCount } = intervalPack(mapped);
@@ -9087,8 +9252,8 @@ function useVisibleSlotRange(args) {
9087
9252
  const endPos = Math.max(0, scrollLeft + viewportWidth);
9088
9253
  let startIdx = Math.max(0, lowerBound2(slotLefts, startPos) - 1);
9089
9254
  let endIdx = Math.min(slotCount, lowerBound2(slotLefts, endPos) + 1);
9090
- startIdx = clamp3(startIdx - overscan, 0, slotCount);
9091
- endIdx = clamp3(endIdx + overscan, 0, slotCount);
9255
+ startIdx = clamp4(startIdx - overscan, 0, slotCount);
9256
+ endIdx = clamp4(endIdx + overscan, 0, slotCount);
9092
9257
  if (endIdx <= startIdx) endIdx = Math.min(slotCount, startIdx + 1);
9093
9258
  return { startIdx, endIdx };
9094
9259
  }, [enabled, overscan, scrollLeft, slotCount, slotLefts, viewportWidth]);
@@ -9411,7 +9576,7 @@ function CalendarTimeline({
9411
9576
  for (const [resourceId, list] of eventsByResource.entries()) {
9412
9577
  const mapped = list.map((ev) => {
9413
9578
  const startIdx = binarySearchLastLE(slotStarts, ev._start);
9414
- const endIdx = clamp3(binarySearchFirstGE(slotStarts, ev._end), startIdx + 1, slots.length);
9579
+ const endIdx = clamp4(binarySearchFirstGE(slotStarts, ev._end), startIdx + 1, slots.length);
9415
9580
  return { startIdx, endIdx };
9416
9581
  });
9417
9582
  const { laneCount } = intervalPack(mapped);
@@ -9434,7 +9599,7 @@ function CalendarTimeline({
9434
9599
  );
9435
9600
  const setRowHeightForResource = React31.useCallback(
9436
9601
  (resourceId, height) => {
9437
- const clamped = clamp3(Math.round(height), rowMin, rowMax);
9602
+ const clamped = clamp4(Math.round(height), rowMin, rowMax);
9438
9603
  onRowHeightChange?.(clamped);
9439
9604
  if (isControlledRowHeights) {
9440
9605
  const next = { ...activeRowHeights ?? {}, [resourceId]: clamped };
@@ -9473,7 +9638,7 @@ function CalendarTimeline({
9473
9638
  const resizeRef = React31.useRef(null);
9474
9639
  const setResourceColumnWidth = React31.useCallback(
9475
9640
  (next) => {
9476
- const clamped = clamp3(Math.round(next), colMin, colMax);
9641
+ const clamped = clamp4(Math.round(next), colMin, colMax);
9477
9642
  if (!isControlledResourceColumnWidth) setInternalResourceColumnWidth(clamped);
9478
9643
  onResourceColumnWidthChange?.(clamped);
9479
9644
  },
@@ -9596,10 +9761,10 @@ function CalendarTimeline({
9596
9761
  if (slots.length > 0) {
9597
9762
  if (activeView === "day") {
9598
9763
  const inRange = resolvedNow.getTime() >= range.start.getTime() && resolvedNow.getTime() < range.end.getTime();
9599
- startIdx = clamp3(inRange ? binarySearchFirstGE(slotStarts, resolvedNow) : 0, 0, slots.length - 1);
9764
+ startIdx = clamp4(inRange ? binarySearchFirstGE(slotStarts, resolvedNow) : 0, 0, slots.length - 1);
9600
9765
  } else {
9601
9766
  const dayStart = startOfZonedDay(activeDate, resolvedTimeZone);
9602
- startIdx = clamp3(binarySearchLastLE(slotStarts, dayStart), 0, slots.length - 1);
9767
+ startIdx = clamp4(binarySearchLastLE(slotStarts, dayStart), 0, slots.length - 1);
9603
9768
  }
9604
9769
  }
9605
9770
  setCreateStartIdx(startIdx);
@@ -9636,7 +9801,7 @@ function CalendarTimeline({
9636
9801
  const commitCreate = React31.useCallback(() => {
9637
9802
  if (!onCreateEvent) return;
9638
9803
  if (!createResourceId) return;
9639
- const start = slotStarts[clamp3(createStartIdx, 0, Math.max(0, slotStarts.length - 1))];
9804
+ const start = slotStarts[clamp4(createStartIdx, 0, Math.max(0, slotStarts.length - 1))];
9640
9805
  if (!start) return;
9641
9806
  const endBoundary = createEndIdx >= slotStarts.length ? range.end : slotStarts[createEndIdx];
9642
9807
  if (!endBoundary) return;
@@ -9666,8 +9831,8 @@ function CalendarTimeline({
9666
9831
  const body = bodyRef.current;
9667
9832
  if (!body) return null;
9668
9833
  const bodyRect = body.getBoundingClientRect();
9669
- const probeX = clamp3(clientX, bodyRect.left + 1, bodyRect.right - 1);
9670
- const probeY = clamp3(clientY, bodyRect.top + 1, bodyRect.bottom - 1);
9834
+ const probeX = clamp4(clientX, bodyRect.left + 1, bodyRect.right - 1);
9835
+ const probeY = clamp4(clientY, bodyRect.top + 1, bodyRect.bottom - 1);
9671
9836
  const el = document.elementFromPoint(probeX, probeY);
9672
9837
  const x = probeX - bodyRect.left + body.scrollLeft;
9673
9838
  const epsilon = opts?.biasLeft ? 0.01 : 0;
@@ -9681,7 +9846,7 @@ function CalendarTimeline({
9681
9846
  );
9682
9847
  const slotToDate = React31.useCallback(
9683
9848
  (slotIdx) => {
9684
- const start = slotStarts[clamp3(slotIdx, 0, slotStarts.length - 1)];
9849
+ const start = slotStarts[clamp4(slotIdx, 0, slotStarts.length - 1)];
9685
9850
  if (activeView === "day") {
9686
9851
  const stepMs = Math.trunc(Math.max(5, Math.min(240, Math.trunc(dayTimeStepMinutes))) * 6e4);
9687
9852
  return { start, end: new Date(start.getTime() + stepMs) };
@@ -9722,12 +9887,12 @@ function CalendarTimeline({
9722
9887
  return;
9723
9888
  }
9724
9889
  if (drag.mode === "resize-start") {
9725
- const nextStart = new Date(clamp3(targetSlotStart.getTime(), range.start.getTime(), drag.originEnd.getTime() - 6e4));
9890
+ const nextStart = new Date(clamp4(targetSlotStart.getTime(), range.start.getTime(), drag.originEnd.getTime() - 6e4));
9726
9891
  setPreview({ eventId: drag.eventId, resourceId: drag.resourceId, start: nextStart, end: drag.originEnd });
9727
9892
  return;
9728
9893
  }
9729
9894
  if (drag.mode === "resize-end") {
9730
- const nextEnd = new Date(clamp3(targetSlotStart.getTime(), drag.originStart.getTime() + 6e4, range.end.getTime()));
9895
+ const nextEnd = new Date(clamp4(targetSlotStart.getTime(), drag.originStart.getTime() + 6e4, range.end.getTime()));
9731
9896
  setPreview({ eventId: drag.eventId, resourceId: drag.resourceId, start: drag.originStart, end: nextEnd });
9732
9897
  return;
9733
9898
  }
@@ -9744,7 +9909,7 @@ function CalendarTimeline({
9744
9909
  }
9745
9910
  const maxScrollLeft = Math.max(0, body.scrollWidth - body.clientWidth);
9746
9911
  const prevLeft = body.scrollLeft;
9747
- const nextLeft = clamp3(prevLeft + st.dir * st.speed, 0, maxScrollLeft);
9912
+ const nextLeft = clamp4(prevLeft + st.dir * st.speed, 0, maxScrollLeft);
9748
9913
  if (nextLeft === prevLeft) {
9749
9914
  stopAutoScroll();
9750
9915
  return;
@@ -9764,12 +9929,12 @@ function CalendarTimeline({
9764
9929
  if (clientX < rect.left + edge) {
9765
9930
  dir = -1;
9766
9931
  const dist = clientX - rect.left;
9767
- const t2 = clamp3(1 - dist / edge, 0, 1);
9932
+ const t2 = clamp4(1 - dist / edge, 0, 1);
9768
9933
  speed = 8 + t2 * 28;
9769
9934
  } else if (clientX > rect.right - edge) {
9770
9935
  dir = 1;
9771
9936
  const dist = rect.right - clientX;
9772
- const t2 = clamp3(1 - dist / edge, 0, 1);
9937
+ const t2 = clamp4(1 - dist / edge, 0, 1);
9773
9938
  speed = 8 + t2 * 28;
9774
9939
  }
9775
9940
  autoScrollStateRef.current.lastClientX = clientX;
@@ -9880,7 +10045,7 @@ function CalendarTimeline({
9880
10045
  return;
9881
10046
  }
9882
10047
  const rect = rowEl.getBoundingClientRect();
9883
- const y = clamp3(e.clientY - rect.top, 0, rect.height);
10048
+ const y = clamp4(e.clientY - rect.top, 0, rect.height);
9884
10049
  if (!hoverCell || hoverCell.resourceId !== ctx.resourceId || hoverCell.slotIdx !== ctx.slotIdx || Math.abs(hoverCell.y - y) > 0.5) {
9885
10050
  setHoverCell({ resourceId: ctx.resourceId, slotIdx: ctx.slotIdx, y });
9886
10051
  }
@@ -10173,8 +10338,8 @@ function CalendarTimeline({
10173
10338
  const resource = resourceById.get(ev.resourceId);
10174
10339
  const tooltipTitle = ev.title || ev.id;
10175
10340
  const shouldCompact = activeView === "day" && dayEventStyle === "compact";
10176
- const defaultMaxVisual = clamp3(Math.round(fixedSlotWidth * 1.2), 160, 360);
10177
- const maxVisual = clamp3(Math.round(dayEventMaxWidth ?? defaultMaxVisual), 80, 1200);
10341
+ const defaultMaxVisual = clamp4(Math.round(fixedSlotWidth * 1.2), 160, 360);
10342
+ const maxVisual = clamp4(Math.round(dayEventMaxWidth ?? defaultMaxVisual), 80, 1200);
10178
10343
  const visualWidth = shouldCompact ? Math.min(width, maxVisual) : width;
10179
10344
  const isClipped = shouldCompact && width > visualWidth + 1;
10180
10345
  const block = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
@@ -10264,7 +10429,7 @@ function CalendarTimeline({
10264
10429
  }),
10265
10430
  preview && preview.resourceId === r.id && !preview.eventId ? (() => {
10266
10431
  const startIdx = binarySearchLastLE(slotStarts, preview.start);
10267
- const endIdx = clamp3(binarySearchFirstGE(slotStarts, preview.end), startIdx + 1, slots.length);
10432
+ const endIdx = clamp4(binarySearchFirstGE(slotStarts, preview.end), startIdx + 1, slots.length);
10268
10433
  const left = slotLefts[startIdx] ?? 0;
10269
10434
  const width = Math.max(1, (slotLefts[endIdx] ?? 0) - (slotLefts[startIdx] ?? 0));
10270
10435
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
@@ -10287,7 +10452,7 @@ function CalendarTimeline({
10287
10452
  ),
10288
10453
  style: {
10289
10454
  left: (slotLefts[hoverCell.slotIdx] ?? 0) + (slotWidths[hoverCell.slotIdx] ?? fixedSlotWidth) / 2 - 10,
10290
- top: clamp3(Math.round(hoverCell.y - 10), 6, Math.max(6, h - 26))
10455
+ top: clamp4(Math.round(hoverCell.y - 10), 6, Math.max(6, h - 26))
10291
10456
  },
10292
10457
  "aria-hidden": true,
10293
10458
  children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react21.Plus, { className: "h-3.5 w-3.5 text-muted-foreground" })
@@ -13523,7 +13688,7 @@ var Timeline_default = Timeline;
13523
13688
  var React42 = __toESM(require("react"), 1);
13524
13689
  var import_lucide_react29 = require("lucide-react");
13525
13690
  var import_jsx_runtime50 = require("react/jsx-runtime");
13526
- var clamp4 = (n, min, max) => Math.max(min, Math.min(max, n));
13691
+ var clamp5 = (n, min, max) => Math.max(min, Math.min(max, n));
13527
13692
  function hexToRgb(hex) {
13528
13693
  const str = hex.replace(/^#/, "").trim();
13529
13694
  if (str.length === 3) {
@@ -13541,7 +13706,7 @@ function hexToRgb(hex) {
13541
13706
  return null;
13542
13707
  }
13543
13708
  function rgbToHex(r, g, b) {
13544
- return `#${[r, g, b].map((v) => clamp4(Math.round(v), 0, 255).toString(16).padStart(2, "0")).join("")}`;
13709
+ return `#${[r, g, b].map((v) => clamp5(Math.round(v), 0, 255).toString(16).padStart(2, "0")).join("")}`;
13545
13710
  }
13546
13711
  function rgbToHsl(r, g, b) {
13547
13712
  r /= 255;
@@ -13602,10 +13767,10 @@ function parseAnyColor(input) {
13602
13767
  }
13603
13768
  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);
13604
13769
  if (rgbaMatch) {
13605
- const r = clamp4(parseInt(rgbaMatch[1], 10), 0, 255);
13606
- const g = clamp4(parseInt(rgbaMatch[2], 10), 0, 255);
13607
- const b = clamp4(parseInt(rgbaMatch[3], 10), 0, 255);
13608
- const a = rgbaMatch[4] != null ? clamp4(parseFloat(rgbaMatch[4]), 0, 1) : 1;
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;
13609
13774
  return { r, g, b, a };
13610
13775
  }
13611
13776
  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);
@@ -13613,7 +13778,7 @@ function parseAnyColor(input) {
13613
13778
  const h = parseFloat(hslaMatch[1]);
13614
13779
  const sl = parseFloat(hslaMatch[2]);
13615
13780
  const l = parseFloat(hslaMatch[3]);
13616
- const a = hslaMatch[4] != null ? clamp4(parseFloat(hslaMatch[4]), 0, 1) : 1;
13781
+ const a = hslaMatch[4] != null ? clamp5(parseFloat(hslaMatch[4]), 0, 1) : 1;
13617
13782
  const rgb = hslToRgb(h, sl, l);
13618
13783
  return { ...rgb, a };
13619
13784
  }
@@ -13626,12 +13791,12 @@ function formatOutput({ r, g, b, a }, withAlpha, format) {
13626
13791
  if (format === "hsl" || format === "hsla") {
13627
13792
  const hsl = rgbToHsl(r, g, b);
13628
13793
  if (format === "hsla" || withAlpha) {
13629
- return `hsla(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%, ${clamp4(a, 0, 1)})`;
13794
+ return `hsla(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%, ${clamp5(a, 0, 1)})`;
13630
13795
  }
13631
13796
  return `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s)}%, ${Math.round(hsl.l)}%)`;
13632
13797
  }
13633
13798
  if (withAlpha || a !== 1) {
13634
- return `rgba(${clamp4(r, 0, 255)}, ${clamp4(g, 0, 255)}, ${clamp4(b, 0, 255)}, ${clamp4(a, 0, 1)})`;
13799
+ return `rgba(${clamp5(r, 0, 255)}, ${clamp5(g, 0, 255)}, ${clamp5(b, 0, 255)}, ${clamp5(a, 0, 1)})`;
13635
13800
  }
13636
13801
  return rgbToHex(r, g, b);
13637
13802
  }
@@ -13765,7 +13930,7 @@ function ColorPicker({
13765
13930
  emit(next);
13766
13931
  };
13767
13932
  const setAlpha = (aPct) => {
13768
- const a = clamp4(aPct / 100, 0, 1);
13933
+ const a = clamp5(aPct / 100, 0, 1);
13769
13934
  const next = { ...rgba, a };
13770
13935
  setRgba(next);
13771
13936
  emit(next);
@@ -18426,7 +18591,7 @@ function toNullableNumber(value) {
18426
18591
  }
18427
18592
  return null;
18428
18593
  }
18429
- function clamp5(value, min, max) {
18594
+ function clamp6(value, min, max) {
18430
18595
  return Math.min(max, Math.max(min, value));
18431
18596
  }
18432
18597
  function ResizableImageNodeView(props) {
@@ -18488,18 +18653,18 @@ function ResizableImageNodeView(props) {
18488
18653
  let nextH = drag.startH;
18489
18654
  if (event.ctrlKey) {
18490
18655
  if (Math.abs(dx) >= Math.abs(dy)) {
18491
- nextW = clamp5(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18492
- nextH = clamp5(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
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);
18493
18658
  } else {
18494
- nextH = clamp5(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
18495
- nextW = clamp5(nextH * drag.aspect, MIN_IMAGE_SIZE_PX, drag.maxW);
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);
18496
18661
  }
18497
18662
  } else {
18498
18663
  if (!drag.axis && (Math.abs(dx) > AXIS_LOCK_THRESHOLD_PX || Math.abs(dy) > AXIS_LOCK_THRESHOLD_PX)) {
18499
18664
  drag.axis = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
18500
18665
  }
18501
- if (drag.axis === "x") nextW = clamp5(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
18502
- if (drag.axis === "y") nextH = clamp5(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
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);
18503
18668
  }
18504
18669
  drag.lastW = nextW;
18505
18670
  drag.lastH = nextH;