@underverse-ui/underverse 0.2.87 → 0.2.89
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 +140 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +140 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
}
|
|
@@ -7995,6 +8007,15 @@ function CalendarTimeline({
|
|
|
7995
8007
|
resources,
|
|
7996
8008
|
events,
|
|
7997
8009
|
size = "md",
|
|
8010
|
+
enableEventSheet,
|
|
8011
|
+
eventSheetSize = "md",
|
|
8012
|
+
renderEventSheet,
|
|
8013
|
+
selectedEventId,
|
|
8014
|
+
defaultSelectedEventId,
|
|
8015
|
+
onSelectedEventIdChange,
|
|
8016
|
+
eventSheetOpen,
|
|
8017
|
+
defaultEventSheetOpen,
|
|
8018
|
+
onEventSheetOpenChange,
|
|
7998
8019
|
view,
|
|
7999
8020
|
defaultView = "month",
|
|
8000
8021
|
onViewChange,
|
|
@@ -8035,6 +8056,28 @@ function CalendarTimeline({
|
|
|
8035
8056
|
const detectedLocale = useLocale();
|
|
8036
8057
|
const resolvedLocale = React28.useMemo(() => localeToBCP47(locale ?? detectedLocale), [locale, detectedLocale]);
|
|
8037
8058
|
const resolvedTimeZone = React28.useMemo(() => timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC", [timeZone]);
|
|
8059
|
+
const effectiveEnableEventSheet = enableEventSheet ?? Boolean(renderEventSheet);
|
|
8060
|
+
const isControlledSelectedEventId = selectedEventId !== void 0;
|
|
8061
|
+
const [internalSelectedEventId, setInternalSelectedEventId] = React28.useState(defaultSelectedEventId ?? null);
|
|
8062
|
+
const activeSelectedEventId = isControlledSelectedEventId ? selectedEventId : internalSelectedEventId;
|
|
8063
|
+
const setSelectedEventId = React28.useCallback(
|
|
8064
|
+
(next) => {
|
|
8065
|
+
if (!isControlledSelectedEventId) setInternalSelectedEventId(next);
|
|
8066
|
+
onSelectedEventIdChange?.(next);
|
|
8067
|
+
},
|
|
8068
|
+
[isControlledSelectedEventId, onSelectedEventIdChange]
|
|
8069
|
+
);
|
|
8070
|
+
const isControlledEventSheetOpen = eventSheetOpen !== void 0;
|
|
8071
|
+
const [internalEventSheetOpen, setInternalEventSheetOpen] = React28.useState(defaultEventSheetOpen ?? false);
|
|
8072
|
+
const activeEventSheetOpen = isControlledEventSheetOpen ? Boolean(eventSheetOpen) : internalEventSheetOpen;
|
|
8073
|
+
const setEventSheetOpen = React28.useCallback(
|
|
8074
|
+
(next) => {
|
|
8075
|
+
if (!isControlledEventSheetOpen) setInternalEventSheetOpen(next);
|
|
8076
|
+
onEventSheetOpenChange?.(next);
|
|
8077
|
+
if (!next) setSelectedEventId(null);
|
|
8078
|
+
},
|
|
8079
|
+
[isControlledEventSheetOpen, onEventSheetOpenChange, setSelectedEventId]
|
|
8080
|
+
);
|
|
8038
8081
|
const sizeConfig = React28.useMemo(() => {
|
|
8039
8082
|
const cfgBySize = {
|
|
8040
8083
|
sm: {
|
|
@@ -8246,13 +8289,59 @@ function CalendarTimeline({
|
|
|
8246
8289
|
}
|
|
8247
8290
|
return map;
|
|
8248
8291
|
}, [normalizedEvents]);
|
|
8292
|
+
const resourceById = React28.useMemo(() => {
|
|
8293
|
+
const map = /* @__PURE__ */ new Map();
|
|
8294
|
+
for (const r of resources) map.set(r.id, r);
|
|
8295
|
+
return map;
|
|
8296
|
+
}, [resources]);
|
|
8297
|
+
const selectedEvent = React28.useMemo(() => {
|
|
8298
|
+
if (!activeSelectedEventId) return null;
|
|
8299
|
+
const found = normalizedEvents.find((e) => e.id === activeSelectedEventId);
|
|
8300
|
+
return found ?? null;
|
|
8301
|
+
}, [activeSelectedEventId, normalizedEvents]);
|
|
8302
|
+
const selectedResource = React28.useMemo(() => {
|
|
8303
|
+
if (!selectedEvent) return void 0;
|
|
8304
|
+
return resourceById.get(selectedEvent.resourceId);
|
|
8305
|
+
}, [resourceById, selectedEvent]);
|
|
8306
|
+
const selectedTimeText = React28.useMemo(() => {
|
|
8307
|
+
if (!selectedEvent) return "";
|
|
8308
|
+
return formatters?.eventTime?.({
|
|
8309
|
+
start: selectedEvent._start,
|
|
8310
|
+
end: selectedEvent._end,
|
|
8311
|
+
locale: resolvedLocale,
|
|
8312
|
+
timeZone: resolvedTimeZone,
|
|
8313
|
+
view: activeView
|
|
8314
|
+
}) ?? defaultEventTime({ start: selectedEvent._start, end: selectedEvent._end, locale: resolvedLocale, timeZone: resolvedTimeZone, view: activeView });
|
|
8315
|
+
}, [activeView, formatters, resolvedLocale, resolvedTimeZone, selectedEvent]);
|
|
8316
|
+
React28.useEffect(() => {
|
|
8317
|
+
if (!effectiveEnableEventSheet) return;
|
|
8318
|
+
if (activeEventSheetOpen && activeSelectedEventId && !selectedEvent) {
|
|
8319
|
+
setEventSheetOpen(false);
|
|
8320
|
+
}
|
|
8321
|
+
}, [activeEventSheetOpen, activeSelectedEventId, effectiveEnableEventSheet, selectedEvent, setEventSheetOpen]);
|
|
8249
8322
|
const leftRef = React28.useRef(null);
|
|
8250
8323
|
const bodyRef = React28.useRef(null);
|
|
8251
8324
|
const headerRef = React28.useRef(null);
|
|
8252
8325
|
useHorizontalScrollSync({ bodyRef, headerRef, leftRef });
|
|
8253
8326
|
const title = React28.useMemo(() => {
|
|
8254
|
-
|
|
8255
|
-
|
|
8327
|
+
if (activeView === "month") {
|
|
8328
|
+
return formatters?.monthTitle?.(activeDate, { locale: resolvedLocale, timeZone: resolvedTimeZone }) ?? defaultMonthTitle(activeDate, resolvedLocale, resolvedTimeZone);
|
|
8329
|
+
}
|
|
8330
|
+
if (activeView === "week") {
|
|
8331
|
+
const { week } = getIsoWeekInfo(range.start, resolvedTimeZone);
|
|
8332
|
+
const fmt2 = getDtf(resolvedLocale, resolvedTimeZone, { month: "short", day: "numeric" });
|
|
8333
|
+
const fmtYear = getDtf(resolvedLocale, resolvedTimeZone, { year: "numeric" });
|
|
8334
|
+
const endInclusive = new Date(range.end.getTime() - 1);
|
|
8335
|
+
const a = fmt2.format(range.start);
|
|
8336
|
+
const b = fmt2.format(endInclusive);
|
|
8337
|
+
const ya = fmtYear.format(range.start);
|
|
8338
|
+
const yb = fmtYear.format(endInclusive);
|
|
8339
|
+
const rangeText = ya === yb ? `${a} \u2013 ${b}, ${ya}` : `${a}, ${ya} \u2013 ${b}, ${yb}`;
|
|
8340
|
+
return `${l.week} ${week} \u2022 ${rangeText}`;
|
|
8341
|
+
}
|
|
8342
|
+
const fmt = getDtf(resolvedLocale, resolvedTimeZone, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
|
|
8343
|
+
return fmt.format(range.start);
|
|
8344
|
+
}, [activeDate, activeView, formatters, l.week, range.end, range.start, resolvedLocale, resolvedTimeZone]);
|
|
8256
8345
|
const densityClass = sizeConfig.densityClass;
|
|
8257
8346
|
const eventHeight = sizeConfig.eventHeight;
|
|
8258
8347
|
const laneGap = sizeConfig.laneGap;
|
|
@@ -8278,10 +8367,9 @@ function CalendarTimeline({
|
|
|
8278
8367
|
const body = bodyRef.current;
|
|
8279
8368
|
if (!body) return null;
|
|
8280
8369
|
const el = document.elementFromPoint(clientX, clientY);
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
const
|
|
8284
|
-
const x = clientX - timelineRect.left + body.scrollLeft;
|
|
8370
|
+
if (!el || !body.contains(el)) return null;
|
|
8371
|
+
const bodyRect = body.getBoundingClientRect();
|
|
8372
|
+
const x = clientX - bodyRect.left + body.scrollLeft;
|
|
8285
8373
|
const slotIdx = clamp3(Math.floor(x / slotWidth), 0, Math.max(0, slots.length - 1));
|
|
8286
8374
|
const rowEl = el?.closest?.("[data-uv-ct-row]");
|
|
8287
8375
|
const rid = rowEl?.dataset?.uvCtRow ?? null;
|
|
@@ -8669,7 +8757,14 @@ function CalendarTimeline({
|
|
|
8669
8757
|
ev.title ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-semibold text-[11px] truncate leading-tight", children: ev.title }) : null,
|
|
8670
8758
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-[10px] opacity-70 truncate ml-auto", children: timeText })
|
|
8671
8759
|
] });
|
|
8672
|
-
|
|
8760
|
+
const resource = resourceById.get(ev.resourceId);
|
|
8761
|
+
const tooltipTitle = ev.title || ev.id;
|
|
8762
|
+
const tooltipContent = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-0.5", children: [
|
|
8763
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "font-semibold", children: tooltipTitle }),
|
|
8764
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs opacity-80", children: timeText }),
|
|
8765
|
+
resource?.label ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs opacity-70", children: resource.label }) : null
|
|
8766
|
+
] });
|
|
8767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Tooltip, { content: tooltipContent, placement: "top", delay: { open: 250, close: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
8673
8768
|
"div",
|
|
8674
8769
|
{
|
|
8675
8770
|
className: cn(
|
|
@@ -8692,7 +8787,13 @@ function CalendarTimeline({
|
|
|
8692
8787
|
role: "button",
|
|
8693
8788
|
tabIndex: 0,
|
|
8694
8789
|
"aria-label": aria,
|
|
8695
|
-
onClick: () =>
|
|
8790
|
+
onClick: () => {
|
|
8791
|
+
onEventClick?.(ev);
|
|
8792
|
+
if (effectiveEnableEventSheet) {
|
|
8793
|
+
setSelectedEventId(ev.id);
|
|
8794
|
+
setEventSheetOpen(true);
|
|
8795
|
+
}
|
|
8796
|
+
},
|
|
8696
8797
|
onDoubleClick: () => onEventDoubleClick?.(ev),
|
|
8697
8798
|
onPointerDown: (e) => onPointerDownEvent(e, ev, "move"),
|
|
8698
8799
|
children: [
|
|
@@ -8714,9 +8815,8 @@ function CalendarTimeline({
|
|
|
8714
8815
|
] }) : null,
|
|
8715
8816
|
node
|
|
8716
8817
|
]
|
|
8717
|
-
}
|
|
8718
|
-
|
|
8719
|
-
);
|
|
8818
|
+
}
|
|
8819
|
+
) }, ev.id);
|
|
8720
8820
|
}),
|
|
8721
8821
|
preview && preview.resourceId === r.id && !preview.eventId ? (() => {
|
|
8722
8822
|
const startIdx = binarySearchLastLE(slotStarts, preview.start);
|
|
@@ -8759,7 +8859,35 @@ function CalendarTimeline({
|
|
|
8759
8859
|
]
|
|
8760
8860
|
}
|
|
8761
8861
|
)
|
|
8762
|
-
] })
|
|
8862
|
+
] }),
|
|
8863
|
+
effectiveEnableEventSheet && selectedEvent ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
8864
|
+
Sheet,
|
|
8865
|
+
{
|
|
8866
|
+
open: activeEventSheetOpen,
|
|
8867
|
+
onOpenChange: setEventSheetOpen,
|
|
8868
|
+
side: "right",
|
|
8869
|
+
size: eventSheetSize,
|
|
8870
|
+
title: selectedEvent.title ?? "Event",
|
|
8871
|
+
description: selectedTimeText || void 0,
|
|
8872
|
+
children: renderEventSheet ? renderEventSheet({
|
|
8873
|
+
event: selectedEvent,
|
|
8874
|
+
resource: selectedResource,
|
|
8875
|
+
close: () => setEventSheetOpen(false),
|
|
8876
|
+
locale: resolvedLocale,
|
|
8877
|
+
timeZone: resolvedTimeZone,
|
|
8878
|
+
view: activeView
|
|
8879
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "space-y-3", children: [
|
|
8880
|
+
selectedResource?.label ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { children: [
|
|
8881
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs text-muted-foreground", children: t("resourcesHeader") }),
|
|
8882
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "font-medium", children: selectedResource.label })
|
|
8883
|
+
] }) : null,
|
|
8884
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { children: [
|
|
8885
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "text-xs text-muted-foreground", children: "ID" }),
|
|
8886
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "font-mono text-xs break-all", children: selectedEvent.id })
|
|
8887
|
+
] })
|
|
8888
|
+
] })
|
|
8889
|
+
}
|
|
8890
|
+
) : null
|
|
8763
8891
|
]
|
|
8764
8892
|
}
|
|
8765
8893
|
);
|