@underverse-ui/underverse 1.0.4 → 1.0.5
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 +233 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +233 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -557,6 +557,8 @@ var defaultTranslations = {
|
|
|
557
557
|
month: "Month",
|
|
558
558
|
week: "Week",
|
|
559
559
|
day: "Day",
|
|
560
|
+
sprint: "Sprint",
|
|
561
|
+
sprints: "{n} sprints",
|
|
560
562
|
resourcesHeader: "Resources",
|
|
561
563
|
expandGroup: "Expand group",
|
|
562
564
|
collapseGroup: "Collapse group",
|
|
@@ -633,6 +635,8 @@ var defaultTranslations = {
|
|
|
633
635
|
month: "Th\xE1ng",
|
|
634
636
|
week: "Tu\u1EA7n",
|
|
635
637
|
day: "Ng\xE0y",
|
|
638
|
+
sprint: "Sprint",
|
|
639
|
+
sprints: "{n} sprint",
|
|
636
640
|
resourcesHeader: "T\xE0i nguy\xEAn",
|
|
637
641
|
expandGroup: "M\u1EDF nh\xF3m",
|
|
638
642
|
collapseGroup: "Thu g\u1ECDn nh\xF3m",
|
|
@@ -709,6 +713,8 @@ var defaultTranslations = {
|
|
|
709
713
|
month: "\uC6D4",
|
|
710
714
|
week: "\uC8FC",
|
|
711
715
|
day: "\uC77C",
|
|
716
|
+
sprint: "\uC2A4\uD504\uB9B0\uD2B8",
|
|
717
|
+
sprints: "{n} \uC2A4\uD504\uB9B0\uD2B8",
|
|
712
718
|
resourcesHeader: "\uB9AC\uC18C\uC2A4",
|
|
713
719
|
expandGroup: "\uADF8\uB8F9 \uD3BC\uCE58\uAE30",
|
|
714
720
|
collapseGroup: "\uADF8\uB8F9 \uC811\uAE30",
|
|
@@ -785,6 +791,8 @@ var defaultTranslations = {
|
|
|
785
791
|
month: "\u6708",
|
|
786
792
|
week: "\u9031",
|
|
787
793
|
day: "\u65E5",
|
|
794
|
+
sprint: "\u30B9\u30D7\u30EA\u30F3\u30C8",
|
|
795
|
+
sprints: "{n} \u30B9\u30D7\u30EA\u30F3\u30C8",
|
|
788
796
|
resourcesHeader: "\u30EA\u30BD\u30FC\u30B9",
|
|
789
797
|
expandGroup: "\u30B0\u30EB\u30FC\u30D7\u3092\u5C55\u958B",
|
|
790
798
|
collapseGroup: "\u30B0\u30EB\u30FC\u30D7\u3092\u6298\u308A\u305F\u305F\u3080",
|
|
@@ -8395,6 +8403,10 @@ function startOfZonedMonth(date, timeZone) {
|
|
|
8395
8403
|
const p = getZonedParts(date, timeZone);
|
|
8396
8404
|
return new Date(zonedTimeToUtcMs({ year: p.year, month: p.month, day: 1, hour: 0, minute: 0, second: 0 }, timeZone));
|
|
8397
8405
|
}
|
|
8406
|
+
function startOfZonedYear(date, timeZone) {
|
|
8407
|
+
const p = getZonedParts(date, timeZone);
|
|
8408
|
+
return new Date(zonedTimeToUtcMs({ year: p.year, month: 1, day: 1, hour: 0, minute: 0, second: 0 }, timeZone));
|
|
8409
|
+
}
|
|
8398
8410
|
function addZonedDays(date, days, timeZone) {
|
|
8399
8411
|
const p = getZonedParts(date, timeZone);
|
|
8400
8412
|
return new Date(zonedTimeToUtcMs({ ...p, day: p.day + days }, timeZone));
|
|
@@ -8407,6 +8419,9 @@ function addZonedMonths(date, months, timeZone) {
|
|
|
8407
8419
|
const clampedDay = Math.min(p.day, daysInTargetMonth);
|
|
8408
8420
|
return new Date(zonedTimeToUtcMs({ year: next.year, month: next.month, day: clampedDay, hour: p.hour, minute: p.minute, second: p.second }, timeZone));
|
|
8409
8421
|
}
|
|
8422
|
+
function addZonedYears(date, years, timeZone) {
|
|
8423
|
+
return addZonedMonths(date, years * 12, timeZone);
|
|
8424
|
+
}
|
|
8410
8425
|
function startOfZonedWeek(date, weekStartsOn, timeZone) {
|
|
8411
8426
|
const p = getZonedParts(date, timeZone);
|
|
8412
8427
|
const weekday = new Date(Date.UTC(p.year, p.month - 1, p.day)).getUTCDay();
|
|
@@ -8645,6 +8660,13 @@ function defaultSlotHeader(slotStart, view, locale, timeZone) {
|
|
|
8645
8660
|
if (view === "day") {
|
|
8646
8661
|
return getDtf(locale, timeZone, { hour: "2-digit", minute: "2-digit", hourCycle: "h23" }).format(slotStart);
|
|
8647
8662
|
}
|
|
8663
|
+
if (view === "sprint") {
|
|
8664
|
+
const { week } = getIsoWeekInfo(slotStart, timeZone);
|
|
8665
|
+
return /* @__PURE__ */ jsxs28("span", { className: "inline-flex flex-col items-center leading-tight", children: [
|
|
8666
|
+
/* @__PURE__ */ jsx33("span", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70", children: "S" }),
|
|
8667
|
+
/* @__PURE__ */ jsx33("span", { className: "text-sm font-semibold text-foreground", children: String(week).padStart(2, "0") })
|
|
8668
|
+
] });
|
|
8669
|
+
}
|
|
8648
8670
|
const weekday = getDtf(locale, timeZone, { weekday: "short" }).format(slotStart);
|
|
8649
8671
|
const day = getDtf(locale, timeZone, { day: "numeric" }).format(slotStart);
|
|
8650
8672
|
return /* @__PURE__ */ jsxs28("span", { className: "inline-flex flex-col items-center leading-tight", children: [
|
|
@@ -8705,7 +8727,10 @@ function getGroupResourceCounts(resources) {
|
|
|
8705
8727
|
function computeSlotStarts(args) {
|
|
8706
8728
|
const { view, date, timeZone, weekStartsOn, dayTimeStepMinutes, dayRangeMode, workHours } = args;
|
|
8707
8729
|
const baseDayStart = startOfZonedDay(date, timeZone);
|
|
8708
|
-
const start = view === "month" ? startOfZonedMonth(date, timeZone) : view === "week" ? startOfZonedWeek(date, weekStartsOn, timeZone) :
|
|
8730
|
+
const start = view === "month" ? startOfZonedMonth(date, timeZone) : view === "week" ? startOfZonedWeek(date, weekStartsOn, timeZone) : view === "sprint" ? (() => {
|
|
8731
|
+
const yearStart = startOfZonedYear(date, timeZone);
|
|
8732
|
+
return startOfZonedWeek(yearStart, weekStartsOn, timeZone);
|
|
8733
|
+
})() : baseDayStart;
|
|
8709
8734
|
if (view === "day") {
|
|
8710
8735
|
const step = Math.max(5, Math.min(240, Math.trunc(dayTimeStepMinutes)));
|
|
8711
8736
|
const stepMs = step * 6e4;
|
|
@@ -8722,6 +8747,21 @@ function computeSlotStarts(args) {
|
|
|
8722
8747
|
}
|
|
8723
8748
|
return { start: start2, end: end3, slotStarts: slotStarts2 };
|
|
8724
8749
|
}
|
|
8750
|
+
if (view === "sprint") {
|
|
8751
|
+
const yearStart = startOfZonedYear(date, timeZone);
|
|
8752
|
+
const nextYearStart = startOfZonedYear(addZonedMonths(yearStart, 12, timeZone), timeZone);
|
|
8753
|
+
const lastDayOfYear = addZonedDays(nextYearStart, -1, timeZone);
|
|
8754
|
+
const lastSlotStart = startOfZonedWeek(lastDayOfYear, weekStartsOn, timeZone);
|
|
8755
|
+
const end2 = addZonedDays(lastSlotStart, 7, timeZone);
|
|
8756
|
+
const slotStarts2 = [];
|
|
8757
|
+
let cur2 = start;
|
|
8758
|
+
let guard2 = 0;
|
|
8759
|
+
while (cur2.getTime() < end2.getTime() && guard2++ < 60) {
|
|
8760
|
+
slotStarts2.push(cur2);
|
|
8761
|
+
cur2 = addZonedDays(cur2, 7, timeZone);
|
|
8762
|
+
}
|
|
8763
|
+
return { start, end: end2, slotStarts: slotStarts2 };
|
|
8764
|
+
}
|
|
8725
8765
|
const end = view === "month" ? startOfZonedMonth(addZonedMonths(start, 1, timeZone), timeZone) : addZonedDays(start, 7, timeZone);
|
|
8726
8766
|
const slotStarts = [];
|
|
8727
8767
|
let cur = start;
|
|
@@ -8770,7 +8810,8 @@ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
|
8770
8810
|
var VIEW_ICONS = {
|
|
8771
8811
|
month: /* @__PURE__ */ jsx34(CalendarRange, { className: "h-4 w-4" }),
|
|
8772
8812
|
week: /* @__PURE__ */ jsx34(CalendarDays, { className: "h-4 w-4" }),
|
|
8773
|
-
day: /* @__PURE__ */ jsx34(Calendar3, { className: "h-4 w-4" })
|
|
8813
|
+
day: /* @__PURE__ */ jsx34(Calendar3, { className: "h-4 w-4" }),
|
|
8814
|
+
sprint: /* @__PURE__ */ jsx34(CalendarDays, { className: "h-4 w-4" })
|
|
8774
8815
|
};
|
|
8775
8816
|
function CalendarTimelineHeader(props) {
|
|
8776
8817
|
const {
|
|
@@ -8795,7 +8836,7 @@ function CalendarTimelineHeader(props) {
|
|
|
8795
8836
|
slotHeaderNodes
|
|
8796
8837
|
} = props;
|
|
8797
8838
|
const resolvedAvailableViews = React28.useMemo(
|
|
8798
|
-
() => availableViews?.length ? availableViews : ["month", "week", "day"],
|
|
8839
|
+
() => availableViews?.length ? availableViews : ["month", "week", "day", "sprint"],
|
|
8799
8840
|
[availableViews]
|
|
8800
8841
|
);
|
|
8801
8842
|
const showViewSwitcher = resolvedAvailableViews.length > 1;
|
|
@@ -8858,7 +8899,7 @@ function CalendarTimelineHeader(props) {
|
|
|
8858
8899
|
onApplyDateTime(tempDate);
|
|
8859
8900
|
setTodayOpen(false);
|
|
8860
8901
|
}, [onApplyDateTime, tempDate]);
|
|
8861
|
-
return /* @__PURE__ */ jsxs29("div", { className: "sticky top-0 z-30 bg-linear-to-b from-
|
|
8902
|
+
return /* @__PURE__ */ jsxs29("div", { className: "sticky top-0 z-30 bg-linear-to-b from-card via-card to-card/95 border-b border-border/40 backdrop-blur-xl", children: [
|
|
8862
8903
|
/* @__PURE__ */ jsxs29("div", { className: cn("flex items-center justify-between gap-4", sizeConfig.headerPaddingClass), children: [
|
|
8863
8904
|
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
8864
8905
|
/* @__PURE__ */ jsxs29("div", { className: "flex items-center bg-muted/40 rounded-full p-1 gap-0.5", children: [
|
|
@@ -8869,7 +8910,7 @@ function CalendarTimelineHeader(props) {
|
|
|
8869
8910
|
size: "icon",
|
|
8870
8911
|
onClick: () => navigate(-1),
|
|
8871
8912
|
"aria-label": labels.prev,
|
|
8872
|
-
className: cn(sizeConfig.controlButtonIconClass, "rounded-full hover:bg-
|
|
8913
|
+
className: cn(sizeConfig.controlButtonIconClass, "rounded-full hover:bg-card/80 transition-all duration-200"),
|
|
8873
8914
|
children: /* @__PURE__ */ jsx34(ChevronLeft4, { className: "h-4 w-4" })
|
|
8874
8915
|
}
|
|
8875
8916
|
),
|
|
@@ -8884,7 +8925,7 @@ function CalendarTimelineHeader(props) {
|
|
|
8884
8925
|
{
|
|
8885
8926
|
variant: "ghost",
|
|
8886
8927
|
size: "sm",
|
|
8887
|
-
className: cn(sizeConfig.controlButtonTextClass, "rounded-full hover:bg-
|
|
8928
|
+
className: cn(sizeConfig.controlButtonTextClass, "rounded-full hover:bg-card/80 font-medium transition-all duration-200"),
|
|
8888
8929
|
children: labels.today
|
|
8889
8930
|
}
|
|
8890
8931
|
),
|
|
@@ -8964,7 +9005,7 @@ function CalendarTimelineHeader(props) {
|
|
|
8964
9005
|
size: "icon",
|
|
8965
9006
|
onClick: () => navigate(1),
|
|
8966
9007
|
"aria-label": labels.next,
|
|
8967
|
-
className: cn(sizeConfig.controlButtonIconClass, "rounded-full hover:bg-
|
|
9008
|
+
className: cn(sizeConfig.controlButtonIconClass, "rounded-full hover:bg-card/80 transition-all duration-200"),
|
|
8968
9009
|
children: /* @__PURE__ */ jsx34(ChevronRight5, { className: "h-4 w-4" })
|
|
8969
9010
|
}
|
|
8970
9011
|
)
|
|
@@ -8993,7 +9034,7 @@ function CalendarTimelineHeader(props) {
|
|
|
8993
9034
|
className: cn(
|
|
8994
9035
|
sizeConfig.controlButtonTextClass,
|
|
8995
9036
|
"rounded-full font-medium transition-all duration-200 gap-1.5",
|
|
8996
|
-
activeView === v ? "bg-primary text-primary-foreground shadow-sm shadow-primary/25" : "hover:bg-
|
|
9037
|
+
activeView === v ? "bg-primary text-primary-foreground shadow-sm shadow-primary/25" : "hover:bg-card/80 text-muted-foreground hover:text-foreground"
|
|
8997
9038
|
),
|
|
8998
9039
|
children: [
|
|
8999
9040
|
VIEW_ICONS[v],
|
|
@@ -9008,7 +9049,7 @@ function CalendarTimelineHeader(props) {
|
|
|
9008
9049
|
showLeftColumn ? /* @__PURE__ */ jsxs29(
|
|
9009
9050
|
"div",
|
|
9010
9051
|
{
|
|
9011
|
-
className: "shrink-0 border-r border-border/30 bg-
|
|
9052
|
+
className: "shrink-0 border-r border-border/30 bg-card/60 flex items-center justify-center relative group/uv-ct-top-left",
|
|
9012
9053
|
style: { width: effectiveResourceColumnWidth, minWidth: effectiveResourceColumnWidth },
|
|
9013
9054
|
children: [
|
|
9014
9055
|
/* @__PURE__ */ jsx34("span", { className: "text-xs font-medium text-muted-foreground/70 uppercase tracking-wider", children: resourcesHeaderLabel }),
|
|
@@ -9063,7 +9104,7 @@ function DefaultGroupRow(props) {
|
|
|
9063
9104
|
"span",
|
|
9064
9105
|
{
|
|
9065
9106
|
className: cn(
|
|
9066
|
-
"inline-flex items-center justify-center w-5 h-5 rounded-md bg-
|
|
9107
|
+
"inline-flex items-center justify-center w-5 h-5 rounded-md bg-card/60 transition-transform duration-200",
|
|
9067
9108
|
collapsed ? "" : "rotate-180"
|
|
9068
9109
|
),
|
|
9069
9110
|
children: /* @__PURE__ */ jsx35(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
@@ -9084,7 +9125,7 @@ function ResourceRowCell(props) {
|
|
|
9084
9125
|
"div",
|
|
9085
9126
|
{
|
|
9086
9127
|
className: cn(
|
|
9087
|
-
"h-full w-full flex items-center border-b border-border/30 bg-linear-to-r from-
|
|
9128
|
+
"h-full w-full flex items-center border-b border-border/30 bg-linear-to-r from-card to-card/95 relative",
|
|
9088
9129
|
sizeConfig.resourceRowClass,
|
|
9089
9130
|
"hover:from-muted/30 hover:to-muted/10 transition-all duration-200 group/uv-ct-row-header"
|
|
9090
9131
|
),
|
|
@@ -9144,7 +9185,7 @@ var CalendarTimelineGridOverlay = React29.memo(function CalendarTimelineGridOver
|
|
|
9144
9185
|
if (showToday && idx === todaySlotIdx) return null;
|
|
9145
9186
|
const left = slotLefts[idx] ?? 0;
|
|
9146
9187
|
const width = slotWidths[idx] ?? 0;
|
|
9147
|
-
return /* @__PURE__ */ jsx36("div", { className: "absolute top-0 h-full bg-
|
|
9188
|
+
return /* @__PURE__ */ jsx36("div", { className: "absolute top-0 h-full bg-muted/20", style: { left, width }, "aria-hidden": true }, `we_${idx}`);
|
|
9148
9189
|
}) : null,
|
|
9149
9190
|
showToday ? /* @__PURE__ */ jsx36(
|
|
9150
9191
|
"div",
|
|
@@ -9207,7 +9248,8 @@ function useTimelineSlots(args) {
|
|
|
9207
9248
|
dayRangeMode,
|
|
9208
9249
|
workHours,
|
|
9209
9250
|
resolvedNow,
|
|
9210
|
-
formatters
|
|
9251
|
+
formatters,
|
|
9252
|
+
dueDateSprint
|
|
9211
9253
|
} = args;
|
|
9212
9254
|
const { slots, range } = React31.useMemo(() => {
|
|
9213
9255
|
const { start, end, slotStarts: slotStarts2 } = computeSlotStarts({
|
|
@@ -9220,17 +9262,89 @@ function useTimelineSlots(args) {
|
|
|
9220
9262
|
workHours
|
|
9221
9263
|
});
|
|
9222
9264
|
const todayStart = startOfZonedDay(resolvedNow, resolvedTimeZone).getTime();
|
|
9223
|
-
const
|
|
9265
|
+
const nowMs = resolvedNow.getTime();
|
|
9266
|
+
const sprintDefs = (() => {
|
|
9267
|
+
if (activeView !== "sprint") return [];
|
|
9268
|
+
if (!dueDateSprint) return [];
|
|
9269
|
+
const out = [];
|
|
9270
|
+
for (const v of Object.values(dueDateSprint)) {
|
|
9271
|
+
const startInput = v.range_date?.start;
|
|
9272
|
+
const endInput = v.range_date?.end;
|
|
9273
|
+
if (startInput == null || endInput == null) continue;
|
|
9274
|
+
const startRaw = toDate2(startInput);
|
|
9275
|
+
const endRaw = toDate2(endInput);
|
|
9276
|
+
if (Number.isNaN(startRaw.getTime()) || Number.isNaN(endRaw.getTime())) continue;
|
|
9277
|
+
const startMs = startOfZonedDay(startRaw, resolvedTimeZone).getTime();
|
|
9278
|
+
const endMs = startOfZonedDay(endRaw, resolvedTimeZone).getTime();
|
|
9279
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) continue;
|
|
9280
|
+
if (endMs <= startMs) continue;
|
|
9281
|
+
out.push({ startMs, endMs, title: v.title });
|
|
9282
|
+
}
|
|
9283
|
+
out.sort((a, b) => a.startMs - b.startMs);
|
|
9284
|
+
return out;
|
|
9285
|
+
})();
|
|
9286
|
+
const sprintRangeText = (() => {
|
|
9287
|
+
if (activeView !== "sprint") return null;
|
|
9288
|
+
const df = getDtf(resolvedLocale, resolvedTimeZone, { month: "short", day: "numeric" });
|
|
9289
|
+
return (startMs, endMs) => {
|
|
9290
|
+
const a = df.format(new Date(startMs));
|
|
9291
|
+
const b = df.format(new Date(endMs - 1));
|
|
9292
|
+
return a === b ? a : `${a}\u2013${b}`;
|
|
9293
|
+
};
|
|
9294
|
+
})();
|
|
9295
|
+
const matchSprintDef = (slotStart, idx) => {
|
|
9296
|
+
if (activeView !== "sprint") return null;
|
|
9297
|
+
if (sprintDefs.length === 0) return null;
|
|
9298
|
+
const sMs = startOfZonedDay(slotStart, resolvedTimeZone).getTime();
|
|
9299
|
+
const byRange = sprintDefs.find((d) => sMs >= d.startMs && sMs < d.endMs) ?? null;
|
|
9300
|
+
return byRange ?? sprintDefs[idx] ?? null;
|
|
9301
|
+
};
|
|
9302
|
+
const slotItems = slotStarts2.map((s, idx) => ({
|
|
9224
9303
|
start: s,
|
|
9225
|
-
label: formatters?.slotHeader?.(s, { view: activeView, locale: resolvedLocale, timeZone: resolvedTimeZone }) ??
|
|
9226
|
-
|
|
9227
|
-
|
|
9304
|
+
label: formatters?.slotHeader?.(s, { view: activeView, locale: resolvedLocale, timeZone: resolvedTimeZone }) ?? (() => {
|
|
9305
|
+
if (activeView !== "sprint") return defaultSlotHeader(s, activeView, resolvedLocale, resolvedTimeZone);
|
|
9306
|
+
const match = matchSprintDef(s, idx);
|
|
9307
|
+
if (match && sprintRangeText) {
|
|
9308
|
+
const rangeText = sprintRangeText(match.startMs, match.endMs);
|
|
9309
|
+
return React31.createElement(
|
|
9310
|
+
"span",
|
|
9311
|
+
{ className: "inline-flex flex-col items-center leading-tight" },
|
|
9312
|
+
React31.createElement("span", { className: "text-[11px] font-semibold text-foreground truncate max-w-[8rem]" }, match.title),
|
|
9313
|
+
React31.createElement("span", { className: "text-[10px] font-medium text-muted-foreground/70" }, rangeText)
|
|
9314
|
+
);
|
|
9315
|
+
}
|
|
9316
|
+
return React31.createElement(
|
|
9317
|
+
"span",
|
|
9318
|
+
{ className: "inline-flex flex-col items-center leading-tight" },
|
|
9319
|
+
React31.createElement("span", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70" }, "S"),
|
|
9320
|
+
React31.createElement("span", { className: "text-sm font-semibold text-foreground" }, String(idx + 1).padStart(2, "0"))
|
|
9321
|
+
);
|
|
9322
|
+
})(),
|
|
9323
|
+
isToday: (() => {
|
|
9324
|
+
if (activeView !== "sprint") return startOfZonedDay(s, resolvedTimeZone).getTime() === todayStart;
|
|
9325
|
+
const match = matchSprintDef(s, idx);
|
|
9326
|
+
const sprintEndMs = match ? match.endMs : addZonedDays(s, 7, resolvedTimeZone).getTime();
|
|
9327
|
+
return nowMs >= s.getTime() && nowMs < sprintEndMs;
|
|
9328
|
+
})(),
|
|
9329
|
+
isWeekend: activeView === "day" || activeView === "sprint" ? false : (() => {
|
|
9228
9330
|
const wd = getZonedWeekday(s, resolvedTimeZone);
|
|
9229
9331
|
return wd === 0 || wd === 6;
|
|
9230
9332
|
})()
|
|
9231
9333
|
}));
|
|
9232
9334
|
return { slots: slotItems, range: { start, end } };
|
|
9233
|
-
}, [
|
|
9335
|
+
}, [
|
|
9336
|
+
activeView,
|
|
9337
|
+
activeDate,
|
|
9338
|
+
dayRangeMode,
|
|
9339
|
+
dayTimeStepMinutes,
|
|
9340
|
+
dueDateSprint,
|
|
9341
|
+
formatters,
|
|
9342
|
+
resolvedLocale,
|
|
9343
|
+
resolvedNow,
|
|
9344
|
+
resolvedTimeZone,
|
|
9345
|
+
weekStartsOn,
|
|
9346
|
+
workHours
|
|
9347
|
+
]);
|
|
9234
9348
|
const slotStarts = React31.useMemo(() => slots.map((s) => s.start), [slots]);
|
|
9235
9349
|
const todaySlotIdx = React31.useMemo(() => slots.findIndex((s) => s.isToday), [slots]);
|
|
9236
9350
|
const weekendSlotIdxs = React31.useMemo(() => {
|
|
@@ -9521,6 +9635,7 @@ function CalendarTimeline({
|
|
|
9521
9635
|
view,
|
|
9522
9636
|
defaultView = "month",
|
|
9523
9637
|
onViewChange,
|
|
9638
|
+
dueDateSprint,
|
|
9524
9639
|
date,
|
|
9525
9640
|
defaultDate,
|
|
9526
9641
|
onDateChange,
|
|
@@ -9553,6 +9668,8 @@ function CalendarTimeline({
|
|
|
9553
9668
|
adaptiveSlotWidths,
|
|
9554
9669
|
dayEventStyle = "span",
|
|
9555
9670
|
dayEventMaxWidth,
|
|
9671
|
+
monthEventStyle = "span",
|
|
9672
|
+
monthEventMaxWidth,
|
|
9556
9673
|
dayTimeStepMinutes = 60,
|
|
9557
9674
|
enableEventTooltips = true,
|
|
9558
9675
|
dayHeaderMode = "full",
|
|
@@ -9647,18 +9764,29 @@ function CalendarTimeline({
|
|
|
9647
9764
|
setInternalRowHeight(defaultRowHeight);
|
|
9648
9765
|
}, [defaultRowHeight, isControlledRowHeight]);
|
|
9649
9766
|
const effectiveRowHeight = isControlledRowHeight ? rowHeight : internalRowHeight;
|
|
9650
|
-
const
|
|
9767
|
+
const baseSlotMinWidth = slotMinWidth ?? sizeConfig.slotMinWidth;
|
|
9651
9768
|
const colMin = minResourceColumnWidth ?? 160;
|
|
9652
9769
|
const colMax = maxResourceColumnWidth ?? 520;
|
|
9653
9770
|
const rowMin = minRowHeight ?? 36;
|
|
9654
9771
|
const rowMax = maxRowHeight ?? 120;
|
|
9655
9772
|
const availableViews = React32.useMemo(
|
|
9656
|
-
() => onlyView ? [onlyView] : ["month", "week", "day"],
|
|
9773
|
+
() => onlyView ? [onlyView] : ["month", "week", "day", "sprint"],
|
|
9657
9774
|
[onlyView]
|
|
9658
9775
|
);
|
|
9659
9776
|
const isControlledView = view !== void 0;
|
|
9660
9777
|
const [internalView, setInternalView] = React32.useState(() => onlyView ?? defaultView);
|
|
9661
9778
|
const activeView = onlyView ? onlyView : isControlledView ? view : internalView;
|
|
9779
|
+
const effectiveSlotMinWidth = React32.useMemo(() => {
|
|
9780
|
+
if (slotMinWidth == null) {
|
|
9781
|
+
if (activeView === "month" && monthEventStyle === "compact") {
|
|
9782
|
+
return clamp4(Math.round(sizeConfig.slotMinWidth * 0.55), 32, sizeConfig.slotMinWidth);
|
|
9783
|
+
}
|
|
9784
|
+
if (activeView === "sprint") {
|
|
9785
|
+
return clamp4(Math.round(sizeConfig.slotMinWidth * 0.4), 18, 48);
|
|
9786
|
+
}
|
|
9787
|
+
}
|
|
9788
|
+
return baseSlotMinWidth;
|
|
9789
|
+
}, [activeView, baseSlotMinWidth, monthEventStyle, sizeConfig.slotMinWidth, slotMinWidth]);
|
|
9662
9790
|
const isControlledDate = date !== void 0;
|
|
9663
9791
|
const [internalDate, setInternalDate] = React32.useState(() => defaultDate ?? /* @__PURE__ */ new Date());
|
|
9664
9792
|
const activeDate = isControlledDate ? date : internalDate;
|
|
@@ -9671,6 +9799,7 @@ function CalendarTimeline({
|
|
|
9671
9799
|
month: labels?.month ?? t("month"),
|
|
9672
9800
|
week: labels?.week ?? t("week"),
|
|
9673
9801
|
day: labels?.day ?? t("day"),
|
|
9802
|
+
sprint: labels?.sprint ?? t("sprint"),
|
|
9674
9803
|
newEvent: labels?.newEvent ?? t("newEvent"),
|
|
9675
9804
|
createEventTitle: labels?.createEventTitle ?? t("createEventTitle"),
|
|
9676
9805
|
create: labels?.create ?? t("create"),
|
|
@@ -9711,6 +9840,10 @@ function CalendarTimeline({
|
|
|
9711
9840
|
setDate(addZonedDays(base, dir * 7, resolvedTimeZone));
|
|
9712
9841
|
return;
|
|
9713
9842
|
}
|
|
9843
|
+
if (activeView === "sprint") {
|
|
9844
|
+
setDate(addZonedYears(base, dir, resolvedTimeZone));
|
|
9845
|
+
return;
|
|
9846
|
+
}
|
|
9714
9847
|
setDate(addZonedDays(base, dir, resolvedTimeZone));
|
|
9715
9848
|
},
|
|
9716
9849
|
[activeDate, activeView, resolvedTimeZone, setDate]
|
|
@@ -9736,7 +9869,8 @@ function CalendarTimeline({
|
|
|
9736
9869
|
dayRangeMode,
|
|
9737
9870
|
workHours,
|
|
9738
9871
|
resolvedNow,
|
|
9739
|
-
formatters
|
|
9872
|
+
formatters,
|
|
9873
|
+
dueDateSprint
|
|
9740
9874
|
});
|
|
9741
9875
|
React32.useEffect(() => {
|
|
9742
9876
|
onRangeChange?.(range);
|
|
@@ -9982,9 +10116,15 @@ function CalendarTimeline({
|
|
|
9982
10116
|
const rangeText = ya === yb ? `${a} \u2013 ${b}, ${ya}` : `${a}, ${ya} \u2013 ${b}, ${yb}`;
|
|
9983
10117
|
return `${l.week} ${week} \u2022 ${rangeText}`;
|
|
9984
10118
|
}
|
|
10119
|
+
if (activeView === "sprint") {
|
|
10120
|
+
const fmtYear = getDtf(resolvedLocale, resolvedTimeZone, { year: "numeric" });
|
|
10121
|
+
const yearText = fmtYear.format(activeDate);
|
|
10122
|
+
const count = Math.max(0, slots.length);
|
|
10123
|
+
return `${l.sprint} \u2022 ${yearText} \u2022 ${t("sprints", { n: count })}`;
|
|
10124
|
+
}
|
|
9985
10125
|
const fmt = getDtf(resolvedLocale, resolvedTimeZone, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
|
|
9986
10126
|
return fmt.format(range.start);
|
|
9987
|
-
}, [activeDate, activeView, formatters, l.week, range.end, range.start, resolvedLocale, resolvedTimeZone]);
|
|
10127
|
+
}, [activeDate, activeView, formatters, l.sprint, l.week, range.end, range.start, resolvedLocale, resolvedTimeZone, slots.length, t]);
|
|
9988
10128
|
const createMode = interactions?.createMode ?? "drag";
|
|
9989
10129
|
const canCreate = !isViewOnly && (interactions?.creatable ?? false) && !!onCreateEvent;
|
|
9990
10130
|
const [createOpen, setCreateOpen] = React32.useState(false);
|
|
@@ -9999,11 +10139,54 @@ function CalendarTimeline({
|
|
|
9999
10139
|
disabled: r.disabled ?? false
|
|
10000
10140
|
}));
|
|
10001
10141
|
}, [resources]);
|
|
10002
|
-
const
|
|
10142
|
+
const formatCreateBoundaryLabel = React32.useMemo(() => {
|
|
10003
10143
|
const timeFmt = getDtf(resolvedLocale, resolvedTimeZone, { hour: "2-digit", minute: "2-digit", hourCycle: "h23" });
|
|
10004
10144
|
const dayFmt = getDtf(resolvedLocale, resolvedTimeZone, { weekday: "short", month: "short", day: "numeric" });
|
|
10005
|
-
|
|
10006
|
-
|
|
10145
|
+
const yearFmt = getDtf(resolvedLocale, resolvedTimeZone, { year: "numeric" });
|
|
10146
|
+
const sprintDefs = (() => {
|
|
10147
|
+
if (!dueDateSprint) return [];
|
|
10148
|
+
const out = [];
|
|
10149
|
+
for (const v of Object.values(dueDateSprint)) {
|
|
10150
|
+
const startInput = v.range_date?.start;
|
|
10151
|
+
const endInput = v.range_date?.end;
|
|
10152
|
+
if (startInput == null || endInput == null) continue;
|
|
10153
|
+
const startRaw = toDate2(startInput);
|
|
10154
|
+
const endRaw = toDate2(endInput);
|
|
10155
|
+
if (Number.isNaN(startRaw.getTime()) || Number.isNaN(endRaw.getTime())) continue;
|
|
10156
|
+
const startMs = startOfZonedDay(startRaw, resolvedTimeZone).getTime();
|
|
10157
|
+
const endMs = startOfZonedDay(endRaw, resolvedTimeZone).getTime();
|
|
10158
|
+
if (endMs <= startMs) continue;
|
|
10159
|
+
out.push({ startMs, endMs, title: v.title });
|
|
10160
|
+
}
|
|
10161
|
+
out.sort((a, b) => a.startMs - b.startMs);
|
|
10162
|
+
return out;
|
|
10163
|
+
})();
|
|
10164
|
+
const sprintTitleForStart = (slotStart, idx) => {
|
|
10165
|
+
if (activeView !== "sprint") return null;
|
|
10166
|
+
if (sprintDefs.length === 0) return null;
|
|
10167
|
+
const sMs = startOfZonedDay(slotStart, resolvedTimeZone).getTime();
|
|
10168
|
+
const match = sprintDefs.find((d) => sMs >= d.startMs && sMs < d.endMs) ?? sprintDefs[idx] ?? null;
|
|
10169
|
+
if (!match) return null;
|
|
10170
|
+
return typeof match.title === "string" || typeof match.title === "number" ? String(match.title) : null;
|
|
10171
|
+
};
|
|
10172
|
+
return (d, opts) => {
|
|
10173
|
+
if (activeView === "day") return timeFmt.format(d);
|
|
10174
|
+
if (activeView === "sprint") {
|
|
10175
|
+
const y = yearFmt.format(d);
|
|
10176
|
+
const idx = opts?.boundaryIdx;
|
|
10177
|
+
if (typeof idx === "number") {
|
|
10178
|
+
const slotStart = slotStarts[clamp4(idx, 0, Math.max(0, slotStarts.length - 1))] ?? d;
|
|
10179
|
+
const dynamicTitle = sprintTitleForStart(slotStart, idx);
|
|
10180
|
+
if (dynamicTitle) return `${dynamicTitle} \u2022 ${dayFmt.format(d)}, ${y}`;
|
|
10181
|
+
const sprintNumber = opts?.kind === "start" ? idx + 1 : Math.min(idx + 1, Math.max(1, slotStarts.length));
|
|
10182
|
+
const sprintText = `${l.sprint} ${String(sprintNumber).padStart(2, "0")}`;
|
|
10183
|
+
const dateText = dayFmt.format(d);
|
|
10184
|
+
return `${sprintText} \u2022 ${dateText}, ${y}`;
|
|
10185
|
+
}
|
|
10186
|
+
}
|
|
10187
|
+
return dayFmt.format(d);
|
|
10188
|
+
};
|
|
10189
|
+
}, [activeView, dueDateSprint, l.sprint, resolvedLocale, resolvedTimeZone, slotStarts, slotStarts.length]);
|
|
10007
10190
|
const openCreate = React32.useCallback(() => {
|
|
10008
10191
|
if (!canCreate) return;
|
|
10009
10192
|
if (activeEventSheetOpen) setEventSheetOpen(false);
|
|
@@ -10040,16 +10223,16 @@ function CalendarTimeline({
|
|
|
10040
10223
|
setCreateEndIdx((prev) => Math.min(slots.length, Math.max(prev, createStartIdx + 1)));
|
|
10041
10224
|
}, [createStartIdx, slots.length]);
|
|
10042
10225
|
const createStartOptions = React32.useMemo(() => {
|
|
10043
|
-
return slotStarts.map((d, idx) => ({ label:
|
|
10044
|
-
}, [
|
|
10226
|
+
return slotStarts.map((d, idx) => ({ label: formatCreateBoundaryLabel(d, { kind: "start", boundaryIdx: idx }), value: idx }));
|
|
10227
|
+
}, [formatCreateBoundaryLabel, slotStarts]);
|
|
10045
10228
|
const createEndOptions = React32.useMemo(() => {
|
|
10046
10229
|
const out = [];
|
|
10047
10230
|
for (let idx = createStartIdx + 1; idx <= slotStarts.length; idx++) {
|
|
10048
10231
|
const boundary = idx >= slotStarts.length ? range.end : slotStarts[idx];
|
|
10049
|
-
out.push({ label:
|
|
10232
|
+
out.push({ label: formatCreateBoundaryLabel(boundary, { kind: "end", boundaryIdx: idx }), value: idx });
|
|
10050
10233
|
}
|
|
10051
10234
|
return out;
|
|
10052
|
-
}, [createStartIdx, range.end,
|
|
10235
|
+
}, [createStartIdx, formatCreateBoundaryLabel, range.end, slotStarts]);
|
|
10053
10236
|
const commitCreate = React32.useCallback(() => {
|
|
10054
10237
|
if (!onCreateEvent) return;
|
|
10055
10238
|
if (!createResourceId) return;
|
|
@@ -10103,6 +10286,9 @@ function CalendarTimeline({
|
|
|
10103
10286
|
const stepMs = Math.trunc(Math.max(5, Math.min(240, Math.trunc(dayTimeStepMinutes))) * 6e4);
|
|
10104
10287
|
return { start, end: new Date(start.getTime() + stepMs) };
|
|
10105
10288
|
}
|
|
10289
|
+
if (activeView === "sprint") {
|
|
10290
|
+
return { start, end: addZonedDays(start, 7, resolvedTimeZone) };
|
|
10291
|
+
}
|
|
10106
10292
|
return { start, end: addZonedDays(start, 1, resolvedTimeZone) };
|
|
10107
10293
|
},
|
|
10108
10294
|
[activeView, dayTimeStepMinutes, resolvedTimeZone, slotStarts]
|
|
@@ -10385,7 +10571,7 @@ function CalendarTimeline({
|
|
|
10385
10571
|
className: cn(
|
|
10386
10572
|
sizeConfig.slotHeaderClass,
|
|
10387
10573
|
activeView !== "day" && s.isToday && "bg-primary/8 border-l-primary/40",
|
|
10388
|
-
activeView !== "day" && !s.isToday && s.isWeekend && "bg-
|
|
10574
|
+
activeView !== "day" && !s.isToday && s.isWeekend && "bg-muted/25"
|
|
10389
10575
|
),
|
|
10390
10576
|
dayHeaderMarks
|
|
10391
10577
|
},
|
|
@@ -10416,7 +10602,7 @@ function CalendarTimeline({
|
|
|
10416
10602
|
{
|
|
10417
10603
|
title,
|
|
10418
10604
|
resourcesHeaderLabel: t("resourcesHeader"),
|
|
10419
|
-
labels: { today: l.today, prev: l.prev, next: l.next, month: l.month, week: l.week, day: l.day },
|
|
10605
|
+
labels: { today: l.today, prev: l.prev, next: l.next, month: l.month, week: l.week, day: l.day, sprint: l.sprint },
|
|
10420
10606
|
newEventLabel: l.newEvent,
|
|
10421
10607
|
newEventDisabled: isViewOnly || !canCreate || resources.length === 0,
|
|
10422
10608
|
onNewEventClick: isViewOnly ? void 0 : openCreate,
|
|
@@ -10451,8 +10637,9 @@ function CalendarTimeline({
|
|
|
10451
10637
|
"div",
|
|
10452
10638
|
{
|
|
10453
10639
|
className: cn(
|
|
10454
|
-
"
|
|
10455
|
-
"shadow-sm hover:shadow-md
|
|
10640
|
+
"rounded-2xl md:rounded-3xl overflow-hidden bg-card text-card-foreground backdrop-blur-sm",
|
|
10641
|
+
"border border-border shadow-sm md:hover:shadow-md",
|
|
10642
|
+
"transition-[transform,box-shadow,border-color,background-color] duration-300 ease-soft",
|
|
10456
10643
|
densityClass,
|
|
10457
10644
|
className
|
|
10458
10645
|
),
|
|
@@ -10585,12 +10772,19 @@ function CalendarTimeline({
|
|
|
10585
10772
|
})();
|
|
10586
10773
|
const resource = resourceById.get(ev.resourceId);
|
|
10587
10774
|
const tooltipTitle = ev.title || ev.id;
|
|
10588
|
-
const shouldCompact = activeView === "day" && dayEventStyle === "compact";
|
|
10775
|
+
const shouldCompact = activeView === "day" && dayEventStyle === "compact" || activeView === "month" && monthEventStyle === "compact";
|
|
10589
10776
|
const eventInsetX = 2;
|
|
10590
10777
|
const leftInset = left + eventInsetX;
|
|
10591
10778
|
const widthInset = Math.max(1, width - eventInsetX * 2);
|
|
10592
|
-
const defaultMaxVisual =
|
|
10593
|
-
|
|
10779
|
+
const defaultMaxVisual = (() => {
|
|
10780
|
+
if (activeView === "month") return clamp4(Math.round(fixedSlotWidth * 2.5), 72, 360);
|
|
10781
|
+
return clamp4(Math.round(fixedSlotWidth * 1.2), 160, 360);
|
|
10782
|
+
})();
|
|
10783
|
+
const maxVisual = clamp4(
|
|
10784
|
+
Math.round(activeView === "month" ? monthEventMaxWidth ?? defaultMaxVisual : dayEventMaxWidth ?? defaultMaxVisual),
|
|
10785
|
+
48,
|
|
10786
|
+
1200
|
|
10787
|
+
);
|
|
10594
10788
|
const visualWidth = shouldCompact ? Math.min(widthInset, maxVisual) : widthInset;
|
|
10595
10789
|
const isClipped = shouldCompact && widthInset > visualWidth + 1;
|
|
10596
10790
|
const block = /* @__PURE__ */ jsxs33(
|
|
@@ -10635,7 +10829,7 @@ function CalendarTimeline({
|
|
|
10635
10829
|
"transition-all duration-150 ease-out",
|
|
10636
10830
|
"backdrop-blur-sm",
|
|
10637
10831
|
ev.className,
|
|
10638
|
-
isPreview && "ring-2 ring-primary/50 ring-offset-1 ring-offset-
|
|
10832
|
+
isPreview && "ring-2 ring-primary/50 ring-offset-1 ring-offset-card scale-[1.02]"
|
|
10639
10833
|
),
|
|
10640
10834
|
style: {
|
|
10641
10835
|
width: visualWidth,
|
|
@@ -10647,7 +10841,7 @@ function CalendarTimeline({
|
|
|
10647
10841
|
},
|
|
10648
10842
|
children: [
|
|
10649
10843
|
node,
|
|
10650
|
-
isClipped ? /* @__PURE__ */ jsx38("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-10 bg-linear-to-l from-
|
|
10844
|
+
isClipped ? /* @__PURE__ */ jsx38("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-10 bg-linear-to-l from-card/50 to-transparent flex items-center justify-end pr-2", children: /* @__PURE__ */ jsx38("span", { className: "text-xs text-muted-foreground/80", children: "\u2026" }) }) : null
|
|
10651
10845
|
]
|
|
10652
10846
|
}
|
|
10653
10847
|
),
|
|
@@ -10703,7 +10897,7 @@ function CalendarTimeline({
|
|
|
10703
10897
|
className: cn(
|
|
10704
10898
|
"pointer-events-none absolute z-20",
|
|
10705
10899
|
"h-5 w-5 rounded-full",
|
|
10706
|
-
"bg-
|
|
10900
|
+
"bg-card/80 backdrop-blur-sm",
|
|
10707
10901
|
"border border-border/60 shadow-xs",
|
|
10708
10902
|
"flex items-center justify-center"
|
|
10709
10903
|
),
|