@underverse-ui/underverse 0.2.98 → 0.2.100
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 +194 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +194 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8121,6 +8121,22 @@ function startOfZonedDay(date, timeZone) {
|
|
|
8121
8121
|
const p = getZonedParts(date, timeZone);
|
|
8122
8122
|
return new Date(zonedTimeToUtcMs({ ...p, hour: 0, minute: 0, second: 0 }, timeZone));
|
|
8123
8123
|
}
|
|
8124
|
+
function zonedDateAtTime(date, timeZone, time) {
|
|
8125
|
+
const p = getZonedParts(date, timeZone);
|
|
8126
|
+
return new Date(
|
|
8127
|
+
zonedTimeToUtcMs(
|
|
8128
|
+
{
|
|
8129
|
+
year: p.year,
|
|
8130
|
+
month: p.month,
|
|
8131
|
+
day: p.day,
|
|
8132
|
+
hour: time.hour,
|
|
8133
|
+
minute: time.minute ?? 0,
|
|
8134
|
+
second: time.second ?? 0
|
|
8135
|
+
},
|
|
8136
|
+
timeZone
|
|
8137
|
+
)
|
|
8138
|
+
);
|
|
8139
|
+
}
|
|
8124
8140
|
function startOfZonedMonth(date, timeZone) {
|
|
8125
8141
|
const p = getZonedParts(date, timeZone);
|
|
8126
8142
|
return new Date(zonedTimeToUtcMs({ year: p.year, month: p.month, day: 1, hour: 0, minute: 0, second: 0 }, timeZone));
|
|
@@ -8429,17 +8445,24 @@ function getGroupResourceCounts(resources) {
|
|
|
8429
8445
|
return counts;
|
|
8430
8446
|
}
|
|
8431
8447
|
function computeSlotStarts(args) {
|
|
8432
|
-
const { view, date, timeZone, weekStartsOn, dayTimeStepMinutes } = args;
|
|
8433
|
-
const
|
|
8448
|
+
const { view, date, timeZone, weekStartsOn, dayTimeStepMinutes, dayRangeMode, workHours } = args;
|
|
8449
|
+
const baseDayStart = startOfZonedDay(date, timeZone);
|
|
8450
|
+
const start = view === "month" ? startOfZonedMonth(date, timeZone) : view === "week" ? startOfZonedWeek(date, weekStartsOn, timeZone) : baseDayStart;
|
|
8434
8451
|
if (view === "day") {
|
|
8435
8452
|
const step = Math.max(5, Math.min(240, Math.trunc(dayTimeStepMinutes)));
|
|
8436
8453
|
const stepMs = step * 6e4;
|
|
8437
|
-
const
|
|
8454
|
+
const hours = workHours ?? { startHour: 8, endHour: 17 };
|
|
8455
|
+
const boundedStartHour = clamp3(Math.trunc(hours.startHour), 0, 23);
|
|
8456
|
+
const boundedEndHour = clamp3(Math.trunc(hours.endHour), 1, 24);
|
|
8457
|
+
const isWork = dayRangeMode === "work";
|
|
8458
|
+
const start2 = isWork ? zonedDateAtTime(baseDayStart, timeZone, { hour: boundedStartHour }) : start;
|
|
8459
|
+
const end2 = isWork ? boundedEndHour === 24 ? addZonedDays(baseDayStart, 1, timeZone) : zonedDateAtTime(baseDayStart, timeZone, { hour: clamp3(boundedEndHour, 0, 23) }) : addZonedDays(start, 1, timeZone);
|
|
8460
|
+
const end3 = end2.getTime() > start2.getTime() ? end2 : addZonedDays(start2, 1, timeZone);
|
|
8438
8461
|
const slotStarts2 = [];
|
|
8439
|
-
for (let cur2 =
|
|
8462
|
+
for (let cur2 = start2.getTime(), guard2 = 0; cur2 < end3.getTime() && guard2++ < 2e3; cur2 += stepMs) {
|
|
8440
8463
|
slotStarts2.push(new Date(cur2));
|
|
8441
8464
|
}
|
|
8442
|
-
return { start, end:
|
|
8465
|
+
return { start: start2, end: end3, slotStarts: slotStarts2 };
|
|
8443
8466
|
}
|
|
8444
8467
|
const end = view === "month" ? startOfZonedMonth(addZonedMonths(start, 1, timeZone), timeZone) : addZonedDays(start, 7, timeZone);
|
|
8445
8468
|
const slotStarts = [];
|
|
@@ -8736,9 +8759,12 @@ function CalendarTimeline({
|
|
|
8736
8759
|
rowHeights,
|
|
8737
8760
|
defaultRowHeights,
|
|
8738
8761
|
onRowHeightsChange,
|
|
8762
|
+
autoRowHeight,
|
|
8739
8763
|
enableLayoutResize,
|
|
8740
8764
|
slotMinWidth,
|
|
8741
8765
|
dayTimeStepMinutes = 60,
|
|
8766
|
+
dayRangeMode,
|
|
8767
|
+
workHours,
|
|
8742
8768
|
maxLanesPerRow = 3,
|
|
8743
8769
|
now,
|
|
8744
8770
|
renderResource,
|
|
@@ -8786,6 +8812,10 @@ function CalendarTimeline({
|
|
|
8786
8812
|
[isControlledEventSheetOpen, onEventSheetOpenChange, setSelectedEventId]
|
|
8787
8813
|
);
|
|
8788
8814
|
const sizeConfig = React28.useMemo(() => getSizeConfig(size), [size]);
|
|
8815
|
+
const densityClass = sizeConfig.densityClass;
|
|
8816
|
+
const eventHeight = sizeConfig.eventHeight;
|
|
8817
|
+
const laneGap = sizeConfig.laneGap;
|
|
8818
|
+
const lanePaddingY = sizeConfig.lanePaddingY;
|
|
8789
8819
|
const canResizeColumn = React28.useMemo(() => {
|
|
8790
8820
|
const cfg = enableLayoutResize;
|
|
8791
8821
|
if (!cfg) return false;
|
|
@@ -8900,7 +8930,9 @@ function CalendarTimeline({
|
|
|
8900
8930
|
date: activeDate,
|
|
8901
8931
|
timeZone: resolvedTimeZone,
|
|
8902
8932
|
weekStartsOn,
|
|
8903
|
-
dayTimeStepMinutes
|
|
8933
|
+
dayTimeStepMinutes,
|
|
8934
|
+
dayRangeMode,
|
|
8935
|
+
workHours
|
|
8904
8936
|
});
|
|
8905
8937
|
const todayStart = startOfZonedDay(resolvedNow, resolvedTimeZone).getTime();
|
|
8906
8938
|
const slotItems = slotStarts2.map((s) => ({
|
|
@@ -8909,7 +8941,7 @@ function CalendarTimeline({
|
|
|
8909
8941
|
isToday: startOfZonedDay(s, resolvedTimeZone).getTime() === todayStart
|
|
8910
8942
|
}));
|
|
8911
8943
|
return { slots: slotItems, range: { start, end } };
|
|
8912
|
-
}, [activeView, activeDate, resolvedTimeZone, resolvedLocale, weekStartsOn, dayTimeStepMinutes, resolvedNow, formatters]);
|
|
8944
|
+
}, [activeView, activeDate, resolvedTimeZone, resolvedLocale, weekStartsOn, dayTimeStepMinutes, dayRangeMode, workHours, resolvedNow, formatters]);
|
|
8913
8945
|
React28.useEffect(() => {
|
|
8914
8946
|
onRangeChange?.(range);
|
|
8915
8947
|
}, [range.start, range.end, onRangeChange]);
|
|
@@ -8919,7 +8951,7 @@ function CalendarTimeline({
|
|
|
8919
8951
|
const bodyClientWidth = useClientWidth(bodyRef);
|
|
8920
8952
|
const slotStarts = React28.useMemo(() => slots.map((s) => s.start), [slots]);
|
|
8921
8953
|
const slotWidth = React28.useMemo(() => {
|
|
8922
|
-
const baseSlotWidth = activeView === "month" ? effectiveSlotMinWidth * 3 : effectiveSlotMinWidth;
|
|
8954
|
+
const baseSlotWidth = activeView === "month" ? effectiveSlotMinWidth * 3 : activeView === "day" ? effectiveSlotMinWidth * 3 : effectiveSlotMinWidth;
|
|
8923
8955
|
if (activeView !== "week") return baseSlotWidth;
|
|
8924
8956
|
if (bodyClientWidth <= 0) return baseSlotWidth;
|
|
8925
8957
|
if (slots.length <= 0) return baseSlotWidth;
|
|
@@ -8971,13 +9003,43 @@ function CalendarTimeline({
|
|
|
8971
9003
|
setInternalRowHeights(defaultRowHeights);
|
|
8972
9004
|
}, [defaultRowHeights, isControlledRowHeights]);
|
|
8973
9005
|
const activeRowHeights = isControlledRowHeights ? rowHeights : internalRowHeights;
|
|
9006
|
+
const autoRowHeightCfg = React28.useMemo(() => {
|
|
9007
|
+
if (!autoRowHeight) return null;
|
|
9008
|
+
return autoRowHeight === true ? {} : autoRowHeight;
|
|
9009
|
+
}, [autoRowHeight]);
|
|
9010
|
+
const effectiveMaxLanesPerRow = React28.useMemo(() => {
|
|
9011
|
+
if (!autoRowHeightCfg) return maxLanesPerRow;
|
|
9012
|
+
const maxLanes = autoRowHeightCfg.maxLanesPerRow;
|
|
9013
|
+
if (typeof maxLanes === "number" && Number.isFinite(maxLanes) && maxLanes > 0) return Math.floor(maxLanes);
|
|
9014
|
+
return Number.POSITIVE_INFINITY;
|
|
9015
|
+
}, [autoRowHeightCfg, maxLanesPerRow]);
|
|
9016
|
+
const autoRowHeightsByResource = React28.useMemo(() => {
|
|
9017
|
+
if (!autoRowHeightCfg) return null;
|
|
9018
|
+
const maxRowHeight2 = autoRowHeightCfg.maxRowHeight;
|
|
9019
|
+
const out = /* @__PURE__ */ new Map();
|
|
9020
|
+
for (const [resourceId, list] of eventsByResource.entries()) {
|
|
9021
|
+
const mapped = list.map((ev) => {
|
|
9022
|
+
const startIdx = binarySearchLastLE(slotStarts, ev._start);
|
|
9023
|
+
const endIdx = clamp3(binarySearchFirstGE(slotStarts, ev._end), startIdx + 1, slots.length);
|
|
9024
|
+
return { startIdx, endIdx };
|
|
9025
|
+
});
|
|
9026
|
+
const { laneCount } = intervalPack(mapped);
|
|
9027
|
+
const lanesToFit = Number.isFinite(effectiveMaxLanesPerRow) ? Math.max(1, Math.min(laneCount, effectiveMaxLanesPerRow)) : Math.max(1, laneCount);
|
|
9028
|
+
const needed = lanePaddingY * 2 + lanesToFit * eventHeight + laneGap * Math.max(0, lanesToFit - 1);
|
|
9029
|
+
const next = typeof maxRowHeight2 === "number" && Number.isFinite(maxRowHeight2) && maxRowHeight2 > 0 ? Math.min(needed, maxRowHeight2) : needed;
|
|
9030
|
+
out.set(resourceId, next);
|
|
9031
|
+
}
|
|
9032
|
+
return out;
|
|
9033
|
+
}, [autoRowHeightCfg, eventHeight, eventsByResource, laneGap, lanePaddingY, slotStarts, slots.length, effectiveMaxLanesPerRow]);
|
|
8974
9034
|
const getResourceRowHeight = React28.useCallback(
|
|
8975
9035
|
(resourceId) => {
|
|
8976
9036
|
const h = activeRowHeights[resourceId];
|
|
8977
|
-
|
|
8978
|
-
|
|
9037
|
+
const base = typeof h === "number" && Number.isFinite(h) && h > 0 ? h : effectiveRowHeight;
|
|
9038
|
+
const auto = autoRowHeightsByResource?.get(resourceId);
|
|
9039
|
+
if (typeof auto === "number" && Number.isFinite(auto) && auto > 0) return Math.max(base, auto);
|
|
9040
|
+
return base;
|
|
8979
9041
|
},
|
|
8980
|
-
[activeRowHeights, effectiveRowHeight]
|
|
9042
|
+
[activeRowHeights, autoRowHeightsByResource, effectiveRowHeight]
|
|
8981
9043
|
);
|
|
8982
9044
|
const setRowHeightForResource = React28.useCallback(
|
|
8983
9045
|
(resourceId, height) => {
|
|
@@ -9110,10 +9172,6 @@ function CalendarTimeline({
|
|
|
9110
9172
|
const fmt = getDtf(resolvedLocale, resolvedTimeZone, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
|
|
9111
9173
|
return fmt.format(range.start);
|
|
9112
9174
|
}, [activeDate, activeView, formatters, l.week, range.end, range.start, resolvedLocale, resolvedTimeZone]);
|
|
9113
|
-
const densityClass = sizeConfig.densityClass;
|
|
9114
|
-
const eventHeight = sizeConfig.eventHeight;
|
|
9115
|
-
const laneGap = sizeConfig.laneGap;
|
|
9116
|
-
const lanePaddingY = sizeConfig.lanePaddingY;
|
|
9117
9175
|
const createMode = interactions?.createMode ?? "drag";
|
|
9118
9176
|
const canCreate = !isViewOnly && (interactions?.creatable ?? false) && !!onCreateEvent;
|
|
9119
9177
|
const [createOpen, setCreateOpen] = React28.useState(false);
|
|
@@ -9180,18 +9238,33 @@ function CalendarTimeline({
|
|
|
9180
9238
|
const dragRef = React28.useRef(null);
|
|
9181
9239
|
const [preview, setPreview] = React28.useState(null);
|
|
9182
9240
|
const suppressNextEventClickRef = React28.useRef(false);
|
|
9241
|
+
const autoScrollStateRef = React28.useRef({
|
|
9242
|
+
dir: 0,
|
|
9243
|
+
speed: 0,
|
|
9244
|
+
lastClientX: 0,
|
|
9245
|
+
lastClientY: 0
|
|
9246
|
+
});
|
|
9247
|
+
const autoScrollRafRef = React28.useRef(null);
|
|
9248
|
+
const stopAutoScroll = React28.useCallback(() => {
|
|
9249
|
+
if (autoScrollRafRef.current != null) cancelAnimationFrame(autoScrollRafRef.current);
|
|
9250
|
+
autoScrollRafRef.current = null;
|
|
9251
|
+
autoScrollStateRef.current.dir = 0;
|
|
9252
|
+
autoScrollStateRef.current.speed = 0;
|
|
9253
|
+
}, []);
|
|
9183
9254
|
const getPointerContext = React28.useCallback(
|
|
9184
9255
|
(clientX, clientY, opts) => {
|
|
9185
9256
|
const body = bodyRef.current;
|
|
9186
9257
|
if (!body) return null;
|
|
9187
|
-
const el = document.elementFromPoint(clientX, clientY);
|
|
9188
|
-
if (!el || !body.contains(el)) return null;
|
|
9189
9258
|
const bodyRect = body.getBoundingClientRect();
|
|
9190
|
-
const
|
|
9259
|
+
const probeX = clamp3(clientX, bodyRect.left + 1, bodyRect.right - 1);
|
|
9260
|
+
const probeY = clamp3(clientY, bodyRect.top + 1, bodyRect.bottom - 1);
|
|
9261
|
+
const el = document.elementFromPoint(probeX, probeY);
|
|
9262
|
+
const x = probeX - bodyRect.left + body.scrollLeft;
|
|
9191
9263
|
const epsilon = opts?.biasLeft ? 0.01 : 0;
|
|
9192
9264
|
const slotIdx = clamp3(Math.floor((x - epsilon) / slotWidth), 0, Math.max(0, slots.length - 1));
|
|
9193
|
-
const rowEl = el
|
|
9194
|
-
const rid = rowEl?.dataset?.uvCtRow ?? null;
|
|
9265
|
+
const rowEl = el && body.contains(el) ? el.closest?.("[data-uv-ct-row]") ?? null : null;
|
|
9266
|
+
const rid = rowEl?.dataset?.uvCtRow ?? opts?.fallbackResourceId ?? null;
|
|
9267
|
+
if (!rid) return null;
|
|
9195
9268
|
return { slotIdx, resourceId: rid, x };
|
|
9196
9269
|
},
|
|
9197
9270
|
[slotWidth, slots.length]
|
|
@@ -9207,6 +9280,97 @@ function CalendarTimeline({
|
|
|
9207
9280
|
},
|
|
9208
9281
|
[activeView, dayTimeStepMinutes, resolvedTimeZone, slotStarts]
|
|
9209
9282
|
);
|
|
9283
|
+
const updateDragPreview = React28.useCallback(
|
|
9284
|
+
(clientX, clientY) => {
|
|
9285
|
+
const drag = dragRef.current;
|
|
9286
|
+
if (!drag) return;
|
|
9287
|
+
const ctx = getPointerContext(clientX, clientY, drag.mode === "create" ? { biasLeft: true, fallbackResourceId: drag.resourceId } : { fallbackResourceId: drag.resourceId });
|
|
9288
|
+
if (!ctx) return;
|
|
9289
|
+
const { slotIdx } = ctx;
|
|
9290
|
+
const movedEnough = Math.abs(clientX - drag.startClientX) > 3 || Math.abs(clientY - drag.startClientY) > 3 || slotIdx !== drag.startSlotIdx || ctx.resourceId !== drag.startRowResourceId;
|
|
9291
|
+
if (movedEnough) suppressNextEventClickRef.current = true;
|
|
9292
|
+
if (drag.mode === "create") {
|
|
9293
|
+
const a = Math.min(drag.startSlotIdx, slotIdx);
|
|
9294
|
+
const b = Math.max(drag.startSlotIdx, slotIdx) + 1;
|
|
9295
|
+
const s = slotToDate(a).start;
|
|
9296
|
+
const e2 = b >= slots.length ? range.end : slotToDate(b).start;
|
|
9297
|
+
setPreview({ resourceId: drag.resourceId, start: s, end: e2 });
|
|
9298
|
+
return;
|
|
9299
|
+
}
|
|
9300
|
+
const targetSlotStart = slotToDate(slotIdx).start;
|
|
9301
|
+
const originSlotStart = slotToDate(drag.startSlotIdx).start;
|
|
9302
|
+
const deltaMs = targetSlotStart.getTime() - originSlotStart.getTime();
|
|
9303
|
+
if (drag.mode === "move") {
|
|
9304
|
+
const nextStart = new Date(drag.originStart.getTime() + deltaMs);
|
|
9305
|
+
const nextEnd = new Date(drag.originEnd.getTime() + deltaMs);
|
|
9306
|
+
setPreview({ eventId: drag.eventId, resourceId: ctx.resourceId, start: nextStart, end: nextEnd });
|
|
9307
|
+
drag.resourceId = ctx.resourceId;
|
|
9308
|
+
return;
|
|
9309
|
+
}
|
|
9310
|
+
if (drag.mode === "resize-start") {
|
|
9311
|
+
const nextStart = new Date(clamp3(targetSlotStart.getTime(), range.start.getTime(), drag.originEnd.getTime() - 6e4));
|
|
9312
|
+
setPreview({ eventId: drag.eventId, resourceId: drag.resourceId, start: nextStart, end: drag.originEnd });
|
|
9313
|
+
return;
|
|
9314
|
+
}
|
|
9315
|
+
if (drag.mode === "resize-end") {
|
|
9316
|
+
const nextEnd = new Date(clamp3(targetSlotStart.getTime(), drag.originStart.getTime() + 6e4, range.end.getTime()));
|
|
9317
|
+
setPreview({ eventId: drag.eventId, resourceId: drag.resourceId, start: drag.originStart, end: nextEnd });
|
|
9318
|
+
return;
|
|
9319
|
+
}
|
|
9320
|
+
},
|
|
9321
|
+
[getPointerContext, range.end, range.start, slotToDate, slots.length]
|
|
9322
|
+
);
|
|
9323
|
+
const autoScrollTick = React28.useCallback(() => {
|
|
9324
|
+
const drag = dragRef.current;
|
|
9325
|
+
const body = bodyRef.current;
|
|
9326
|
+
const st = autoScrollStateRef.current;
|
|
9327
|
+
if (!drag || !body || st.dir === 0) {
|
|
9328
|
+
stopAutoScroll();
|
|
9329
|
+
return;
|
|
9330
|
+
}
|
|
9331
|
+
const maxScrollLeft = Math.max(0, body.scrollWidth - body.clientWidth);
|
|
9332
|
+
const prevLeft = body.scrollLeft;
|
|
9333
|
+
const nextLeft = clamp3(prevLeft + st.dir * st.speed, 0, maxScrollLeft);
|
|
9334
|
+
if (nextLeft === prevLeft) {
|
|
9335
|
+
stopAutoScroll();
|
|
9336
|
+
return;
|
|
9337
|
+
}
|
|
9338
|
+
body.scrollLeft = nextLeft;
|
|
9339
|
+
updateDragPreview(st.lastClientX, st.lastClientY);
|
|
9340
|
+
autoScrollRafRef.current = requestAnimationFrame(autoScrollTick);
|
|
9341
|
+
}, [stopAutoScroll, updateDragPreview]);
|
|
9342
|
+
const updateAutoScrollFromPointer = React28.useCallback(
|
|
9343
|
+
(clientX, clientY) => {
|
|
9344
|
+
const body = bodyRef.current;
|
|
9345
|
+
if (!body) return;
|
|
9346
|
+
const rect = body.getBoundingClientRect();
|
|
9347
|
+
const edge = 56;
|
|
9348
|
+
let dir = 0;
|
|
9349
|
+
let speed = 0;
|
|
9350
|
+
if (clientX < rect.left + edge) {
|
|
9351
|
+
dir = -1;
|
|
9352
|
+
const dist = clientX - rect.left;
|
|
9353
|
+
const t2 = clamp3(1 - dist / edge, 0, 1);
|
|
9354
|
+
speed = 8 + t2 * 28;
|
|
9355
|
+
} else if (clientX > rect.right - edge) {
|
|
9356
|
+
dir = 1;
|
|
9357
|
+
const dist = rect.right - clientX;
|
|
9358
|
+
const t2 = clamp3(1 - dist / edge, 0, 1);
|
|
9359
|
+
speed = 8 + t2 * 28;
|
|
9360
|
+
}
|
|
9361
|
+
autoScrollStateRef.current.lastClientX = clientX;
|
|
9362
|
+
autoScrollStateRef.current.lastClientY = clientY;
|
|
9363
|
+
autoScrollStateRef.current.dir = dir;
|
|
9364
|
+
autoScrollStateRef.current.speed = speed;
|
|
9365
|
+
if (dir === 0) {
|
|
9366
|
+
stopAutoScroll();
|
|
9367
|
+
return;
|
|
9368
|
+
}
|
|
9369
|
+
if (autoScrollRafRef.current == null) autoScrollRafRef.current = requestAnimationFrame(autoScrollTick);
|
|
9370
|
+
},
|
|
9371
|
+
[autoScrollTick, stopAutoScroll]
|
|
9372
|
+
);
|
|
9373
|
+
React28.useEffect(() => stopAutoScroll, [stopAutoScroll]);
|
|
9210
9374
|
const onPointerDownEvent = (e, ev, mode) => {
|
|
9211
9375
|
if (e.button !== 0 || e.ctrlKey) return;
|
|
9212
9376
|
if (isViewOnly) return;
|
|
@@ -9218,6 +9382,8 @@ function CalendarTimeline({
|
|
|
9218
9382
|
suppressNextEventClickRef.current = false;
|
|
9219
9383
|
const startIdx = binarySearchLastLE(slotStarts, ev._start);
|
|
9220
9384
|
const endIdx = binarySearchFirstGE(slotStarts, ev._end);
|
|
9385
|
+
const pointerCtx = getPointerContext(e.clientX, e.clientY, { fallbackResourceId: ev.resourceId });
|
|
9386
|
+
const grabSlotIdx = pointerCtx?.slotIdx ?? startIdx;
|
|
9221
9387
|
dragRef.current = {
|
|
9222
9388
|
mode,
|
|
9223
9389
|
eventId: ev.id,
|
|
@@ -9226,7 +9392,7 @@ function CalendarTimeline({
|
|
|
9226
9392
|
originEnd: ev._end,
|
|
9227
9393
|
durationMs: ev._end.getTime() - ev._start.getTime(),
|
|
9228
9394
|
pointerId: e.pointerId,
|
|
9229
|
-
startSlotIdx:
|
|
9395
|
+
startSlotIdx: grabSlotIdx,
|
|
9230
9396
|
startRowResourceId: ev.resourceId,
|
|
9231
9397
|
startClientX: e.clientX,
|
|
9232
9398
|
startClientY: e.clientY
|
|
@@ -9282,44 +9448,14 @@ function CalendarTimeline({
|
|
|
9282
9448
|
const onPointerMove = (e) => {
|
|
9283
9449
|
const drag = dragRef.current;
|
|
9284
9450
|
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
const { slotIdx } = ctx;
|
|
9288
|
-
const movedEnough = Math.abs(e.clientX - drag.startClientX) > 3 || Math.abs(e.clientY - drag.startClientY) > 3 || slotIdx !== drag.startSlotIdx || ctx.resourceId !== drag.startRowResourceId;
|
|
9289
|
-
if (movedEnough) suppressNextEventClickRef.current = true;
|
|
9290
|
-
if (drag.mode === "create") {
|
|
9291
|
-
const a = Math.min(drag.startSlotIdx, slotIdx);
|
|
9292
|
-
const b = Math.max(drag.startSlotIdx, slotIdx) + 1;
|
|
9293
|
-
const s = slotToDate(a).start;
|
|
9294
|
-
const e2 = b >= slots.length ? range.end : slotToDate(b).start;
|
|
9295
|
-
setPreview({ resourceId: drag.resourceId, start: s, end: e2 });
|
|
9296
|
-
return;
|
|
9297
|
-
}
|
|
9298
|
-
const targetSlotStart = slotToDate(slotIdx).start;
|
|
9299
|
-
const originSlotStart = slotToDate(drag.startSlotIdx).start;
|
|
9300
|
-
const deltaMs = targetSlotStart.getTime() - originSlotStart.getTime();
|
|
9301
|
-
if (drag.mode === "move") {
|
|
9302
|
-
const nextStart = new Date(drag.originStart.getTime() + deltaMs);
|
|
9303
|
-
const nextEnd = new Date(drag.originEnd.getTime() + deltaMs);
|
|
9304
|
-
setPreview({ eventId: drag.eventId, resourceId: ctx.resourceId, start: nextStart, end: nextEnd });
|
|
9305
|
-
drag.resourceId = ctx.resourceId;
|
|
9306
|
-
return;
|
|
9307
|
-
}
|
|
9308
|
-
if (drag.mode === "resize-start") {
|
|
9309
|
-
const nextStart = new Date(clamp3(targetSlotStart.getTime(), range.start.getTime(), drag.originEnd.getTime() - 6e4));
|
|
9310
|
-
setPreview({ eventId: drag.eventId, resourceId: drag.resourceId, start: nextStart, end: drag.originEnd });
|
|
9311
|
-
return;
|
|
9312
|
-
}
|
|
9313
|
-
if (drag.mode === "resize-end") {
|
|
9314
|
-
const nextEnd = new Date(clamp3(targetSlotStart.getTime(), drag.originStart.getTime() + 6e4, range.end.getTime()));
|
|
9315
|
-
setPreview({ eventId: drag.eventId, resourceId: drag.resourceId, start: drag.originStart, end: nextEnd });
|
|
9316
|
-
return;
|
|
9317
|
-
}
|
|
9451
|
+
updateAutoScrollFromPointer(e.clientX, e.clientY);
|
|
9452
|
+
updateDragPreview(e.clientX, e.clientY);
|
|
9318
9453
|
};
|
|
9319
9454
|
const onPointerUp = (e) => {
|
|
9320
9455
|
const drag = dragRef.current;
|
|
9321
9456
|
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
9322
9457
|
dragRef.current = null;
|
|
9458
|
+
stopAutoScroll();
|
|
9323
9459
|
if (!preview) {
|
|
9324
9460
|
setPreview(null);
|
|
9325
9461
|
return;
|
|
@@ -9420,10 +9556,10 @@ function CalendarTimeline({
|
|
|
9420
9556
|
return { ev: { ...ev, _start: s, _end: e }, startIdx, endIdx };
|
|
9421
9557
|
});
|
|
9422
9558
|
const { packed, laneCount } = intervalPack(mapped);
|
|
9423
|
-
const visible = packed.filter((p) => p.lane <
|
|
9424
|
-
const hidden = packed.filter((p) => p.lane >=
|
|
9559
|
+
const visible = packed.filter((p) => p.lane < effectiveMaxLanesPerRow);
|
|
9560
|
+
const hidden = packed.filter((p) => p.lane >= effectiveMaxLanesPerRow);
|
|
9425
9561
|
const rowHeightPx = getResourceRowHeight(resourceId);
|
|
9426
|
-
const visibleLaneCount = Math.max(1, Math.min(laneCount,
|
|
9562
|
+
const visibleLaneCount = Math.max(1, Math.min(laneCount, effectiveMaxLanesPerRow));
|
|
9427
9563
|
const available = Math.max(0, rowHeightPx - lanePaddingY * 2 - laneGap * Math.max(0, visibleLaneCount - 1));
|
|
9428
9564
|
const fitPerLane = visibleLaneCount > 0 ? Math.floor(available / visibleLaneCount) : eventHeight;
|
|
9429
9565
|
const perLaneHeight = Math.max(9, Math.min(eventHeight, fitPerLane || eventHeight));
|
|
@@ -9441,7 +9577,7 @@ function CalendarTimeline({
|
|
|
9441
9577
|
});
|
|
9442
9578
|
}
|
|
9443
9579
|
return map;
|
|
9444
|
-
}, [eventsByResource, getResourceRowHeight, laneGap, lanePaddingY, slotStarts, slots.length, slotWidth,
|
|
9580
|
+
}, [eventsByResource, getResourceRowHeight, laneGap, lanePaddingY, slotStarts, slots.length, slotWidth, effectiveMaxLanesPerRow, preview, eventHeight]);
|
|
9445
9581
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
9446
9582
|
"div",
|
|
9447
9583
|
{
|