@underverse-ui/underverse 0.2.92 → 0.2.94
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 +353 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -2
- package/dist/index.d.ts +53 -2
- package/dist/index.js +353 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6600,6 +6600,13 @@ function Calendar2({
|
|
|
6600
6600
|
maxEventsPerDay = 3,
|
|
6601
6601
|
onEventClick,
|
|
6602
6602
|
renderEvent,
|
|
6603
|
+
enableEventSheet,
|
|
6604
|
+
eventSheetSize = "md",
|
|
6605
|
+
renderEventSheet,
|
|
6606
|
+
selectedEventId,
|
|
6607
|
+
eventSheetOpen,
|
|
6608
|
+
onEventSheetOpenChange,
|
|
6609
|
+
onSelectedEventIdChange,
|
|
6603
6610
|
...rest
|
|
6604
6611
|
}) {
|
|
6605
6612
|
const isControlledMonth = month != null;
|
|
@@ -6630,6 +6637,60 @@ function Calendar2({
|
|
|
6630
6637
|
}
|
|
6631
6638
|
return map;
|
|
6632
6639
|
}, [events]);
|
|
6640
|
+
const effectiveEnableEventSheet = enableEventSheet ?? !!renderEventSheet;
|
|
6641
|
+
const isEventSheetOpenControlled = eventSheetOpen !== void 0;
|
|
6642
|
+
const [internalEventSheetOpen, setInternalEventSheetOpen] = React24.useState(false);
|
|
6643
|
+
const activeEventSheetOpen = isEventSheetOpenControlled ? !!eventSheetOpen : internalEventSheetOpen;
|
|
6644
|
+
const isSelectedEventControlled = selectedEventId !== void 0;
|
|
6645
|
+
const [internalSelectedEventRef, setInternalSelectedEventRef] = React24.useState(null);
|
|
6646
|
+
const setEventSheetOpen = React24.useCallback(
|
|
6647
|
+
(open) => {
|
|
6648
|
+
if (!isEventSheetOpenControlled) setInternalEventSheetOpen(open);
|
|
6649
|
+
onEventSheetOpenChange?.(open);
|
|
6650
|
+
if (!open) {
|
|
6651
|
+
if (!isSelectedEventControlled) setInternalSelectedEventRef(null);
|
|
6652
|
+
onSelectedEventIdChange?.(void 0);
|
|
6653
|
+
}
|
|
6654
|
+
},
|
|
6655
|
+
[isEventSheetOpenControlled, isSelectedEventControlled, onEventSheetOpenChange, onSelectedEventIdChange]
|
|
6656
|
+
);
|
|
6657
|
+
const selectedEventRef = React24.useMemo(() => {
|
|
6658
|
+
if (isSelectedEventControlled && selectedEventId != null) {
|
|
6659
|
+
const ev = events.find((e) => e.id === selectedEventId);
|
|
6660
|
+
if (!ev) return null;
|
|
6661
|
+
const d = toDate(ev.date);
|
|
6662
|
+
const dayKey = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
|
|
6663
|
+
return { dayKey, eventId: selectedEventId };
|
|
6664
|
+
}
|
|
6665
|
+
return internalSelectedEventRef;
|
|
6666
|
+
}, [events, internalSelectedEventRef, isSelectedEventControlled, selectedEventId]);
|
|
6667
|
+
const selectedEvent = React24.useMemo(() => {
|
|
6668
|
+
if (!selectedEventRef) return null;
|
|
6669
|
+
const list = byDay.get(selectedEventRef.dayKey) || [];
|
|
6670
|
+
if (selectedEventRef.eventId != null) {
|
|
6671
|
+
return list.find((e) => e.id === selectedEventRef.eventId) || null;
|
|
6672
|
+
}
|
|
6673
|
+
const idx = selectedEventRef.index ?? -1;
|
|
6674
|
+
return idx >= 0 && idx < list.length ? list[idx] : null;
|
|
6675
|
+
}, [byDay, selectedEventRef]);
|
|
6676
|
+
const selectedEventDate = React24.useMemo(() => {
|
|
6677
|
+
if (!selectedEventRef) return null;
|
|
6678
|
+
const [y, m, d] = selectedEventRef.dayKey.split("-").map((x) => Number(x));
|
|
6679
|
+
if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) return null;
|
|
6680
|
+
return new Date(y, m, d);
|
|
6681
|
+
}, [selectedEventRef]);
|
|
6682
|
+
const handleEventActivate = React24.useCallback(
|
|
6683
|
+
(event, date, dayKey, index) => {
|
|
6684
|
+
onEventClick?.(event, date);
|
|
6685
|
+
onSelectedEventIdChange?.(event.id ?? void 0);
|
|
6686
|
+
if (!effectiveEnableEventSheet) return;
|
|
6687
|
+
if (!isSelectedEventControlled) {
|
|
6688
|
+
setInternalSelectedEventRef({ dayKey, eventId: event.id, index });
|
|
6689
|
+
}
|
|
6690
|
+
setEventSheetOpen(true);
|
|
6691
|
+
},
|
|
6692
|
+
[effectiveEnableEventSheet, isSelectedEventControlled, onEventClick, onSelectedEventIdChange, setEventSheetOpen]
|
|
6693
|
+
);
|
|
6633
6694
|
const isSelected = (d) => {
|
|
6634
6695
|
if (!selected) return false;
|
|
6635
6696
|
if (selectMode === "single" && selected instanceof Date) return isSameDay(selected, d);
|
|
@@ -6773,7 +6834,7 @@ function Calendar2({
|
|
|
6773
6834
|
"button",
|
|
6774
6835
|
{
|
|
6775
6836
|
type: "button",
|
|
6776
|
-
onClick: () =>
|
|
6837
|
+
onClick: () => handleEventActivate(e, d, k, i),
|
|
6777
6838
|
className: cn(
|
|
6778
6839
|
"w-full text-left rounded-lg px-2 py-1",
|
|
6779
6840
|
"transition-colors duration-150 hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
|
|
@@ -6937,7 +6998,7 @@ function Calendar2({
|
|
|
6937
6998
|
"button",
|
|
6938
6999
|
{
|
|
6939
7000
|
type: "button",
|
|
6940
|
-
onClick: () =>
|
|
7001
|
+
onClick: () => handleEventActivate(e, d, k, i),
|
|
6941
7002
|
className: cn(
|
|
6942
7003
|
"w-full text-left rounded-lg px-2 py-1",
|
|
6943
7004
|
"transition-colors duration-150 hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
|
|
@@ -6987,7 +7048,32 @@ function Calendar2({
|
|
|
6987
7048
|
`wd-${idx}`
|
|
6988
7049
|
);
|
|
6989
7050
|
}) })
|
|
6990
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: cn(months > 1 ? "grid md:grid-cols-2 lg:grid-cols-3 gap-4" : ""), children: Array.from({ length: Math.max(1, months) }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(React24.Fragment, { children: renderMonth(addMonths(view, i)) }, `cal-month-${view.getFullYear()}-${view.getMonth()}-${i}`)) })
|
|
7051
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: cn(months > 1 ? "grid md:grid-cols-2 lg:grid-cols-3 gap-4" : ""), children: Array.from({ length: Math.max(1, months) }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(React24.Fragment, { children: renderMonth(addMonths(view, i)) }, `cal-month-${view.getFullYear()}-${view.getMonth()}-${i}`)) }),
|
|
7052
|
+
effectiveEnableEventSheet && selectedEvent && selectedEventDate ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
7053
|
+
Sheet,
|
|
7054
|
+
{
|
|
7055
|
+
open: activeEventSheetOpen,
|
|
7056
|
+
onOpenChange: setEventSheetOpen,
|
|
7057
|
+
side: "right",
|
|
7058
|
+
size: eventSheetSize,
|
|
7059
|
+
title: selectedEvent.title ?? "Event",
|
|
7060
|
+
description: selectedEventDate.toDateString(),
|
|
7061
|
+
children: renderEventSheet ? renderEventSheet({ event: selectedEvent, date: selectedEventDate, close: () => setEventSheetOpen(false) }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "space-y-3", children: [
|
|
7062
|
+
selectedEvent.id != null ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
|
|
7063
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "text-xs text-muted-foreground", children: "ID" }),
|
|
7064
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "font-mono text-xs break-all", children: String(selectedEvent.id) })
|
|
7065
|
+
] }) : null,
|
|
7066
|
+
selectedEvent.badge ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
|
|
7067
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "text-xs text-muted-foreground", children: "Badge" }),
|
|
7068
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "text-sm", children: selectedEvent.badge })
|
|
7069
|
+
] }) : null,
|
|
7070
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
7071
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "h-3 w-3 rounded-full", style: { backgroundColor: selectedEvent.color || "hsl(var(--primary))" } }),
|
|
7072
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "Color" })
|
|
7073
|
+
] })
|
|
7074
|
+
] })
|
|
7075
|
+
}
|
|
7076
|
+
) : null
|
|
6991
7077
|
] });
|
|
6992
7078
|
}
|
|
6993
7079
|
|
|
@@ -8150,8 +8236,19 @@ function useHorizontalScrollSync(args) {
|
|
|
8150
8236
|
};
|
|
8151
8237
|
}, [bodyRef, headerRef, leftRef]);
|
|
8152
8238
|
}
|
|
8153
|
-
function
|
|
8154
|
-
|
|
8239
|
+
function lowerBound(arr, target) {
|
|
8240
|
+
let lo = 0;
|
|
8241
|
+
let hi = arr.length;
|
|
8242
|
+
while (lo < hi) {
|
|
8243
|
+
const mid = lo + hi >> 1;
|
|
8244
|
+
if (arr[mid] < target) lo = mid + 1;
|
|
8245
|
+
else hi = mid;
|
|
8246
|
+
}
|
|
8247
|
+
return lo;
|
|
8248
|
+
}
|
|
8249
|
+
function useVirtualVariableRows(args) {
|
|
8250
|
+
const { enabled, overscan, rowHeights, scrollRef } = args;
|
|
8251
|
+
const itemCount = rowHeights.length;
|
|
8155
8252
|
const [viewportHeight, setViewportHeight] = React27.useState(0);
|
|
8156
8253
|
const [scrollTop, setScrollTop] = React27.useState(0);
|
|
8157
8254
|
React27.useEffect(() => {
|
|
@@ -8170,16 +8267,28 @@ function useVirtualRows(args) {
|
|
|
8170
8267
|
el.addEventListener("scroll", onScroll, { passive: true });
|
|
8171
8268
|
return () => el.removeEventListener("scroll", onScroll);
|
|
8172
8269
|
}, [scrollRef]);
|
|
8270
|
+
const prefix = React27.useMemo(() => {
|
|
8271
|
+
const out = new Array(itemCount + 1);
|
|
8272
|
+
out[0] = 0;
|
|
8273
|
+
for (let i = 0; i < itemCount; i++) {
|
|
8274
|
+
out[i + 1] = out[i] + (rowHeights[i] ?? 0);
|
|
8275
|
+
}
|
|
8276
|
+
return out;
|
|
8277
|
+
}, [itemCount, rowHeights]);
|
|
8173
8278
|
return React27.useMemo(() => {
|
|
8174
8279
|
if (!enabled) {
|
|
8175
|
-
return { startIndex: 0, endIndex: itemCount, topSpacer: 0, bottomSpacer: 0 };
|
|
8176
|
-
}
|
|
8177
|
-
const
|
|
8178
|
-
const
|
|
8179
|
-
const
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8280
|
+
return { startIndex: 0, endIndex: itemCount, topSpacer: 0, bottomSpacer: 0, totalHeight: prefix[itemCount] ?? 0 };
|
|
8281
|
+
}
|
|
8282
|
+
const total = prefix[itemCount] ?? 0;
|
|
8283
|
+
const startPos = Math.max(0, Math.min(scrollTop, total));
|
|
8284
|
+
const endPos = Math.max(0, Math.min(scrollTop + viewportHeight, total));
|
|
8285
|
+
let startIndex = Math.max(0, lowerBound(prefix, startPos) - 1);
|
|
8286
|
+
let endIndex = Math.min(itemCount, lowerBound(prefix, endPos) + overscan);
|
|
8287
|
+
startIndex = clamp3(startIndex - overscan, 0, itemCount);
|
|
8288
|
+
const topSpacer = prefix[startIndex] ?? 0;
|
|
8289
|
+
const bottomSpacer = total - (prefix[endIndex] ?? total);
|
|
8290
|
+
return { startIndex, endIndex, topSpacer, bottomSpacer, totalHeight: total };
|
|
8291
|
+
}, [enabled, itemCount, overscan, prefix, scrollTop, viewportHeight]);
|
|
8183
8292
|
}
|
|
8184
8293
|
|
|
8185
8294
|
// ../../components/ui/CalendarTimeline/CalendarTimeline.tsx
|
|
@@ -8236,7 +8345,19 @@ function CalendarTimeline({
|
|
|
8236
8345
|
defaultGroupCollapsed,
|
|
8237
8346
|
onGroupCollapsedChange,
|
|
8238
8347
|
resourceColumnWidth,
|
|
8348
|
+
defaultResourceColumnWidth,
|
|
8349
|
+
onResourceColumnWidthChange,
|
|
8350
|
+
minResourceColumnWidth,
|
|
8351
|
+
maxResourceColumnWidth,
|
|
8239
8352
|
rowHeight,
|
|
8353
|
+
defaultRowHeight,
|
|
8354
|
+
onRowHeightChange,
|
|
8355
|
+
minRowHeight,
|
|
8356
|
+
maxRowHeight,
|
|
8357
|
+
rowHeights,
|
|
8358
|
+
defaultRowHeights,
|
|
8359
|
+
onRowHeightsChange,
|
|
8360
|
+
enableLayoutResize,
|
|
8240
8361
|
slotMinWidth,
|
|
8241
8362
|
dayTimeStepMinutes = 60,
|
|
8242
8363
|
maxLanesPerRow = 3,
|
|
@@ -8335,9 +8456,42 @@ function CalendarTimeline({
|
|
|
8335
8456
|
};
|
|
8336
8457
|
return cfgBySize[size];
|
|
8337
8458
|
}, [size]);
|
|
8338
|
-
const
|
|
8339
|
-
|
|
8459
|
+
const canResizeColumn = React28.useMemo(() => {
|
|
8460
|
+
const cfg = enableLayoutResize;
|
|
8461
|
+
if (!cfg) return false;
|
|
8462
|
+
if (cfg === true) return true;
|
|
8463
|
+
return cfg.column !== false;
|
|
8464
|
+
}, [enableLayoutResize]);
|
|
8465
|
+
const canResizeRow = React28.useMemo(() => {
|
|
8466
|
+
const cfg = enableLayoutResize;
|
|
8467
|
+
if (!cfg) return false;
|
|
8468
|
+
if (cfg === true) return true;
|
|
8469
|
+
return cfg.row !== false;
|
|
8470
|
+
}, [enableLayoutResize]);
|
|
8471
|
+
const isControlledResourceColumnWidth = resourceColumnWidth !== void 0;
|
|
8472
|
+
const [internalResourceColumnWidth, setInternalResourceColumnWidth] = React28.useState(() => {
|
|
8473
|
+
const init = defaultResourceColumnWidth ?? sizeConfig.resourceColumnWidth;
|
|
8474
|
+
return typeof init === "number" ? init : sizeConfig.resourceColumnWidth;
|
|
8475
|
+
});
|
|
8476
|
+
React28.useEffect(() => {
|
|
8477
|
+
if (isControlledResourceColumnWidth) return;
|
|
8478
|
+
if (defaultResourceColumnWidth == null) return;
|
|
8479
|
+
setInternalResourceColumnWidth(defaultResourceColumnWidth);
|
|
8480
|
+
}, [defaultResourceColumnWidth, isControlledResourceColumnWidth]);
|
|
8481
|
+
const effectiveResourceColumnWidth = isControlledResourceColumnWidth ? resourceColumnWidth : internalResourceColumnWidth;
|
|
8482
|
+
const isControlledRowHeight = rowHeight !== void 0;
|
|
8483
|
+
const [internalRowHeight, setInternalRowHeight] = React28.useState(() => defaultRowHeight ?? sizeConfig.rowHeight);
|
|
8484
|
+
React28.useEffect(() => {
|
|
8485
|
+
if (isControlledRowHeight) return;
|
|
8486
|
+
if (defaultRowHeight == null) return;
|
|
8487
|
+
setInternalRowHeight(defaultRowHeight);
|
|
8488
|
+
}, [defaultRowHeight, isControlledRowHeight]);
|
|
8489
|
+
const effectiveRowHeight = isControlledRowHeight ? rowHeight : internalRowHeight;
|
|
8340
8490
|
const effectiveSlotMinWidth = slotMinWidth ?? sizeConfig.slotMinWidth;
|
|
8491
|
+
const colMin = minResourceColumnWidth ?? 160;
|
|
8492
|
+
const colMax = maxResourceColumnWidth ?? 520;
|
|
8493
|
+
const rowMin = minRowHeight ?? 36;
|
|
8494
|
+
const rowMax = maxRowHeight ?? 120;
|
|
8341
8495
|
const isControlledView = view !== void 0;
|
|
8342
8496
|
const [internalView, setInternalView] = React28.useState(defaultView);
|
|
8343
8497
|
const activeView = isControlledView ? view : internalView;
|
|
@@ -8527,6 +8681,135 @@ function CalendarTimeline({
|
|
|
8527
8681
|
const bodyRef = React28.useRef(null);
|
|
8528
8682
|
const headerRef = React28.useRef(null);
|
|
8529
8683
|
useHorizontalScrollSync({ bodyRef, headerRef, leftRef });
|
|
8684
|
+
const virt = virtualization?.enabled;
|
|
8685
|
+
const overscan = virtualization?.overscan ?? 8;
|
|
8686
|
+
const isControlledRowHeights = rowHeights !== void 0;
|
|
8687
|
+
const [internalRowHeights, setInternalRowHeights] = React28.useState(() => defaultRowHeights ?? {});
|
|
8688
|
+
React28.useEffect(() => {
|
|
8689
|
+
if (isControlledRowHeights) return;
|
|
8690
|
+
if (!defaultRowHeights) return;
|
|
8691
|
+
setInternalRowHeights(defaultRowHeights);
|
|
8692
|
+
}, [defaultRowHeights, isControlledRowHeights]);
|
|
8693
|
+
const activeRowHeights = isControlledRowHeights ? rowHeights : internalRowHeights;
|
|
8694
|
+
const getResourceRowHeight = React28.useCallback(
|
|
8695
|
+
(resourceId) => {
|
|
8696
|
+
const h = activeRowHeights[resourceId];
|
|
8697
|
+
if (typeof h === "number" && Number.isFinite(h) && h > 0) return h;
|
|
8698
|
+
return effectiveRowHeight;
|
|
8699
|
+
},
|
|
8700
|
+
[activeRowHeights, effectiveRowHeight]
|
|
8701
|
+
);
|
|
8702
|
+
const setRowHeightForResource = React28.useCallback(
|
|
8703
|
+
(resourceId, height) => {
|
|
8704
|
+
const clamped = clamp3(Math.round(height), rowMin, rowMax);
|
|
8705
|
+
onRowHeightChange?.(clamped);
|
|
8706
|
+
if (isControlledRowHeights) {
|
|
8707
|
+
const next = { ...activeRowHeights ?? {}, [resourceId]: clamped };
|
|
8708
|
+
onRowHeightsChange?.(next);
|
|
8709
|
+
return;
|
|
8710
|
+
}
|
|
8711
|
+
setInternalRowHeights((prev) => {
|
|
8712
|
+
const next = { ...prev, [resourceId]: clamped };
|
|
8713
|
+
onRowHeightsChange?.(next);
|
|
8714
|
+
return next;
|
|
8715
|
+
});
|
|
8716
|
+
},
|
|
8717
|
+
[activeRowHeights, isControlledRowHeights, onRowHeightChange, onRowHeightsChange, rowMax, rowMin]
|
|
8718
|
+
);
|
|
8719
|
+
const rowHeightsArray = React28.useMemo(() => {
|
|
8720
|
+
return rows.map((r) => {
|
|
8721
|
+
if (r.kind === "resource") return getResourceRowHeight(r.resource.id);
|
|
8722
|
+
return effectiveRowHeight;
|
|
8723
|
+
});
|
|
8724
|
+
}, [effectiveRowHeight, getResourceRowHeight, rows]);
|
|
8725
|
+
const virtualResult = useVirtualVariableRows({
|
|
8726
|
+
enabled: virt,
|
|
8727
|
+
overscan,
|
|
8728
|
+
rowHeights: rowHeightsArray,
|
|
8729
|
+
scrollRef: bodyRef
|
|
8730
|
+
});
|
|
8731
|
+
const startRow = virt ? virtualResult.startIndex : 0;
|
|
8732
|
+
const endRow = virt ? virtualResult.endIndex : rows.length;
|
|
8733
|
+
const topSpacer = virt ? virtualResult.topSpacer : 0;
|
|
8734
|
+
const bottomSpacer = virt ? virtualResult.bottomSpacer : 0;
|
|
8735
|
+
const resizeRef = React28.useRef(null);
|
|
8736
|
+
const setResourceColumnWidth = React28.useCallback(
|
|
8737
|
+
(next) => {
|
|
8738
|
+
const clamped = clamp3(Math.round(next), colMin, colMax);
|
|
8739
|
+
if (!isControlledResourceColumnWidth) setInternalResourceColumnWidth(clamped);
|
|
8740
|
+
onResourceColumnWidthChange?.(clamped);
|
|
8741
|
+
},
|
|
8742
|
+
[colMax, colMin, isControlledResourceColumnWidth, onResourceColumnWidthChange]
|
|
8743
|
+
);
|
|
8744
|
+
const startResize = React28.useCallback(
|
|
8745
|
+
(mode, e, args) => {
|
|
8746
|
+
resizeRef.current = {
|
|
8747
|
+
mode,
|
|
8748
|
+
pointerId: e.pointerId,
|
|
8749
|
+
startX: e.clientX,
|
|
8750
|
+
startY: e.clientY,
|
|
8751
|
+
startWidth: args.startWidth,
|
|
8752
|
+
startHeight: args.startHeight,
|
|
8753
|
+
resourceId: args.resourceId
|
|
8754
|
+
};
|
|
8755
|
+
document.body.style.cursor = mode === "column" ? "col-resize" : "row-resize";
|
|
8756
|
+
document.body.style.userSelect = "none";
|
|
8757
|
+
const onMove = (ev) => {
|
|
8758
|
+
const st = resizeRef.current;
|
|
8759
|
+
if (!st) return;
|
|
8760
|
+
if (ev.pointerId !== st.pointerId) return;
|
|
8761
|
+
if (st.mode === "column") {
|
|
8762
|
+
setResourceColumnWidth(st.startWidth + (ev.clientX - st.startX));
|
|
8763
|
+
} else {
|
|
8764
|
+
if (!st.resourceId) return;
|
|
8765
|
+
setRowHeightForResource(st.resourceId, st.startHeight + (ev.clientY - st.startY));
|
|
8766
|
+
}
|
|
8767
|
+
};
|
|
8768
|
+
const onUp = (ev) => {
|
|
8769
|
+
const st = resizeRef.current;
|
|
8770
|
+
if (!st) return;
|
|
8771
|
+
if (ev.pointerId !== st.pointerId) return;
|
|
8772
|
+
resizeRef.current = null;
|
|
8773
|
+
document.body.style.cursor = "";
|
|
8774
|
+
document.body.style.userSelect = "";
|
|
8775
|
+
window.removeEventListener("pointermove", onMove);
|
|
8776
|
+
window.removeEventListener("pointerup", onUp);
|
|
8777
|
+
};
|
|
8778
|
+
window.addEventListener("pointermove", onMove);
|
|
8779
|
+
window.addEventListener("pointerup", onUp);
|
|
8780
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
8781
|
+
e.preventDefault();
|
|
8782
|
+
e.stopPropagation();
|
|
8783
|
+
},
|
|
8784
|
+
[setResourceColumnWidth, setRowHeightForResource]
|
|
8785
|
+
);
|
|
8786
|
+
React28.useEffect(() => {
|
|
8787
|
+
return () => {
|
|
8788
|
+
if (!resizeRef.current) return;
|
|
8789
|
+
resizeRef.current = null;
|
|
8790
|
+
document.body.style.cursor = "";
|
|
8791
|
+
document.body.style.userSelect = "";
|
|
8792
|
+
};
|
|
8793
|
+
}, []);
|
|
8794
|
+
const beginResizeColumn = React28.useCallback(
|
|
8795
|
+
(e) => {
|
|
8796
|
+
if (!canResizeColumn) return;
|
|
8797
|
+
if (typeof effectiveResourceColumnWidth !== "number") return;
|
|
8798
|
+
startResize("column", e, { startWidth: effectiveResourceColumnWidth, startHeight: effectiveRowHeight });
|
|
8799
|
+
},
|
|
8800
|
+
[canResizeColumn, effectiveResourceColumnWidth, effectiveRowHeight, startResize]
|
|
8801
|
+
);
|
|
8802
|
+
const beginResizeResourceRow = React28.useCallback(
|
|
8803
|
+
(resourceId) => (e) => {
|
|
8804
|
+
if (!canResizeRow) return;
|
|
8805
|
+
startResize("row", e, {
|
|
8806
|
+
startWidth: typeof effectiveResourceColumnWidth === "number" ? effectiveResourceColumnWidth : 0,
|
|
8807
|
+
startHeight: getResourceRowHeight(resourceId),
|
|
8808
|
+
resourceId
|
|
8809
|
+
});
|
|
8810
|
+
},
|
|
8811
|
+
[canResizeRow, effectiveResourceColumnWidth, getResourceRowHeight, startResize]
|
|
8812
|
+
);
|
|
8530
8813
|
const title = React28.useMemo(() => {
|
|
8531
8814
|
if (activeView === "month") {
|
|
8532
8815
|
return formatters?.monthTitle?.(activeDate, { locale: resolvedLocale, timeZone: resolvedTimeZone }) ?? defaultMonthTitle(activeDate, resolvedLocale, resolvedTimeZone);
|
|
@@ -8550,20 +8833,6 @@ function CalendarTimeline({
|
|
|
8550
8833
|
const eventHeight = sizeConfig.eventHeight;
|
|
8551
8834
|
const laneGap = sizeConfig.laneGap;
|
|
8552
8835
|
const lanePaddingY = sizeConfig.lanePaddingY;
|
|
8553
|
-
const virt = virtualization?.enabled;
|
|
8554
|
-
const overscan = virtualization?.overscan ?? 8;
|
|
8555
|
-
const {
|
|
8556
|
-
startIndex: startRow,
|
|
8557
|
-
endIndex: endRow,
|
|
8558
|
-
topSpacer,
|
|
8559
|
-
bottomSpacer
|
|
8560
|
-
} = useVirtualRows({
|
|
8561
|
-
enabled: virt,
|
|
8562
|
-
overscan,
|
|
8563
|
-
rowHeight: effectiveRowHeight,
|
|
8564
|
-
itemCount: rows.length,
|
|
8565
|
-
scrollRef: bodyRef
|
|
8566
|
-
});
|
|
8567
8836
|
const dragRef = React28.useRef(null);
|
|
8568
8837
|
const [preview, setPreview] = React28.useState(null);
|
|
8569
8838
|
const getPointerContext = React28.useCallback(
|
|
@@ -8824,12 +9093,34 @@ function CalendarTimeline({
|
|
|
8824
9093
|
)) })
|
|
8825
9094
|
] }),
|
|
8826
9095
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex border-t border-border/20", children: [
|
|
8827
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.
|
|
9096
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
8828
9097
|
"div",
|
|
8829
9098
|
{
|
|
8830
|
-
className: "shrink-0 border-r border-border/30 bg-muted/20 flex items-center justify-center",
|
|
9099
|
+
className: "shrink-0 border-r border-border/30 bg-muted/20 flex items-center justify-center relative group/uv-ct-top-left",
|
|
8831
9100
|
style: { width: effectiveResourceColumnWidth, minWidth: effectiveResourceColumnWidth },
|
|
8832
|
-
children:
|
|
9101
|
+
children: [
|
|
9102
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-xs font-medium text-muted-foreground/70 uppercase tracking-wider", children: t("resourcesHeader") }),
|
|
9103
|
+
canResizeColumn && typeof effectiveResourceColumnWidth === "number" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
9104
|
+
"div",
|
|
9105
|
+
{
|
|
9106
|
+
role: "separator",
|
|
9107
|
+
"aria-orientation": "vertical",
|
|
9108
|
+
"aria-label": "Resize resource column",
|
|
9109
|
+
className: cn(
|
|
9110
|
+
"absolute right-0 top-0 h-full w-3 cursor-col-resize z-20",
|
|
9111
|
+
"bg-transparent hover:bg-primary/10 active:bg-primary/15",
|
|
9112
|
+
"transition-all",
|
|
9113
|
+
"opacity-0 pointer-events-none",
|
|
9114
|
+
"group-hover/uv-ct-top-left:opacity-100 group-hover/uv-ct-top-left:pointer-events-auto"
|
|
9115
|
+
),
|
|
9116
|
+
onPointerDown: beginResizeColumn,
|
|
9117
|
+
children: [
|
|
9118
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "absolute inset-y-2 left-1/2 w-px -translate-x-1/2 bg-border/70" }),
|
|
9119
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-70", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.GripVertical, { className: "h-4 w-4 text-muted-foreground" }) })
|
|
9120
|
+
]
|
|
9121
|
+
}
|
|
9122
|
+
) : null
|
|
9123
|
+
]
|
|
8833
9124
|
}
|
|
8834
9125
|
),
|
|
8835
9126
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: headerRef, className: "overflow-x-auto overflow-y-hidden scrollbar-none", children: slotHeaderNodes })
|
|
@@ -8839,13 +9130,33 @@ function CalendarTimeline({
|
|
|
8839
9130
|
"div",
|
|
8840
9131
|
{
|
|
8841
9132
|
className: cn(
|
|
8842
|
-
"h-full w-full flex items-center border-b border-border/30 bg-linear-to-r from-background to-background/95",
|
|
9133
|
+
"h-full w-full flex items-center border-b border-border/30 bg-linear-to-r from-background to-background/95 relative",
|
|
8843
9134
|
sizeConfig.resourceRowClass,
|
|
8844
|
-
"hover:from-muted/30 hover:to-muted/10 transition-all duration-200 group"
|
|
9135
|
+
"hover:from-muted/30 hover:to-muted/10 transition-all duration-200 group/uv-ct-row-header"
|
|
8845
9136
|
),
|
|
8846
9137
|
children: [
|
|
8847
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "shrink-0 opacity-0 group-hover:opacity-60 transition-opacity cursor-grab", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.GripVertical, { className: "h-4 w-4 text-muted-foreground" }) }),
|
|
8848
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: cn("flex-1 min-w-0", r.disabled && "opacity-50"), children: renderResource ? renderResource(r) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-medium text-sm truncate block", children: r.label }) })
|
|
9138
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "shrink-0 opacity-0 group-hover/uv-ct-row-header:opacity-60 transition-opacity cursor-grab", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.GripVertical, { className: "h-4 w-4 text-muted-foreground" }) }),
|
|
9139
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: cn("flex-1 min-w-0", r.disabled && "opacity-50"), children: renderResource ? renderResource(r) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "font-medium text-sm truncate block", children: r.label }) }),
|
|
9140
|
+
canResizeRow ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
9141
|
+
"div",
|
|
9142
|
+
{
|
|
9143
|
+
role: "separator",
|
|
9144
|
+
"aria-orientation": "horizontal",
|
|
9145
|
+
"aria-label": "Resize row height",
|
|
9146
|
+
className: cn(
|
|
9147
|
+
"absolute left-0 bottom-0 w-full h-3 cursor-row-resize z-20",
|
|
9148
|
+
"bg-transparent hover:bg-primary/10 active:bg-primary/15",
|
|
9149
|
+
"transition-all",
|
|
9150
|
+
"opacity-0 pointer-events-none",
|
|
9151
|
+
"group-hover/uv-ct-row-header:opacity-100 group-hover/uv-ct-row-header:pointer-events-auto"
|
|
9152
|
+
),
|
|
9153
|
+
onPointerDown: beginResizeResourceRow(r.id),
|
|
9154
|
+
children: [
|
|
9155
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "absolute inset-x-3 top-1/2 h-px -translate-y-1/2 bg-border/70" }),
|
|
9156
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "absolute top-1/2 right-3 -translate-y-1/2 opacity-70", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react18.GripVertical, { className: "h-4 w-4 text-muted-foreground rotate-90" }) })
|
|
9157
|
+
]
|
|
9158
|
+
}
|
|
9159
|
+
) : null
|
|
8849
9160
|
]
|
|
8850
9161
|
}
|
|
8851
9162
|
);
|
|
@@ -8898,11 +9209,12 @@ function CalendarTimeline({
|
|
|
8898
9209
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: topSpacer } }),
|
|
8899
9210
|
rows.slice(startRow, endRow).map((row, idx) => {
|
|
8900
9211
|
const rowIndex = startRow + idx;
|
|
9212
|
+
const h = rowHeightsArray[rowIndex] ?? effectiveRowHeight;
|
|
8901
9213
|
if (row.kind === "group") {
|
|
8902
|
-
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height:
|
|
9214
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: h }, children: renderGroupRow(row.group) }, `lg_${row.group.id}_${rowIndex}`);
|
|
8903
9215
|
}
|
|
8904
9216
|
const r = row.resource;
|
|
8905
|
-
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height:
|
|
9217
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: h }, children: ResourceCell(r) }, `lr_${r.id}_${rowIndex}`);
|
|
8906
9218
|
}),
|
|
8907
9219
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: bottomSpacer } })
|
|
8908
9220
|
]
|
|
@@ -8919,8 +9231,9 @@ function CalendarTimeline({
|
|
|
8919
9231
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { height: topSpacer } }),
|
|
8920
9232
|
rows.slice(startRow, endRow).map((row, idx) => {
|
|
8921
9233
|
const rowIndex = startRow + idx;
|
|
9234
|
+
const h = rowHeightsArray[rowIndex] ?? effectiveRowHeight;
|
|
8922
9235
|
if (row.kind === "group") {
|
|
8923
|
-
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex", style: { height:
|
|
9236
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex", style: { height: h }, 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}`);
|
|
8924
9237
|
}
|
|
8925
9238
|
const r = row.resource;
|
|
8926
9239
|
const layout = layoutsByResource.get(r.id) ?? { visible: [], hidden: [] };
|
|
@@ -8929,7 +9242,7 @@ function CalendarTimeline({
|
|
|
8929
9242
|
"div",
|
|
8930
9243
|
{
|
|
8931
9244
|
className: "group/row hover:bg-muted/5 transition-colors duration-150",
|
|
8932
|
-
style: { height:
|
|
9245
|
+
style: { height: h },
|
|
8933
9246
|
"data-uv-ct-row": r.id,
|
|
8934
9247
|
children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "relative shrink-0", style: { width: gridWidth, minWidth: gridWidth, height: "100%" }, children: [
|
|
8935
9248
|
/* @__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)(
|