@underverse-ui/underverse 0.2.95 → 0.2.96
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 +193 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +196 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8492,6 +8492,9 @@ function CalendarTimelineHeader(props) {
|
|
|
8492
8492
|
title,
|
|
8493
8493
|
resourcesHeaderLabel,
|
|
8494
8494
|
labels,
|
|
8495
|
+
newEventLabel,
|
|
8496
|
+
newEventDisabled,
|
|
8497
|
+
onNewEventClick,
|
|
8495
8498
|
activeView,
|
|
8496
8499
|
sizeConfig,
|
|
8497
8500
|
navigate,
|
|
@@ -8542,24 +8545,38 @@ function CalendarTimelineHeader(props) {
|
|
|
8542
8545
|
] }),
|
|
8543
8546
|
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: cn("ml-3 font-semibold tracking-tight truncate text-foreground", sizeConfig.titleClass), children: title })
|
|
8544
8547
|
] }),
|
|
8545
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8548
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
8549
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
8550
|
+
Button_default,
|
|
8551
|
+
{
|
|
8552
|
+
variant: "default",
|
|
8553
|
+
size: "sm",
|
|
8554
|
+
icon: import_lucide_react18.Plus,
|
|
8555
|
+
disabled: newEventDisabled || !onNewEventClick,
|
|
8556
|
+
onClick: onNewEventClick,
|
|
8557
|
+
className: cn(sizeConfig.controlButtonTextClass, "rounded-lg font-medium transition-all duration-200 gap-1.5"),
|
|
8558
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "hidden sm:inline", children: newEventLabel })
|
|
8559
|
+
}
|
|
8560
|
+
),
|
|
8561
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex items-center bg-muted/40 rounded-xl p-1 gap-0.5", children: ["month", "week", "day"].map((v) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
8562
|
+
Button_default,
|
|
8563
|
+
{
|
|
8564
|
+
variant: activeView === v ? "default" : "ghost",
|
|
8565
|
+
size: "sm",
|
|
8566
|
+
onClick: () => setView(v),
|
|
8567
|
+
className: cn(
|
|
8568
|
+
sizeConfig.controlButtonTextClass,
|
|
8569
|
+
"rounded-lg font-medium transition-all duration-200 gap-1.5",
|
|
8570
|
+
activeView === v ? "bg-primary text-primary-foreground shadow-sm shadow-primary/25" : "hover:bg-background/80 text-muted-foreground hover:text-foreground"
|
|
8571
|
+
),
|
|
8572
|
+
children: [
|
|
8573
|
+
VIEW_ICONS[v],
|
|
8574
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "hidden sm:inline", children: labels[v] })
|
|
8575
|
+
]
|
|
8576
|
+
},
|
|
8577
|
+
v
|
|
8578
|
+
)) })
|
|
8579
|
+
] })
|
|
8563
8580
|
] }),
|
|
8564
8581
|
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex border-t border-border/20", children: [
|
|
8565
8582
|
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
@@ -8728,6 +8745,7 @@ function CalendarTimeline({
|
|
|
8728
8745
|
onRangeChange,
|
|
8729
8746
|
onEventClick,
|
|
8730
8747
|
onEventDoubleClick,
|
|
8748
|
+
onCreateEventClick,
|
|
8731
8749
|
onCreateEvent,
|
|
8732
8750
|
onEventMove,
|
|
8733
8751
|
onEventResize,
|
|
@@ -8815,6 +8833,13 @@ function CalendarTimeline({
|
|
|
8815
8833
|
month: labels?.month ?? t("month"),
|
|
8816
8834
|
week: labels?.week ?? t("week"),
|
|
8817
8835
|
day: labels?.day ?? t("day"),
|
|
8836
|
+
newEvent: labels?.newEvent ?? t("newEvent"),
|
|
8837
|
+
createEventTitle: labels?.createEventTitle ?? t("createEventTitle"),
|
|
8838
|
+
create: labels?.create ?? t("create"),
|
|
8839
|
+
cancel: labels?.cancel ?? t("cancel"),
|
|
8840
|
+
resource: labels?.resource ?? t("resource"),
|
|
8841
|
+
start: labels?.start ?? t("start"),
|
|
8842
|
+
end: labels?.end ?? t("end"),
|
|
8818
8843
|
expandGroup: labels?.expandGroup ?? t("expandGroup"),
|
|
8819
8844
|
collapseGroup: labels?.collapseGroup ?? t("collapseGroup"),
|
|
8820
8845
|
more: labels?.more ?? ((n) => t("more", { n })),
|
|
@@ -9082,6 +9107,69 @@ function CalendarTimeline({
|
|
|
9082
9107
|
const eventHeight = sizeConfig.eventHeight;
|
|
9083
9108
|
const laneGap = sizeConfig.laneGap;
|
|
9084
9109
|
const lanePaddingY = sizeConfig.lanePaddingY;
|
|
9110
|
+
const createMode = interactions?.createMode ?? "drag";
|
|
9111
|
+
const canCreate = (interactions?.creatable ?? false) && !!onCreateEvent;
|
|
9112
|
+
const [createOpen, setCreateOpen] = React28.useState(false);
|
|
9113
|
+
const [createResourceId, setCreateResourceId] = React28.useState(null);
|
|
9114
|
+
const [createStartIdx, setCreateStartIdx] = React28.useState(0);
|
|
9115
|
+
const [createEndIdx, setCreateEndIdx] = React28.useState(1);
|
|
9116
|
+
const resourceOptions = React28.useMemo(() => {
|
|
9117
|
+
return resources.map((r) => ({
|
|
9118
|
+
label: typeof r.label === "string" ? r.label : r.id,
|
|
9119
|
+
value: r.id,
|
|
9120
|
+
description: r.groupId ? String(r.groupId) : void 0,
|
|
9121
|
+
disabled: r.disabled ?? false
|
|
9122
|
+
}));
|
|
9123
|
+
}, [resources]);
|
|
9124
|
+
const slotPickerLabel = React28.useMemo(() => {
|
|
9125
|
+
const timeFmt = getDtf(resolvedLocale, resolvedTimeZone, { hour: "2-digit", minute: "2-digit", hourCycle: "h23" });
|
|
9126
|
+
const dayFmt = getDtf(resolvedLocale, resolvedTimeZone, { weekday: "short", month: "short", day: "numeric" });
|
|
9127
|
+
return (d) => activeView === "day" ? timeFmt.format(d) : dayFmt.format(d);
|
|
9128
|
+
}, [activeView, resolvedLocale, resolvedTimeZone]);
|
|
9129
|
+
const openCreate = React28.useCallback(() => {
|
|
9130
|
+
if (!canCreate) return;
|
|
9131
|
+
if (activeEventSheetOpen) setEventSheetOpen(false);
|
|
9132
|
+
const firstResource = resources.find((r) => !r.disabled)?.id ?? resources[0]?.id ?? null;
|
|
9133
|
+
setCreateResourceId(firstResource ?? null);
|
|
9134
|
+
let startIdx = 0;
|
|
9135
|
+
if (slots.length > 0) {
|
|
9136
|
+
if (activeView === "day") {
|
|
9137
|
+
const inRange = resolvedNow.getTime() >= range.start.getTime() && resolvedNow.getTime() < range.end.getTime();
|
|
9138
|
+
startIdx = clamp3(inRange ? binarySearchFirstGE(slotStarts, resolvedNow) : 0, 0, slots.length - 1);
|
|
9139
|
+
} else {
|
|
9140
|
+
const dayStart = startOfZonedDay(activeDate, resolvedTimeZone);
|
|
9141
|
+
startIdx = clamp3(binarySearchLastLE(slotStarts, dayStart), 0, slots.length - 1);
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
setCreateStartIdx(startIdx);
|
|
9145
|
+
setCreateEndIdx(Math.min(slots.length, startIdx + 1));
|
|
9146
|
+
setCreateOpen(true);
|
|
9147
|
+
}, [activeDate, activeEventSheetOpen, activeView, canCreate, range.end, range.start, resolvedNow, resolvedTimeZone, resources, setEventSheetOpen, slotStarts, slots.length]);
|
|
9148
|
+
React28.useEffect(() => {
|
|
9149
|
+
setCreateEndIdx((prev) => Math.min(slots.length, Math.max(prev, createStartIdx + 1)));
|
|
9150
|
+
}, [createStartIdx, slots.length]);
|
|
9151
|
+
const createStartOptions = React28.useMemo(() => {
|
|
9152
|
+
return slotStarts.map((d, idx) => ({ label: slotPickerLabel(d), value: idx }));
|
|
9153
|
+
}, [slotStarts, slotPickerLabel]);
|
|
9154
|
+
const createEndOptions = React28.useMemo(() => {
|
|
9155
|
+
const out = [];
|
|
9156
|
+
for (let idx = createStartIdx + 1; idx <= slotStarts.length; idx++) {
|
|
9157
|
+
const boundary = idx >= slotStarts.length ? range.end : slotStarts[idx];
|
|
9158
|
+
out.push({ label: slotPickerLabel(boundary), value: idx });
|
|
9159
|
+
}
|
|
9160
|
+
return out;
|
|
9161
|
+
}, [createStartIdx, range.end, slotPickerLabel, slotStarts]);
|
|
9162
|
+
const commitCreate = React28.useCallback(() => {
|
|
9163
|
+
if (!onCreateEvent) return;
|
|
9164
|
+
if (!createResourceId) return;
|
|
9165
|
+
const start = slotStarts[clamp3(createStartIdx, 0, Math.max(0, slotStarts.length - 1))];
|
|
9166
|
+
if (!start) return;
|
|
9167
|
+
const endBoundary = createEndIdx >= slotStarts.length ? range.end : slotStarts[createEndIdx];
|
|
9168
|
+
if (!endBoundary) return;
|
|
9169
|
+
if (endBoundary.getTime() <= start.getTime()) return;
|
|
9170
|
+
onCreateEvent({ resourceId: createResourceId, start, end: endBoundary });
|
|
9171
|
+
setCreateOpen(false);
|
|
9172
|
+
}, [createEndIdx, createResourceId, createStartIdx, onCreateEvent, range.end, slotStarts]);
|
|
9085
9173
|
const dragRef = React28.useRef(null);
|
|
9086
9174
|
const [preview, setPreview] = React28.useState(null);
|
|
9087
9175
|
const suppressNextEventClickRef = React28.useRef(false);
|
|
@@ -9142,6 +9230,7 @@ function CalendarTimeline({
|
|
|
9142
9230
|
const onPointerDownCell = (e) => {
|
|
9143
9231
|
if (e.button !== 0 || e.ctrlKey) return;
|
|
9144
9232
|
if (!(interactions?.creatable ?? false) || !onCreateEvent) return;
|
|
9233
|
+
if (createMode === "click") return;
|
|
9145
9234
|
const ctx = getPointerContext(e.clientX, e.clientY, { biasLeft: true });
|
|
9146
9235
|
if (!ctx?.resourceId) return;
|
|
9147
9236
|
const { start } = slotToDate(ctx.slotIdx);
|
|
@@ -9162,6 +9251,24 @@ function CalendarTimeline({
|
|
|
9162
9251
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
9163
9252
|
e.preventDefault();
|
|
9164
9253
|
};
|
|
9254
|
+
const onClickCell = (e) => {
|
|
9255
|
+
if (e.button !== 0 || e.ctrlKey) return;
|
|
9256
|
+
if (!(interactions?.creatable ?? false)) return;
|
|
9257
|
+
if (createMode !== "click") return;
|
|
9258
|
+
if (!onCreateEventClick) return;
|
|
9259
|
+
const ctx = getPointerContext(e.clientX, e.clientY, { biasLeft: true });
|
|
9260
|
+
if (!ctx?.resourceId) return;
|
|
9261
|
+
const { start, end } = slotToDate(ctx.slotIdx);
|
|
9262
|
+
onCreateEventClick({
|
|
9263
|
+
resourceId: ctx.resourceId,
|
|
9264
|
+
start,
|
|
9265
|
+
end: ctx.slotIdx + 1 >= slots.length ? range.end : end,
|
|
9266
|
+
slotIdx: ctx.slotIdx,
|
|
9267
|
+
view: activeView,
|
|
9268
|
+
locale: resolvedLocale,
|
|
9269
|
+
timeZone: resolvedTimeZone
|
|
9270
|
+
});
|
|
9271
|
+
};
|
|
9165
9272
|
const onPointerMove = (e) => {
|
|
9166
9273
|
const drag = dragRef.current;
|
|
9167
9274
|
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
@@ -9276,6 +9383,9 @@ function CalendarTimeline({
|
|
|
9276
9383
|
title,
|
|
9277
9384
|
resourcesHeaderLabel: t("resourcesHeader"),
|
|
9278
9385
|
labels: { today: l.today, prev: l.prev, next: l.next, month: l.month, week: l.week, day: l.day },
|
|
9386
|
+
newEventLabel: l.newEvent,
|
|
9387
|
+
newEventDisabled: !canCreate || resources.length === 0,
|
|
9388
|
+
onNewEventClick: openCreate,
|
|
9279
9389
|
activeView,
|
|
9280
9390
|
sizeConfig,
|
|
9281
9391
|
navigate,
|
|
@@ -9391,7 +9501,7 @@ function CalendarTimeline({
|
|
|
9391
9501
|
"data-uv-ct-row": r.id,
|
|
9392
9502
|
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "relative shrink-0", style: { width: gridWidth, minWidth: gridWidth, height: "100%" }, children: [
|
|
9393
9503
|
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-px bg-border/25" }),
|
|
9394
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "absolute inset-0", onPointerDown: onPointerDownCell, "data-uv-ct-timeline": true, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "absolute inset-0 flex", children: slots.map((s, i2) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9504
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "absolute inset-0", onPointerDown: onPointerDownCell, onClick: onClickCell, "data-uv-ct-timeline": true, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "absolute inset-0 flex", children: slots.map((s, i2) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9395
9505
|
"div",
|
|
9396
9506
|
{
|
|
9397
9507
|
className: cn(
|
|
@@ -9563,6 +9673,70 @@ function CalendarTimeline({
|
|
|
9563
9673
|
] })
|
|
9564
9674
|
] })
|
|
9565
9675
|
}
|
|
9676
|
+
) : null,
|
|
9677
|
+
canCreate ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9678
|
+
Sheet,
|
|
9679
|
+
{
|
|
9680
|
+
open: createOpen,
|
|
9681
|
+
onOpenChange: setCreateOpen,
|
|
9682
|
+
side: "right",
|
|
9683
|
+
size: "md",
|
|
9684
|
+
title: l.createEventTitle,
|
|
9685
|
+
description: activeView === "day" ? l.day : activeView === "week" ? l.week : l.month,
|
|
9686
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "space-y-4", children: [
|
|
9687
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "space-y-2", children: [
|
|
9688
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs text-muted-foreground", children: l.resource }),
|
|
9689
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9690
|
+
Combobox,
|
|
9691
|
+
{
|
|
9692
|
+
options: resourceOptions,
|
|
9693
|
+
value: createResourceId,
|
|
9694
|
+
onChange: (v) => setCreateResourceId(v),
|
|
9695
|
+
placeholder: l.resource
|
|
9696
|
+
}
|
|
9697
|
+
)
|
|
9698
|
+
] }),
|
|
9699
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
9700
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "space-y-2", children: [
|
|
9701
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs text-muted-foreground", children: l.start }),
|
|
9702
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9703
|
+
Combobox,
|
|
9704
|
+
{
|
|
9705
|
+
options: createStartOptions,
|
|
9706
|
+
value: createStartIdx,
|
|
9707
|
+
onChange: (v) => setCreateStartIdx(Number(v)),
|
|
9708
|
+
placeholder: l.start
|
|
9709
|
+
}
|
|
9710
|
+
)
|
|
9711
|
+
] }),
|
|
9712
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "space-y-2", children: [
|
|
9713
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "text-xs text-muted-foreground", children: l.end }),
|
|
9714
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9715
|
+
Combobox,
|
|
9716
|
+
{
|
|
9717
|
+
options: createEndOptions,
|
|
9718
|
+
value: createEndIdx,
|
|
9719
|
+
onChange: (v) => setCreateEndIdx(Number(v)),
|
|
9720
|
+
placeholder: l.end
|
|
9721
|
+
}
|
|
9722
|
+
)
|
|
9723
|
+
] })
|
|
9724
|
+
] }),
|
|
9725
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-2", children: [
|
|
9726
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Button_default, { variant: "ghost", size: "sm", onClick: () => setCreateOpen(false), children: l.cancel }),
|
|
9727
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
9728
|
+
Button_default,
|
|
9729
|
+
{
|
|
9730
|
+
variant: "default",
|
|
9731
|
+
size: "sm",
|
|
9732
|
+
onClick: commitCreate,
|
|
9733
|
+
disabled: !createResourceId || createEndIdx <= createStartIdx || createStartOptions.length === 0,
|
|
9734
|
+
children: l.create
|
|
9735
|
+
}
|
|
9736
|
+
)
|
|
9737
|
+
] })
|
|
9738
|
+
] })
|
|
9739
|
+
}
|
|
9566
9740
|
) : null
|
|
9567
9741
|
]
|
|
9568
9742
|
}
|