@underverse-ui/underverse 0.2.102 → 0.2.103

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
@@ -8763,6 +8763,8 @@ function CalendarTimeline({
8763
8763
  enableLayoutResize,
8764
8764
  slotMinWidth,
8765
8765
  adaptiveSlotWidths,
8766
+ dayEventStyle = "span",
8767
+ dayEventMaxWidth,
8766
8768
  dayTimeStepMinutes = 60,
8767
8769
  dayRangeMode,
8768
8770
  workHours,
@@ -8961,6 +8963,26 @@ function CalendarTimeline({
8961
8963
  const normalizedEvents = React28.useMemo(() => {
8962
8964
  return normalizeEvents({ events, range, view: activeView, timeZone: resolvedTimeZone });
8963
8965
  }, [events, range, activeView, resolvedTimeZone]);
8966
+ const dayHeaderMarks = React28.useMemo(() => {
8967
+ if (activeView !== "day") return null;
8968
+ const n = slots.length;
8969
+ const showTime = new Array(n).fill(false);
8970
+ const showEllipsis = new Array(n).fill(false);
8971
+ for (const ev of normalizedEvents) {
8972
+ const startIdx = binarySearchLastLE(slotStarts, ev._start);
8973
+ const endIdxRaw = binarySearchFirstGE(slotStarts, ev._end);
8974
+ const endIdx = clamp3(endIdxRaw, 0, n - 1);
8975
+ if (startIdx >= 0 && startIdx < n) showTime[startIdx] = true;
8976
+ if (endIdx >= 0 && endIdx < n) showTime[endIdx] = true;
8977
+ const span = endIdx - startIdx;
8978
+ if (span >= 3) {
8979
+ const mid = clamp3(Math.floor((startIdx + endIdx) / 2), 0, n - 1);
8980
+ if (!showTime[mid]) showEllipsis[mid] = true;
8981
+ }
8982
+ }
8983
+ const anchor = showTime.map((v, i) => v || showEllipsis[i]);
8984
+ return { showTime, showEllipsis, anchor };
8985
+ }, [activeView, normalizedEvents, slotStarts, slots.length]);
8964
8986
  const slotMetrics = React28.useMemo(() => {
8965
8987
  const n = slots.length;
8966
8988
  const widths = new Array(n).fill(fixedSlotWidth);
@@ -8973,12 +8995,38 @@ function CalendarTimeline({
8973
8995
  for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
8974
8996
  const gridWidth3 = lefts2[n] ?? 0;
8975
8997
  const xToSlotIdx3 = (x) => clamp3(Math.floor(x / Math.max(1, fixedSlotWidth)), 0, Math.max(0, n - 1));
8976
- return { slotWidths: widths, slotLefts: lefts2, gridWidth: gridWidth3, xToSlotIdx: xToSlotIdx3 };
8998
+ return { slotWidths: widths, slotLefts: lefts2, slotHasEvent: null, gridWidth: gridWidth3, xToSlotIdx: xToSlotIdx3 };
8977
8999
  }
8978
9000
  const cfg = typeof adaptiveCfg === "object" ? adaptiveCfg : {};
8979
9001
  const mode = cfg.mode ?? "shrink";
8980
9002
  const defaultEmptySlotWidth = Math.max(18, Math.round(effectiveSlotMinWidth * 0.6));
8981
- const emptySlotWidth = Math.max(12, Math.min(fixedSlotWidth, cfg.emptySlotWidth ?? defaultEmptySlotWidth));
9003
+ const minEmptySlotWidth = activeView === "month" ? Math.max(24, Math.round(effectiveSlotMinWidth * 0.45)) : 12;
9004
+ const emptySlotWidth = Math.max(minEmptySlotWidth, Math.min(fixedSlotWidth, cfg.emptySlotWidth ?? defaultEmptySlotWidth));
9005
+ const dayAnchorCompression = activeView === "day" && Boolean(dayHeaderMarks?.anchor);
9006
+ if (dayAnchorCompression) {
9007
+ const hasEvent2 = dayHeaderMarks.anchor.slice(0, n);
9008
+ const compressedEmptySlotWidth = clamp3(emptySlotWidth, 12, 20);
9009
+ for (let i = 0; i < n; i++) widths[i] = hasEvent2[i] ? fixedSlotWidth : compressedEmptySlotWidth;
9010
+ const lefts2 = new Array(n + 1);
9011
+ lefts2[0] = 0;
9012
+ for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
9013
+ const gridWidth3 = lefts2[n] ?? 0;
9014
+ const xToSlotIdx3 = (x) => {
9015
+ const xc = clamp3(x, 0, Math.max(0, gridWidth3 - 1e-3));
9016
+ let lo = 0;
9017
+ let hi = n - 1;
9018
+ while (lo <= hi) {
9019
+ const mid = lo + hi >> 1;
9020
+ const left = lefts2[mid] ?? 0;
9021
+ const right = lefts2[mid + 1] ?? gridWidth3;
9022
+ if (xc < left) hi = mid - 1;
9023
+ else if (xc >= right) lo = mid + 1;
9024
+ else return mid;
9025
+ }
9026
+ return clamp3(lo, 0, Math.max(0, n - 1));
9027
+ };
9028
+ return { slotWidths: widths, slotLefts: lefts2, slotHasEvent: hasEvent2, gridWidth: gridWidth3, xToSlotIdx: xToSlotIdx3 };
9029
+ }
8982
9030
  const diff = new Array(n + 1).fill(0);
8983
9031
  for (const ev of normalizedEvents) {
8984
9032
  const startIdx = binarySearchLastLE(slotStarts, ev._start);
@@ -9001,7 +9049,7 @@ function CalendarTimeline({
9001
9049
  for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
9002
9050
  const gridWidth3 = lefts2[n] ?? 0;
9003
9051
  const xToSlotIdx3 = (x) => clamp3(Math.floor(x / Math.max(1, fixedSlotWidth)), 0, Math.max(0, n - 1));
9004
- return { slotWidths: widths, slotLefts: lefts2, gridWidth: gridWidth3, xToSlotIdx: xToSlotIdx3 };
9052
+ return { slotWidths: widths, slotLefts: lefts2, slotHasEvent: null, gridWidth: gridWidth3, xToSlotIdx: xToSlotIdx3 };
9005
9053
  }
9006
9054
  const emptyCount = n - eventCount;
9007
9055
  let eventSlotWidth = fixedSlotWidth;
@@ -9031,9 +9079,9 @@ function CalendarTimeline({
9031
9079
  }
9032
9080
  return clamp3(lo, 0, Math.max(0, n - 1));
9033
9081
  };
9034
- return { slotWidths: widths, slotLefts: lefts, gridWidth: gridWidth2, xToSlotIdx: xToSlotIdx2 };
9035
- }, [activeView, adaptiveSlotWidths, effectiveSlotMinWidth, fixedSlotWidth, normalizedEvents, slotStarts, slots.length]);
9036
- const { slotWidths, slotLefts, gridWidth, xToSlotIdx } = slotMetrics;
9082
+ return { slotWidths: widths, slotLefts: lefts, slotHasEvent: hasEvent, gridWidth: gridWidth2, xToSlotIdx: xToSlotIdx2 };
9083
+ }, [activeView, adaptiveSlotWidths, dayHeaderMarks, effectiveSlotMinWidth, fixedSlotWidth, normalizedEvents, slotStarts, slots.length]);
9084
+ const { slotWidths, slotLefts, slotHasEvent, gridWidth, xToSlotIdx } = slotMetrics;
9037
9085
  const eventsByResource = React28.useMemo(() => {
9038
9086
  return eventsByResourceId(normalizedEvents);
9039
9087
  }, [normalizedEvents]);
@@ -9625,16 +9673,24 @@ function CalendarTimeline({
9625
9673
  "div",
9626
9674
  {
9627
9675
  className: cn(
9628
- "shrink-0 border-l border-border/30 flex items-center justify-center transition-colors duration-150",
9676
+ "shrink-0 border-l flex items-center justify-center transition-colors duration-150 overflow-hidden",
9677
+ activeView === "day" && dayHeaderMarks?.anchor ? dayHeaderMarks.anchor[idx] ? "border-border/30" : "border-border/10" : "border-border/30",
9629
9678
  sizeConfig.slotHeaderClass,
9630
- s.isToday && "bg-primary/8 border-l-primary/40"
9679
+ activeView !== "day" && s.isToday && "bg-primary/8 border-l-primary/40"
9631
9680
  ),
9632
9681
  style: { width: slotWidths[idx], minWidth: slotWidths[idx] },
9633
9682
  "aria-label": formatters?.ariaSlotLabel?.(s.start, { view: activeView, locale: resolvedLocale, timeZone: resolvedTimeZone }),
9634
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("flex flex-col items-center", s.isToday && "relative"), children: [
9635
- s.isToday && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react20.Dot, { className: "absolute -top-2.5 h-4 w-4 text-primary animate-pulse" }),
9636
- s.label
9637
- ] })
9683
+ children: (() => {
9684
+ const label = typeof s.label === "string" || typeof s.label === "number" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "truncate whitespace-nowrap", children: s.label }) : s.label;
9685
+ if (activeView === "day" && dayHeaderMarks) {
9686
+ if (dayHeaderMarks.showEllipsis[idx]) return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-xs text-muted-foreground/70 select-none", children: "\u2026" });
9687
+ if (!dayHeaderMarks.showTime[idx]) return null;
9688
+ }
9689
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("flex flex-col items-center min-w-0 overflow-hidden", activeView !== "day" && s.isToday && "relative"), children: [
9690
+ activeView !== "day" && s.isToday && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react20.Dot, { className: "absolute -top-2.5 h-4 w-4 text-primary animate-pulse" }),
9691
+ label
9692
+ ] });
9693
+ })()
9638
9694
  },
9639
9695
  `${s.start.toISOString()}_${idx}`
9640
9696
  ))
@@ -9787,8 +9843,9 @@ function CalendarTimeline({
9787
9843
  "div",
9788
9844
  {
9789
9845
  className: cn(
9790
- "h-full border-l border-border/20 transition-colors duration-100",
9791
- s.isToday && "bg-primary/5 border-l-primary/30",
9846
+ "h-full border-l transition-colors duration-100",
9847
+ activeView === "day" && dayHeaderMarks?.anchor ? !dayHeaderMarks.anchor[i2] ? "border-border/10" : "border-border/20" : "border-border/20",
9848
+ activeView !== "day" && s.isToday && "bg-primary/5 border-l-primary/30",
9792
9849
  "hover:bg-muted/10"
9793
9850
  ),
9794
9851
  style: { width: slotWidths[i2], minWidth: slotWidths[i2] }
@@ -9835,28 +9892,17 @@ function CalendarTimeline({
9835
9892
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs opacity-80", children: timeText }),
9836
9893
  resource?.label ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs opacity-70", children: resource.label }) : null
9837
9894
  ] });
9895
+ const shouldCompact = activeView === "day" && dayEventStyle === "compact";
9896
+ const defaultMaxVisual = clamp3(Math.round(fixedSlotWidth * 1.2), 160, 360);
9897
+ const maxVisual = clamp3(Math.round(dayEventMaxWidth ?? defaultMaxVisual), 80, 1200);
9898
+ const visualWidth = shouldCompact ? Math.min(width, maxVisual) : width;
9899
+ const isClipped = shouldCompact && width > visualWidth + 1;
9838
9900
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Tooltip, { content: tooltipContent, placement: "top", delay: { open: 250, close: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
9839
9901
  "div",
9840
9902
  {
9841
- className: cn(
9842
- "absolute rounded-lg border select-none cursor-pointer",
9843
- "shadow-sm hover:shadow-md hover:scale-[1.02] hover:z-10",
9844
- "transition-all duration-150 ease-out",
9845
- "backdrop-blur-sm",
9846
- "overflow-hidden",
9847
- ev.className,
9848
- isPreview && "ring-2 ring-primary/50 ring-offset-1 ring-offset-background scale-[1.02] z-10"
9849
- ),
9903
+ className: cn("absolute select-none cursor-pointer", isPreview && "z-10"),
9850
9904
  "data-uv-ct-event": true,
9851
- style: {
9852
- left,
9853
- top,
9854
- width,
9855
- height: layout.eventHeight,
9856
- background: bg,
9857
- borderColor: border,
9858
- borderLeftWidth: 3
9859
- },
9905
+ style: { left, top, width, height: layout.eventHeight },
9860
9906
  role: "button",
9861
9907
  tabIndex: 0,
9862
9908
  "aria-label": aria,
@@ -9884,6 +9930,31 @@ function CalendarTimeline({
9884
9930
  onDoubleClick: () => onEventDoubleClick?.(ev),
9885
9931
  onPointerDown: (e) => onPointerDownEvent(e, ev, "move"),
9886
9932
  children: [
9933
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
9934
+ "div",
9935
+ {
9936
+ className: cn(
9937
+ "relative h-full rounded-lg border overflow-hidden",
9938
+ "shadow-sm hover:shadow-md hover:scale-[1.02]",
9939
+ "transition-all duration-150 ease-out",
9940
+ "backdrop-blur-sm",
9941
+ ev.className,
9942
+ isPreview && "ring-2 ring-primary/50 ring-offset-1 ring-offset-background scale-[1.02]"
9943
+ ),
9944
+ style: {
9945
+ width: visualWidth,
9946
+ maxWidth: "100%",
9947
+ height: "100%",
9948
+ background: bg,
9949
+ borderColor: border,
9950
+ borderLeftWidth: 3
9951
+ },
9952
+ children: [
9953
+ node,
9954
+ isClipped ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-10 bg-linear-to-l from-background/50 to-transparent flex items-center justify-end pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-xs text-muted-foreground/80", children: "\u2026" }) }) : null
9955
+ ]
9956
+ }
9957
+ ),
9887
9958
  !isViewOnly && (interactions?.resizableEvents ?? true) && ev.resizable !== false ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
9888
9959
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
9889
9960
  "div",
@@ -9899,8 +9970,7 @@ function CalendarTimeline({
9899
9970
  onPointerDown: (e) => onPointerDownEvent(e, ev, "resize-end")
9900
9971
  }
9901
9972
  )
9902
- ] }) : null,
9903
- node
9973
+ ] }) : null
9904
9974
  ]
9905
9975
  }
9906
9976
  ) }, ev.id);