@underverse-ui/underverse 0.2.109 → 0.2.111

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
@@ -6196,8 +6196,8 @@ var DatePicker = ({
6196
6196
  "div",
6197
6197
  {
6198
6198
  className: cn(
6199
- "flex items-center justify-center rounded-lg p-1.5 transition-all duration-300",
6200
- isOpen ? "bg-primary/15 text-primary" : "bg-muted/50 text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary"
6199
+ "flex items-center justify-center transition-colors duration-300",
6200
+ isOpen ? "text-primary" : "text-muted-foreground group-hover:text-primary"
6201
6201
  ),
6202
6202
  children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react14.Calendar, { className: cn(size === "sm" ? "h-3.5 w-3.5" : "h-4 w-4", "transition-transform duration-300", isOpen && "scale-110") })
6203
6203
  }
@@ -6492,9 +6492,8 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
6492
6492
  "div",
6493
6493
  {
6494
6494
  className: cn(
6495
- "flex items-center justify-center rounded-lg transition-all duration-300",
6496
- size === "sm" ? "p-1" : "p-1.5",
6497
- isOpen ? "bg-primary/15 text-primary" : "bg-muted/50 text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary"
6495
+ "flex items-center justify-center transition-colors duration-300",
6496
+ isOpen ? "text-primary" : "text-muted-foreground group-hover:text-primary"
6498
6497
  ),
6499
6498
  children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react14.Calendar, { className: cn("transition-transform duration-300", size === "sm" ? "h-3 w-3" : "h-4 w-4", isOpen && "scale-110") })
6500
6499
  }
@@ -7568,6 +7567,8 @@ function TimePicker({
7568
7567
  showPresets = false,
7569
7568
  allowManualInput = false,
7570
7569
  customPresets = [],
7570
+ min,
7571
+ max,
7571
7572
  minTime,
7572
7573
  maxTime,
7573
7574
  disabledTimes,
@@ -7604,37 +7605,65 @@ function TimePicker({
7604
7605
  },
7605
7606
  [disabledTimes]
7606
7607
  );
7608
+ const resolvedMinTime = minTime ?? min;
7609
+ const resolvedMaxTime = maxTime ?? max;
7610
+ const toSeconds = React25.useCallback(
7611
+ (p) => {
7612
+ let h = p.h;
7613
+ if (format === "12") {
7614
+ const period = p.p ?? (h >= 12 ? "PM" : "AM");
7615
+ const base = h % 12;
7616
+ h = period === "PM" ? base + 12 : base;
7617
+ }
7618
+ return h * 3600 + p.m * 60 + (includeSeconds ? p.s : 0);
7619
+ },
7620
+ [format, includeSeconds]
7621
+ );
7607
7622
  const isTimeInRange = React25.useCallback(
7608
7623
  (timeStr) => {
7609
- if (!minTime && !maxTime) return true;
7624
+ if (!resolvedMinTime && !resolvedMaxTime) return true;
7610
7625
  const parsed = parseTime(timeStr, format, includeSeconds);
7611
7626
  if (!parsed) return true;
7612
- if (minTime) {
7613
- const min = parseTime(minTime, format, includeSeconds);
7614
- if (min) {
7615
- const currentMinutes = parsed.h * 60 + parsed.m;
7616
- const minMinutes = min.h * 60 + min.m;
7617
- if (currentMinutes < minMinutes) return false;
7618
- }
7627
+ const current = toSeconds(parsed);
7628
+ if (resolvedMinTime) {
7629
+ const minParsed = parseTime(resolvedMinTime, format, includeSeconds);
7630
+ if (minParsed && current < toSeconds(minParsed)) return false;
7619
7631
  }
7620
- if (maxTime) {
7621
- const max = parseTime(maxTime, format, includeSeconds);
7622
- if (max) {
7623
- const currentMinutes = parsed.h * 60 + parsed.m;
7624
- const maxMinutes = max.h * 60 + max.m;
7625
- if (currentMinutes > maxMinutes) return false;
7626
- }
7632
+ if (resolvedMaxTime) {
7633
+ const maxParsed = parseTime(resolvedMaxTime, format, includeSeconds);
7634
+ if (maxParsed && current > toSeconds(maxParsed)) return false;
7627
7635
  }
7628
7636
  return true;
7629
7637
  },
7630
- [minTime, maxTime, format, includeSeconds]
7638
+ [format, includeSeconds, resolvedMaxTime, resolvedMinTime, toSeconds]
7639
+ );
7640
+ const canEmit = React25.useCallback(
7641
+ (next) => {
7642
+ const timeStr = next ? formatTime(next, format, includeSeconds) : void 0;
7643
+ if (!timeStr) return true;
7644
+ if (!isTimeInRange(timeStr)) return false;
7645
+ if (isTimeDisabled(timeStr)) return false;
7646
+ return true;
7647
+ },
7648
+ [format, includeSeconds, isTimeDisabled, isTimeInRange]
7649
+ );
7650
+ const emit = React25.useCallback(
7651
+ (next) => {
7652
+ const timeStr = next ? formatTime(next, format, includeSeconds) : void 0;
7653
+ if (!canEmit(next)) return;
7654
+ onChange?.(timeStr);
7655
+ },
7656
+ [canEmit, format, includeSeconds, onChange]
7657
+ );
7658
+ const tryUpdate = React25.useCallback(
7659
+ (next) => {
7660
+ if (!canEmit(next)) return false;
7661
+ setParts(next);
7662
+ emit(next);
7663
+ return true;
7664
+ },
7665
+ [canEmit, emit]
7631
7666
  );
7632
- const emit = (next) => {
7633
- const timeStr = next ? formatTime(next, format, includeSeconds) : void 0;
7634
- if (timeStr && !isTimeInRange(timeStr)) return;
7635
- if (timeStr && isTimeDisabled(timeStr)) return;
7636
- onChange?.(timeStr);
7637
- };
7638
7667
  const handleOpenChange = (newOpen) => {
7639
7668
  setOpen(newOpen);
7640
7669
  if (newOpen) {
@@ -7685,8 +7714,7 @@ function TimePicker({
7685
7714
  if (e.key === "ArrowLeft") setFocusedColumn(includeSeconds ? "second" : "minute");
7686
7715
  break;
7687
7716
  }
7688
- setParts(newParts);
7689
- emit(newParts);
7717
+ tryUpdate(newParts);
7690
7718
  };
7691
7719
  const setNow = () => {
7692
7720
  const now2 = /* @__PURE__ */ new Date();
@@ -7699,8 +7727,7 @@ function TimePicker({
7699
7727
  } else {
7700
7728
  next = { h, m, s };
7701
7729
  }
7702
- setParts(next);
7703
- emit(next);
7730
+ tryUpdate(next);
7704
7731
  };
7705
7732
  const setPreset = (preset) => {
7706
7733
  const { h, m, s } = PRESETS[preset];
@@ -7710,8 +7737,7 @@ function TimePicker({
7710
7737
  } else {
7711
7738
  next = { h, m, s };
7712
7739
  }
7713
- setParts(next);
7714
- emit(next);
7740
+ tryUpdate(next);
7715
7741
  };
7716
7742
  const handleManualInput = (input) => {
7717
7743
  setManualInput(input);
@@ -7719,16 +7745,14 @@ function TimePicker({
7719
7745
  if (parsed) {
7720
7746
  const timeStr = formatTime(parsed, format, includeSeconds);
7721
7747
  if (isTimeInRange(timeStr) && !isTimeDisabled(timeStr)) {
7722
- setParts(parsed);
7723
- emit(parsed);
7748
+ tryUpdate(parsed);
7724
7749
  }
7725
7750
  }
7726
7751
  };
7727
7752
  const handleCustomPreset = (time) => {
7728
7753
  const parsed = parseTime(time, format, includeSeconds);
7729
7754
  if (parsed) {
7730
- setParts(parsed);
7731
- emit(parsed);
7755
+ tryUpdate(parsed);
7732
7756
  }
7733
7757
  };
7734
7758
  const hours = format === "24" ? Array.from({ length: 24 }, (_, i) => i) : Array.from({ length: 12 }, (_, i) => i + 1);
@@ -7849,8 +7873,7 @@ function TimePicker({
7849
7873
  return period === "PM" ? base + 12 : base;
7850
7874
  })();
7851
7875
  const next = { ...parts, h: nextH, p: format === "12" ? period : parts.p };
7852
- setParts(next);
7853
- emit(next);
7876
+ tryUpdate(next);
7854
7877
  };
7855
7878
  const timePickerContent = /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: panelSz.stackGap, children: [
7856
7879
  /* @__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 }) }),
@@ -7956,8 +7979,7 @@ function TimePicker({
7956
7979
  valueIndex: minuteIndex,
7957
7980
  onSelect: (m) => {
7958
7981
  const next = { ...parts, m };
7959
- setParts(next);
7960
- emit(next);
7982
+ tryUpdate(next);
7961
7983
  },
7962
7984
  scrollRef: minuteScrollRef,
7963
7985
  itemHeight,
@@ -7985,8 +8007,7 @@ function TimePicker({
7985
8007
  valueIndex: secondIndex,
7986
8008
  onSelect: (s) => {
7987
8009
  const next = { ...parts, s };
7988
- setParts(next);
7989
- emit(next);
8010
+ tryUpdate(next);
7990
8011
  },
7991
8012
  scrollRef: secondScrollRef,
7992
8013
  itemHeight,
@@ -8039,8 +8060,7 @@ function TimePicker({
8039
8060
  if (pVal === "AM" && hour >= 12) hour -= 12;
8040
8061
  if (pVal === "PM" && hour < 12) hour += 12;
8041
8062
  const next = { ...parts, p: pVal, h: hour };
8042
- setParts(next);
8043
- emit(next);
8063
+ tryUpdate(next);
8044
8064
  },
8045
8065
  children: [
8046
8066
  isSelected && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "absolute inset-0 bg-linear-to-tr from-white/20 to-transparent" }),
@@ -8877,7 +8897,7 @@ function CalendarTimelineHeader(props) {
8877
8897
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "sticky top-0 z-30 bg-linear-to-b from-background via-background to-background/95 border-b border-border/40 backdrop-blur-xl", children: [
8878
8898
  /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: cn("flex items-center justify-between gap-4", sizeConfig.headerPaddingClass), children: [
8879
8899
  /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0", children: [
8880
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center bg-muted/40 rounded-xl p-1 gap-0.5", children: [
8900
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center bg-muted/40 rounded-full p-1 gap-0.5", children: [
8881
8901
  /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
8882
8902
  Button_default,
8883
8903
  {
@@ -8885,7 +8905,7 @@ function CalendarTimelineHeader(props) {
8885
8905
  size: "icon",
8886
8906
  onClick: () => navigate(-1),
8887
8907
  "aria-label": labels.prev,
8888
- className: cn(sizeConfig.controlButtonIconClass, "rounded-lg hover:bg-background/80 transition-all duration-200"),
8908
+ className: cn(sizeConfig.controlButtonIconClass, "rounded-full hover:bg-background/80 transition-all duration-200"),
8889
8909
  children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react18.ChevronLeft, { className: "h-4 w-4" })
8890
8910
  }
8891
8911
  ),
@@ -8895,7 +8915,7 @@ function CalendarTimelineHeader(props) {
8895
8915
  variant: "ghost",
8896
8916
  size: "sm",
8897
8917
  onClick: goToday,
8898
- className: cn(sizeConfig.controlButtonTextClass, "rounded-lg hover:bg-background/80 font-medium transition-all duration-200"),
8918
+ className: cn(sizeConfig.controlButtonTextClass, "rounded-full hover:bg-background/80 font-medium transition-all duration-200"),
8899
8919
  children: labels.today
8900
8920
  }
8901
8921
  ),
@@ -8906,7 +8926,7 @@ function CalendarTimelineHeader(props) {
8906
8926
  size: "icon",
8907
8927
  onClick: () => navigate(1),
8908
8928
  "aria-label": labels.next,
8909
- className: cn(sizeConfig.controlButtonIconClass, "rounded-lg hover:bg-background/80 transition-all duration-200"),
8929
+ className: cn(sizeConfig.controlButtonIconClass, "rounded-full hover:bg-background/80 transition-all duration-200"),
8910
8930
  children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react18.ChevronRight, { className: "h-4 w-4" })
8911
8931
  }
8912
8932
  )
@@ -8922,11 +8942,11 @@ function CalendarTimelineHeader(props) {
8922
8942
  icon: import_lucide_react18.Plus,
8923
8943
  disabled: newEventDisabled,
8924
8944
  onClick: onNewEventClick,
8925
- className: cn(sizeConfig.controlButtonTextClass, "rounded-lg font-medium transition-all duration-200 gap-1.5"),
8945
+ className: cn(sizeConfig.controlButtonTextClass, "rounded-full font-medium transition-all duration-200 gap-1.5"),
8926
8946
  children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "hidden sm:inline", children: newEventLabel })
8927
8947
  }
8928
8948
  ) : null,
8929
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex items-center bg-muted/40 rounded-xl p-1 gap-0.5", children: ["month", "week", "day"].map((v) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
8949
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex items-center bg-muted/40 rounded-full p-1 gap-0.5", children: ["month", "week", "day"].map((v) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
8930
8950
  Button_default,
8931
8951
  {
8932
8952
  variant: activeView === v ? "default" : "ghost",
@@ -8934,7 +8954,7 @@ function CalendarTimelineHeader(props) {
8934
8954
  onClick: () => setView(v),
8935
8955
  className: cn(
8936
8956
  sizeConfig.controlButtonTextClass,
8937
- "rounded-lg font-medium transition-all duration-200 gap-1.5",
8957
+ "rounded-full font-medium transition-all duration-200 gap-1.5",
8938
8958
  activeView === v ? "bg-primary text-primary-foreground shadow-sm shadow-primary/25" : "hover:bg-background/80 text-muted-foreground hover:text-foreground"
8939
8959
  ),
8940
8960
  children: [
@@ -10369,7 +10389,7 @@ function CalendarTimeline({
10369
10389
  "div",
10370
10390
  {
10371
10391
  className: cn(
10372
- "border border-border/40 rounded-2xl overflow-hidden bg-background/95 backdrop-blur-sm",
10392
+ "border border-border/40 rounded-2xl md:rounded-3xl overflow-hidden bg-background/95 backdrop-blur-sm",
10373
10393
  "shadow-sm hover:shadow-md transition-shadow duration-300",
10374
10394
  densityClass,
10375
10395
  className
@@ -10511,16 +10531,19 @@ function CalendarTimeline({
10511
10531
  const resource = resourceById.get(ev.resourceId);
10512
10532
  const tooltipTitle = ev.title || ev.id;
10513
10533
  const shouldCompact = activeView === "day" && dayEventStyle === "compact";
10534
+ const eventInsetX = 2;
10535
+ const leftInset = left + eventInsetX;
10536
+ const widthInset = Math.max(1, width - eventInsetX * 2);
10514
10537
  const defaultMaxVisual = clamp4(Math.round(fixedSlotWidth * 1.2), 160, 360);
10515
10538
  const maxVisual = clamp4(Math.round(dayEventMaxWidth ?? defaultMaxVisual), 80, 1200);
10516
- const visualWidth = shouldCompact ? Math.min(width, maxVisual) : width;
10517
- const isClipped = shouldCompact && width > visualWidth + 1;
10539
+ const visualWidth = shouldCompact ? Math.min(widthInset, maxVisual) : widthInset;
10540
+ const isClipped = shouldCompact && widthInset > visualWidth + 1;
10518
10541
  const block = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
10519
10542
  "div",
10520
10543
  {
10521
10544
  className: cn("absolute select-none cursor-pointer", isPreview && "z-10"),
10522
10545
  "data-uv-ct-event": true,
10523
- style: { left, top, width, height: layout.eventHeight },
10546
+ style: { left: leftInset, top, width: widthInset, height: layout.eventHeight },
10524
10547
  role: "button",
10525
10548
  tabIndex: 0,
10526
10549
  "aria-label": aria,
@@ -10605,11 +10628,17 @@ function CalendarTimeline({
10605
10628
  const endIdx = clamp4(binarySearchFirstGE(slotStarts, preview.end), startIdx + 1, slots.length);
10606
10629
  const left = slotLefts[startIdx] ?? 0;
10607
10630
  const width = Math.max(1, (slotLefts[endIdx] ?? 0) - (slotLefts[startIdx] ?? 0));
10631
+ const eventInsetX = 2;
10608
10632
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
10609
10633
  "div",
10610
10634
  {
10611
10635
  className: "absolute rounded-lg border-2 border-dashed border-primary/60 bg-primary/10 backdrop-blur-sm animate-pulse",
10612
- style: { left, top: layout.baseTop, width, height: layout.eventHeight }
10636
+ style: {
10637
+ left: left + eventInsetX,
10638
+ top: layout.baseTop,
10639
+ width: Math.max(1, width - eventInsetX * 2),
10640
+ height: layout.eventHeight
10641
+ }
10613
10642
  }
10614
10643
  );
10615
10644
  })() : null,