@underverse-ui/underverse 0.2.101 → 0.2.102
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 +125 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +125 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8762,6 +8762,7 @@ function CalendarTimeline({
|
|
|
8762
8762
|
autoRowHeight,
|
|
8763
8763
|
enableLayoutResize,
|
|
8764
8764
|
slotMinWidth,
|
|
8765
|
+
adaptiveSlotWidths,
|
|
8765
8766
|
dayTimeStepMinutes = 60,
|
|
8766
8767
|
dayRangeMode,
|
|
8767
8768
|
workHours,
|
|
@@ -8950,17 +8951,89 @@ function CalendarTimeline({
|
|
|
8950
8951
|
const headerRef = React28.useRef(null);
|
|
8951
8952
|
const bodyClientWidth = useClientWidth(bodyRef);
|
|
8952
8953
|
const slotStarts = React28.useMemo(() => slots.map((s) => s.start), [slots]);
|
|
8953
|
-
const
|
|
8954
|
-
const baseSlotWidth = activeView === "month"
|
|
8954
|
+
const fixedSlotWidth = React28.useMemo(() => {
|
|
8955
|
+
const baseSlotWidth = activeView === "month" || activeView === "day" ? effectiveSlotMinWidth * 3 : effectiveSlotMinWidth;
|
|
8955
8956
|
if (activeView !== "week") return baseSlotWidth;
|
|
8956
8957
|
if (bodyClientWidth <= 0) return baseSlotWidth;
|
|
8957
8958
|
if (slots.length <= 0) return baseSlotWidth;
|
|
8958
8959
|
return Math.max(baseSlotWidth, bodyClientWidth / slots.length);
|
|
8959
8960
|
}, [activeView, bodyClientWidth, effectiveSlotMinWidth, slots.length]);
|
|
8960
|
-
const gridWidth = slots.length * slotWidth;
|
|
8961
8961
|
const normalizedEvents = React28.useMemo(() => {
|
|
8962
8962
|
return normalizeEvents({ events, range, view: activeView, timeZone: resolvedTimeZone });
|
|
8963
8963
|
}, [events, range, activeView, resolvedTimeZone]);
|
|
8964
|
+
const slotMetrics = React28.useMemo(() => {
|
|
8965
|
+
const n = slots.length;
|
|
8966
|
+
const widths = new Array(n).fill(fixedSlotWidth);
|
|
8967
|
+
const isAdaptiveView = activeView === "month" || activeView === "day";
|
|
8968
|
+
const adaptiveCfg = adaptiveSlotWidths;
|
|
8969
|
+
const adaptiveEnabled = Boolean(adaptiveCfg) && isAdaptiveView;
|
|
8970
|
+
if (!adaptiveEnabled || n === 0) {
|
|
8971
|
+
const lefts2 = new Array(n + 1);
|
|
8972
|
+
lefts2[0] = 0;
|
|
8973
|
+
for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
|
|
8974
|
+
const gridWidth3 = lefts2[n] ?? 0;
|
|
8975
|
+
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 };
|
|
8977
|
+
}
|
|
8978
|
+
const cfg = typeof adaptiveCfg === "object" ? adaptiveCfg : {};
|
|
8979
|
+
const mode = cfg.mode ?? "shrink";
|
|
8980
|
+
const defaultEmptySlotWidth = Math.max(18, Math.round(effectiveSlotMinWidth * 0.6));
|
|
8981
|
+
const emptySlotWidth = Math.max(12, Math.min(fixedSlotWidth, cfg.emptySlotWidth ?? defaultEmptySlotWidth));
|
|
8982
|
+
const diff = new Array(n + 1).fill(0);
|
|
8983
|
+
for (const ev of normalizedEvents) {
|
|
8984
|
+
const startIdx = binarySearchLastLE(slotStarts, ev._start);
|
|
8985
|
+
const endIdx = clamp3(binarySearchFirstGE(slotStarts, ev._end), startIdx + 1, n);
|
|
8986
|
+
diff[startIdx] = (diff[startIdx] ?? 0) + 1;
|
|
8987
|
+
diff[endIdx] = (diff[endIdx] ?? 0) - 1;
|
|
8988
|
+
}
|
|
8989
|
+
const hasEvent = new Array(n);
|
|
8990
|
+
let running = 0;
|
|
8991
|
+
let eventCount = 0;
|
|
8992
|
+
for (let i = 0; i < n; i++) {
|
|
8993
|
+
running += diff[i] ?? 0;
|
|
8994
|
+
const covered = running > 0;
|
|
8995
|
+
hasEvent[i] = covered;
|
|
8996
|
+
if (covered) eventCount++;
|
|
8997
|
+
}
|
|
8998
|
+
if (eventCount === 0 || eventCount === n) {
|
|
8999
|
+
const lefts2 = new Array(n + 1);
|
|
9000
|
+
lefts2[0] = 0;
|
|
9001
|
+
for (let i = 0; i < n; i++) lefts2[i + 1] = lefts2[i] + widths[i];
|
|
9002
|
+
const gridWidth3 = lefts2[n] ?? 0;
|
|
9003
|
+
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 };
|
|
9005
|
+
}
|
|
9006
|
+
const emptyCount = n - eventCount;
|
|
9007
|
+
let eventSlotWidth = fixedSlotWidth;
|
|
9008
|
+
if (mode === "redistribute") {
|
|
9009
|
+
const targetTotal = n * fixedSlotWidth;
|
|
9010
|
+
const remaining = Math.max(0, targetTotal - emptyCount * emptySlotWidth);
|
|
9011
|
+
const raw = remaining / Math.max(1, eventCount);
|
|
9012
|
+
const maxEventSlotWidth = cfg.maxEventSlotWidth ?? fixedSlotWidth * 2.5;
|
|
9013
|
+
eventSlotWidth = clamp3(raw, fixedSlotWidth, Math.max(fixedSlotWidth, maxEventSlotWidth));
|
|
9014
|
+
}
|
|
9015
|
+
for (let i = 0; i < n; i++) widths[i] = hasEvent[i] ? eventSlotWidth : emptySlotWidth;
|
|
9016
|
+
const lefts = new Array(n + 1);
|
|
9017
|
+
lefts[0] = 0;
|
|
9018
|
+
for (let i = 0; i < n; i++) lefts[i + 1] = lefts[i] + widths[i];
|
|
9019
|
+
const gridWidth2 = lefts[n] ?? 0;
|
|
9020
|
+
const xToSlotIdx2 = (x) => {
|
|
9021
|
+
const xc = clamp3(x, 0, Math.max(0, gridWidth2 - 1e-3));
|
|
9022
|
+
let lo = 0;
|
|
9023
|
+
let hi = n - 1;
|
|
9024
|
+
while (lo <= hi) {
|
|
9025
|
+
const mid = lo + hi >> 1;
|
|
9026
|
+
const left = lefts[mid] ?? 0;
|
|
9027
|
+
const right = lefts[mid + 1] ?? gridWidth2;
|
|
9028
|
+
if (xc < left) hi = mid - 1;
|
|
9029
|
+
else if (xc >= right) lo = mid + 1;
|
|
9030
|
+
else return mid;
|
|
9031
|
+
}
|
|
9032
|
+
return clamp3(lo, 0, Math.max(0, n - 1));
|
|
9033
|
+
};
|
|
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;
|
|
8964
9037
|
const eventsByResource = React28.useMemo(() => {
|
|
8965
9038
|
return eventsByResourceId(normalizedEvents);
|
|
8966
9039
|
}, [normalizedEvents]);
|
|
@@ -9209,7 +9282,20 @@ function CalendarTimeline({
|
|
|
9209
9282
|
setCreateStartIdx(startIdx);
|
|
9210
9283
|
setCreateEndIdx(Math.min(slots.length, startIdx + 1));
|
|
9211
9284
|
setCreateOpen(true);
|
|
9212
|
-
}, [
|
|
9285
|
+
}, [
|
|
9286
|
+
activeDate,
|
|
9287
|
+
activeEventSheetOpen,
|
|
9288
|
+
activeView,
|
|
9289
|
+
canCreate,
|
|
9290
|
+
range.end,
|
|
9291
|
+
range.start,
|
|
9292
|
+
resolvedNow,
|
|
9293
|
+
resolvedTimeZone,
|
|
9294
|
+
resources,
|
|
9295
|
+
setEventSheetOpen,
|
|
9296
|
+
slotStarts,
|
|
9297
|
+
slots.length
|
|
9298
|
+
]);
|
|
9213
9299
|
React28.useEffect(() => {
|
|
9214
9300
|
setCreateEndIdx((prev) => Math.min(slots.length, Math.max(prev, createStartIdx + 1)));
|
|
9215
9301
|
}, [createStartIdx, slots.length]);
|
|
@@ -9262,13 +9348,13 @@ function CalendarTimeline({
|
|
|
9262
9348
|
const el = document.elementFromPoint(probeX, probeY);
|
|
9263
9349
|
const x = probeX - bodyRect.left + body.scrollLeft;
|
|
9264
9350
|
const epsilon = opts?.biasLeft ? 0.01 : 0;
|
|
9265
|
-
const slotIdx =
|
|
9351
|
+
const slotIdx = xToSlotIdx(x - epsilon);
|
|
9266
9352
|
const rowEl = el && body.contains(el) ? el.closest?.("[data-uv-ct-row]") ?? null : null;
|
|
9267
9353
|
const rid = rowEl?.dataset?.uvCtRow ?? opts?.fallbackResourceId ?? null;
|
|
9268
9354
|
if (!rid) return null;
|
|
9269
9355
|
return { slotIdx, resourceId: rid, x };
|
|
9270
9356
|
},
|
|
9271
|
-
[
|
|
9357
|
+
[xToSlotIdx]
|
|
9272
9358
|
);
|
|
9273
9359
|
const slotToDate = React28.useCallback(
|
|
9274
9360
|
(slotIdx) => {
|
|
@@ -9285,7 +9371,11 @@ function CalendarTimeline({
|
|
|
9285
9371
|
(clientX, clientY) => {
|
|
9286
9372
|
const drag = dragRef.current;
|
|
9287
9373
|
if (!drag) return;
|
|
9288
|
-
const ctx = getPointerContext(
|
|
9374
|
+
const ctx = getPointerContext(
|
|
9375
|
+
clientX,
|
|
9376
|
+
clientY,
|
|
9377
|
+
drag.mode === "create" ? { biasLeft: true, fallbackResourceId: drag.resourceId } : { fallbackResourceId: drag.resourceId }
|
|
9378
|
+
);
|
|
9289
9379
|
if (!ctx) return;
|
|
9290
9380
|
const { slotIdx } = ctx;
|
|
9291
9381
|
const movedEnough = Math.abs(clientX - drag.startClientX) > 3 || Math.abs(clientY - drag.startClientY) > 3 || slotIdx !== drag.startSlotIdx || ctx.resourceId !== drag.startRowResourceId;
|
|
@@ -9539,7 +9629,7 @@ function CalendarTimeline({
|
|
|
9539
9629
|
sizeConfig.slotHeaderClass,
|
|
9540
9630
|
s.isToday && "bg-primary/8 border-l-primary/40"
|
|
9541
9631
|
),
|
|
9542
|
-
style: { width:
|
|
9632
|
+
style: { width: slotWidths[idx], minWidth: slotWidths[idx] },
|
|
9543
9633
|
"aria-label": formatters?.ariaSlotLabel?.(s.start, { view: activeView, locale: resolvedLocale, timeZone: resolvedTimeZone }),
|
|
9544
9634
|
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("flex flex-col items-center", s.isToday && "relative"), children: [
|
|
9545
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" }),
|
|
@@ -9597,14 +9687,25 @@ function CalendarTimeline({
|
|
|
9597
9687
|
visible: visible.map((p) => ({
|
|
9598
9688
|
ev: p.ev,
|
|
9599
9689
|
lane: p.lane,
|
|
9600
|
-
left: p.startIdx
|
|
9601
|
-
width: Math.max(1, (p.endIdx - p.startIdx
|
|
9690
|
+
left: slotLefts[p.startIdx] ?? 0,
|
|
9691
|
+
width: Math.max(1, (slotLefts[p.endIdx] ?? 0) - (slotLefts[p.startIdx] ?? 0))
|
|
9602
9692
|
})),
|
|
9603
9693
|
hidden: hidden.map((h) => h.ev)
|
|
9604
9694
|
});
|
|
9605
9695
|
}
|
|
9606
9696
|
return map;
|
|
9607
|
-
}, [
|
|
9697
|
+
}, [
|
|
9698
|
+
eventsByResource,
|
|
9699
|
+
eventHeight,
|
|
9700
|
+
effectiveMaxLanesPerRow,
|
|
9701
|
+
getResourceRowHeight,
|
|
9702
|
+
laneGap,
|
|
9703
|
+
lanePaddingY,
|
|
9704
|
+
preview,
|
|
9705
|
+
slotLefts,
|
|
9706
|
+
slotStarts,
|
|
9707
|
+
slots.length
|
|
9708
|
+
]);
|
|
9608
9709
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
9609
9710
|
"div",
|
|
9610
9711
|
{
|
|
@@ -9662,7 +9763,13 @@ function CalendarTimeline({
|
|
|
9662
9763
|
const rowIndex = startRow + idx;
|
|
9663
9764
|
const h = rowHeightsArray[rowIndex] ?? effectiveRowHeight;
|
|
9664
9765
|
if (row.kind === "group") {
|
|
9665
|
-
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex", style: { height: h }, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9766
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex", style: { height: h }, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9767
|
+
"div",
|
|
9768
|
+
{
|
|
9769
|
+
className: "border-b border-border/30 bg-linear-to-r from-muted/15 to-muted/5",
|
|
9770
|
+
style: { width: gridWidth, minWidth: gridWidth }
|
|
9771
|
+
}
|
|
9772
|
+
) }, `rg_${row.group.id}_${rowIndex}`);
|
|
9666
9773
|
}
|
|
9667
9774
|
const r = row.resource;
|
|
9668
9775
|
const layout = layoutsByResource.get(r.id) ?? { visible: [], hidden: [], baseTop: lanePaddingY, eventHeight };
|
|
@@ -9684,7 +9791,7 @@ function CalendarTimeline({
|
|
|
9684
9791
|
s.isToday && "bg-primary/5 border-l-primary/30",
|
|
9685
9792
|
"hover:bg-muted/10"
|
|
9686
9793
|
),
|
|
9687
|
-
style: { width:
|
|
9794
|
+
style: { width: slotWidths[i2], minWidth: slotWidths[i2] }
|
|
9688
9795
|
},
|
|
9689
9796
|
`${r.id}_${i2}`
|
|
9690
9797
|
)) }) }),
|
|
@@ -9801,8 +9908,8 @@ function CalendarTimeline({
|
|
|
9801
9908
|
preview && preview.resourceId === r.id && !preview.eventId ? (() => {
|
|
9802
9909
|
const startIdx = binarySearchLastLE(slotStarts, preview.start);
|
|
9803
9910
|
const endIdx = clamp3(binarySearchFirstGE(slotStarts, preview.end), startIdx + 1, slots.length);
|
|
9804
|
-
const left = startIdx
|
|
9805
|
-
const width = Math.max(1, (endIdx - startIdx
|
|
9911
|
+
const left = slotLefts[startIdx] ?? 0;
|
|
9912
|
+
const width = Math.max(1, (slotLefts[endIdx] ?? 0) - (slotLefts[startIdx] ?? 0));
|
|
9806
9913
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9807
9914
|
"div",
|
|
9808
9915
|
{
|
|
@@ -9822,7 +9929,7 @@ function CalendarTimeline({
|
|
|
9822
9929
|
"flex items-center justify-center"
|
|
9823
9930
|
),
|
|
9824
9931
|
style: {
|
|
9825
|
-
left: hoverCell.slotIdx
|
|
9932
|
+
left: (slotLefts[hoverCell.slotIdx] ?? 0) + (slotWidths[hoverCell.slotIdx] ?? fixedSlotWidth) / 2 - 10,
|
|
9826
9933
|
top: clamp3(Math.round(hoverCell.y - 10), 6, Math.max(6, h - 26))
|
|
9827
9934
|
},
|
|
9828
9935
|
"aria-hidden": true,
|
|
@@ -9911,27 +10018,11 @@ function CalendarTimeline({
|
|
|
9911
10018
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
9912
10019
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "space-y-2", children: [
|
|
9913
10020
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs text-muted-foreground", children: l.start }),
|
|
9914
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9915
|
-
Combobox,
|
|
9916
|
-
{
|
|
9917
|
-
options: createStartOptions,
|
|
9918
|
-
value: createStartIdx,
|
|
9919
|
-
onChange: (v) => setCreateStartIdx(Number(v)),
|
|
9920
|
-
placeholder: l.start
|
|
9921
|
-
}
|
|
9922
|
-
)
|
|
10021
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Combobox, { options: createStartOptions, value: createStartIdx, onChange: (v) => setCreateStartIdx(Number(v)), placeholder: l.start })
|
|
9923
10022
|
] }),
|
|
9924
10023
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "space-y-2", children: [
|
|
9925
10024
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs text-muted-foreground", children: l.end }),
|
|
9926
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9927
|
-
Combobox,
|
|
9928
|
-
{
|
|
9929
|
-
options: createEndOptions,
|
|
9930
|
-
value: createEndIdx,
|
|
9931
|
-
onChange: (v) => setCreateEndIdx(Number(v)),
|
|
9932
|
-
placeholder: l.end
|
|
9933
|
-
}
|
|
9934
|
-
)
|
|
10025
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Combobox, { options: createEndOptions, value: createEndIdx, onChange: (v) => setCreateEndIdx(Number(v)), placeholder: l.end })
|
|
9935
10026
|
] })
|
|
9936
10027
|
] }),
|
|
9937
10028
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-2", children: [
|