@underverse-ui/underverse 0.2.86 → 0.2.88

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
@@ -7802,6 +7802,18 @@ function getZonedParts(date, timeZone) {
7802
7802
  second: get("second")
7803
7803
  };
7804
7804
  }
7805
+ function getIsoWeekInfo(date, timeZone) {
7806
+ const p = getZonedParts(date, timeZone);
7807
+ const target = new Date(Date.UTC(p.year, p.month - 1, p.day));
7808
+ const dayNr = (target.getUTCDay() + 6) % 7;
7809
+ target.setUTCDate(target.getUTCDate() - dayNr + 3);
7810
+ const isoYear = target.getUTCFullYear();
7811
+ const firstThursday = new Date(Date.UTC(isoYear, 0, 4));
7812
+ const firstDayNr = (firstThursday.getUTCDay() + 6) % 7;
7813
+ firstThursday.setUTCDate(firstThursday.getUTCDate() - firstDayNr + 3);
7814
+ const week = 1 + Math.round((target.getTime() - firstThursday.getTime()) / (7 * 24 * 60 * 60 * 1e3));
7815
+ return { year: isoYear, week };
7816
+ }
7805
7817
  function partsToUtcMs(p) {
7806
7818
  return Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
7807
7819
  }
@@ -7891,41 +7903,48 @@ function intervalPack(items) {
7891
7903
  // ../../components/ui/CalendarTimeline/hooks.ts
7892
7904
  var React27 = __toESM(require("react"), 1);
7893
7905
  function useHorizontalScrollSync(args) {
7894
- const { bodyRef, headerRef } = args;
7906
+ const { bodyRef, headerRef, leftRef } = args;
7895
7907
  React27.useEffect(() => {
7896
7908
  const body = bodyRef.current;
7897
7909
  const header = headerRef.current;
7910
+ const left = leftRef?.current ?? null;
7898
7911
  if (!body || !header) return;
7899
7912
  let raf = 0;
7900
7913
  let syncing = false;
7901
- const syncHeader = () => {
7914
+ const syncFrom = (source) => {
7902
7915
  if (syncing) return;
7903
7916
  syncing = true;
7904
- header.scrollLeft = body.scrollLeft;
7905
- syncing = false;
7906
- };
7907
- const syncBody = () => {
7908
- if (syncing) return;
7909
- syncing = true;
7910
- body.scrollLeft = header.scrollLeft;
7911
- syncing = false;
7912
- };
7913
- const onBodyScroll = () => {
7914
- cancelAnimationFrame(raf);
7915
- raf = requestAnimationFrame(syncHeader);
7916
- };
7917
- const onHeaderScroll = () => {
7917
+ if (source === "header") {
7918
+ const x = header.scrollLeft;
7919
+ if (body.scrollLeft !== x) body.scrollLeft = x;
7920
+ } else if (source === "left" && left) {
7921
+ const y = left.scrollTop;
7922
+ if (body.scrollTop !== y) body.scrollTop = y;
7923
+ } else {
7924
+ const x = body.scrollLeft;
7925
+ const y = body.scrollTop;
7926
+ if (header.scrollLeft !== x) header.scrollLeft = x;
7927
+ if (left && left.scrollTop !== y) left.scrollTop = y;
7928
+ }
7918
7929
  cancelAnimationFrame(raf);
7919
- raf = requestAnimationFrame(syncBody);
7930
+ raf = requestAnimationFrame(() => {
7931
+ syncing = false;
7932
+ });
7920
7933
  };
7934
+ const onBodyScroll = () => syncFrom("body");
7935
+ const onHeaderScroll = () => syncFrom("header");
7936
+ const onLeftScroll = () => syncFrom("left");
7937
+ syncFrom("body");
7921
7938
  body.addEventListener("scroll", onBodyScroll, { passive: true });
7922
7939
  header.addEventListener("scroll", onHeaderScroll, { passive: true });
7940
+ left?.addEventListener("scroll", onLeftScroll, { passive: true });
7923
7941
  return () => {
7924
7942
  cancelAnimationFrame(raf);
7925
7943
  body.removeEventListener("scroll", onBodyScroll);
7926
7944
  header.removeEventListener("scroll", onHeaderScroll);
7945
+ left?.removeEventListener("scroll", onLeftScroll);
7927
7946
  };
7928
- }, [bodyRef, headerRef]);
7947
+ }, [bodyRef, headerRef, leftRef]);
7929
7948
  }
7930
7949
  function useVirtualRows(args) {
7931
7950
  const { enabled, overscan, rowHeight, itemCount, scrollRef } = args;
@@ -8239,12 +8258,34 @@ function CalendarTimeline({
8239
8258
  }
8240
8259
  return map;
8241
8260
  }, [normalizedEvents]);
8261
+ const resourceById = React28.useMemo(() => {
8262
+ const map = /* @__PURE__ */ new Map();
8263
+ for (const r of resources) map.set(r.id, r);
8264
+ return map;
8265
+ }, [resources]);
8266
+ const leftRef = React28.useRef(null);
8242
8267
  const bodyRef = React28.useRef(null);
8243
8268
  const headerRef = React28.useRef(null);
8244
- useHorizontalScrollSync({ bodyRef, headerRef });
8269
+ useHorizontalScrollSync({ bodyRef, headerRef, leftRef });
8245
8270
  const title = React28.useMemo(() => {
8246
- return formatters?.monthTitle?.(activeDate, { locale: resolvedLocale, timeZone: resolvedTimeZone }) ?? defaultMonthTitle(activeDate, resolvedLocale, resolvedTimeZone);
8247
- }, [activeDate, formatters, resolvedLocale, resolvedTimeZone]);
8271
+ if (activeView === "month") {
8272
+ return formatters?.monthTitle?.(activeDate, { locale: resolvedLocale, timeZone: resolvedTimeZone }) ?? defaultMonthTitle(activeDate, resolvedLocale, resolvedTimeZone);
8273
+ }
8274
+ if (activeView === "week") {
8275
+ const { week } = getIsoWeekInfo(range.start, resolvedTimeZone);
8276
+ const fmt2 = getDtf(resolvedLocale, resolvedTimeZone, { month: "short", day: "numeric" });
8277
+ const fmtYear = getDtf(resolvedLocale, resolvedTimeZone, { year: "numeric" });
8278
+ const endInclusive = new Date(range.end.getTime() - 1);
8279
+ const a = fmt2.format(range.start);
8280
+ const b = fmt2.format(endInclusive);
8281
+ const ya = fmtYear.format(range.start);
8282
+ const yb = fmtYear.format(endInclusive);
8283
+ const rangeText = ya === yb ? `${a} \u2013 ${b}, ${ya}` : `${a}, ${ya} \u2013 ${b}, ${yb}`;
8284
+ return `${l.week} ${week} \u2022 ${rangeText}`;
8285
+ }
8286
+ const fmt = getDtf(resolvedLocale, resolvedTimeZone, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
8287
+ return fmt.format(range.start);
8288
+ }, [activeDate, activeView, formatters, l.week, range.end, range.start, resolvedLocale, resolvedTimeZone]);
8248
8289
  const densityClass = sizeConfig.densityClass;
8249
8290
  const eventHeight = sizeConfig.eventHeight;
8250
8291
  const laneGap = sizeConfig.laneGap;
@@ -8270,10 +8311,9 @@ function CalendarTimeline({
8270
8311
  const body = bodyRef.current;
8271
8312
  if (!body) return null;
8272
8313
  const el = document.elementFromPoint(clientX, clientY);
8273
- const timelineEl = el?.closest?.("[data-uv-ct-timeline]");
8274
- if (!timelineEl) return null;
8275
- const timelineRect = timelineEl.getBoundingClientRect();
8276
- const x = clientX - timelineRect.left + body.scrollLeft;
8314
+ if (!el || !body.contains(el)) return null;
8315
+ const bodyRect = body.getBoundingClientRect();
8316
+ const x = clientX - bodyRect.left + body.scrollLeft;
8277
8317
  const slotIdx = clamp3(Math.floor(x / slotWidth), 0, Math.max(0, slots.length - 1));
8278
8318
  const rowEl = el?.closest?.("[data-uv-ct-row]");
8279
8319
  const rid = rowEl?.dataset?.uvCtRow ?? null;
@@ -8587,49 +8627,51 @@ function CalendarTimeline({
8587
8627
  ...rest,
8588
8628
  children: [
8589
8629
  Header,
8590
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
8591
- "div",
8592
- {
8593
- ref: bodyRef,
8594
- className: "relative overflow-auto scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent",
8595
- onPointerMove,
8596
- onPointerUp,
8597
- children: [
8598
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: topSpacer } }),
8599
- rows.slice(startRow, endRow).map((row, idx) => {
8600
- const rowIndex = startRow + idx;
8601
- if (row.kind === "group") {
8602
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex", style: { height: effectiveRowHeight }, children: [
8603
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8604
- "div",
8605
- {
8606
- className: "shrink-0 sticky left-0 z-20",
8607
- style: { width: effectiveResourceColumnWidth, minWidth: effectiveResourceColumnWidth },
8608
- children: renderGroupRow(row.group)
8609
- }
8610
- ),
8611
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 border-b border-border/30 bg-linear-to-r from-muted/15 to-muted/5", style: { minWidth: gridWidth } })
8612
- ] }, `g_${row.group.id}_${rowIndex}`);
8613
- }
8614
- const r = row.resource;
8615
- const layout = layoutsByResource.get(r.id) ?? { visible: [], hidden: [] };
8616
- const canMore = layout.hidden.length > 0 && !!onMoreClick;
8617
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
8618
- "div",
8619
- {
8620
- className: "flex group/row hover:bg-muted/5 transition-colors duration-150",
8621
- style: { height: effectiveRowHeight },
8622
- "data-uv-ct-row": r.id,
8623
- children: [
8624
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8625
- "div",
8626
- {
8627
- className: "shrink-0 sticky left-0 z-20 border-r border-border/30",
8628
- style: { width: effectiveResourceColumnWidth, minWidth: effectiveResourceColumnWidth },
8629
- children: ResourceCell(r)
8630
- }
8631
- ),
8632
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative shrink-0", style: { width: gridWidth, minWidth: gridWidth }, children: [
8630
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex min-h-0", children: [
8631
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
8632
+ "div",
8633
+ {
8634
+ ref: leftRef,
8635
+ className: "shrink-0 overflow-y-auto overflow-x-hidden scrollbar-none",
8636
+ style: { width: effectiveResourceColumnWidth, minWidth: effectiveResourceColumnWidth },
8637
+ children: [
8638
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: topSpacer } }),
8639
+ rows.slice(startRow, endRow).map((row, idx) => {
8640
+ const rowIndex = startRow + idx;
8641
+ if (row.kind === "group") {
8642
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: effectiveRowHeight }, children: renderGroupRow(row.group) }, `lg_${row.group.id}_${rowIndex}`);
8643
+ }
8644
+ const r = row.resource;
8645
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: effectiveRowHeight }, children: ResourceCell(r) }, `lr_${r.id}_${rowIndex}`);
8646
+ }),
8647
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: bottomSpacer } })
8648
+ ]
8649
+ }
8650
+ ),
8651
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
8652
+ "div",
8653
+ {
8654
+ ref: bodyRef,
8655
+ className: "relative flex-1 overflow-auto scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent",
8656
+ onPointerMove,
8657
+ onPointerUp,
8658
+ children: [
8659
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: topSpacer } }),
8660
+ rows.slice(startRow, endRow).map((row, idx) => {
8661
+ const rowIndex = startRow + idx;
8662
+ if (row.kind === "group") {
8663
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex", style: { height: effectiveRowHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "border-b border-border/30 bg-linear-to-r from-muted/15 to-muted/5", style: { width: gridWidth, minWidth: gridWidth } }) }, `rg_${row.group.id}_${rowIndex}`);
8664
+ }
8665
+ const r = row.resource;
8666
+ const layout = layoutsByResource.get(r.id) ?? { visible: [], hidden: [] };
8667
+ const canMore = layout.hidden.length > 0 && !!onMoreClick;
8668
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8669
+ "div",
8670
+ {
8671
+ className: "group/row hover:bg-muted/5 transition-colors duration-150",
8672
+ style: { height: effectiveRowHeight },
8673
+ "data-uv-ct-row": r.id,
8674
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative shrink-0", style: { width: gridWidth, minWidth: gridWidth, height: "100%" }, children: [
8633
8675
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "absolute inset-0", onPointerDown: onPointerDownCell, "data-uv-ct-timeline": true, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "absolute inset-0 flex", children: slots.map((s, i2) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
8634
8676
  "div",
8635
8677
  {
@@ -8659,7 +8701,14 @@ function CalendarTimeline({
8659
8701
  ev.title ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-semibold text-[11px] truncate leading-tight", children: ev.title }) : null,
8660
8702
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-[10px] opacity-70 truncate ml-auto", children: timeText })
8661
8703
  ] });
8662
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
8704
+ const resource = resourceById.get(ev.resourceId);
8705
+ const tooltipTitle = ev.title || ev.id;
8706
+ const tooltipContent = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
8707
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "font-semibold", children: tooltipTitle }),
8708
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs opacity-80", children: timeText }),
8709
+ resource?.label ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs opacity-70", children: resource.label }) : null
8710
+ ] });
8711
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Tooltip, { content: tooltipContent, placement: "top", delay: { open: 250, close: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
8663
8712
  "div",
8664
8713
  {
8665
8714
  className: cn(
@@ -8704,9 +8753,8 @@ function CalendarTimeline({
8704
8753
  ] }) : null,
8705
8754
  node
8706
8755
  ]
8707
- },
8708
- ev.id
8709
- );
8756
+ }
8757
+ ) }, ev.id);
8710
8758
  }),
8711
8759
  preview && preview.resourceId === r.id && !preview.eventId ? (() => {
8712
8760
  const startIdx = binarySearchLastLE(slotStarts, preview.start);
@@ -8741,15 +8789,15 @@ function CalendarTimeline({
8741
8789
  }
8742
8790
  ) : null
8743
8791
  ] })
8744
- ]
8745
- },
8746
- `r_${r.id}_${rowIndex}`
8747
- );
8748
- }),
8749
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: bottomSpacer } })
8750
- ]
8751
- }
8752
- )
8792
+ },
8793
+ `rr_${r.id}_${rowIndex}`
8794
+ );
8795
+ }),
8796
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: bottomSpacer } })
8797
+ ]
8798
+ }
8799
+ )
8800
+ ] })
8753
8801
  ]
8754
8802
  }
8755
8803
  );