@stll/ui 0.26.0 → 0.26.2
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/calendar/resource-calendar.logic.js +11 -12
- package/dist/components/button-variants.d.ts +2 -2
- package/dist/components/date-picker-popover.js +64 -64
- package/dist/components/date-picker-popover.logic.d.ts +5 -1
- package/dist/components/date-picker-popover.logic.js +16 -11
- package/dist/components/outline-rail.js +3 -2
- package/dist/inspector/chrome.js +1 -1
- package/dist/inspector/dock.js +13 -3
- package/dist/lib/week.d.ts +2 -2
- package/dist/lib/week.js +2 -2
- package/dist/review/review-comment-card.js +2 -1
- package/package.json +4 -3
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
import { Result } from "better-result";
|
|
2
|
+
import { Temporal } from "temporal-polyfill/full";
|
|
1
3
|
//#region src/calendar/resource-calendar.logic.ts
|
|
2
4
|
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
3
|
-
const
|
|
4
|
-
const toUTCDate = (value) => {
|
|
5
|
+
const toPlainDate = (value) => {
|
|
5
6
|
if (!ISO_DATE_PATTERN.test(value)) return null;
|
|
6
|
-
|
|
7
|
-
return date.toISOString().slice(0, 10) === value ? date : null;
|
|
7
|
+
return Result.try(() => Temporal.PlainDate.from(value)).unwrapOr(null);
|
|
8
8
|
};
|
|
9
9
|
const differenceInCalendarDays = (later, earlier) => {
|
|
10
|
-
const laterDate =
|
|
11
|
-
const earlierDate =
|
|
10
|
+
const laterDate = toPlainDate(later);
|
|
11
|
+
const earlierDate = toPlainDate(earlier);
|
|
12
12
|
if (laterDate === null || earlierDate === null) throw new RangeError("Calendar dates must use normalized YYYY-MM-DD values");
|
|
13
|
-
return
|
|
13
|
+
return laterDate.since(earlierDate, { largestUnit: "days" }).days;
|
|
14
14
|
};
|
|
15
15
|
const getResourceCalendarPlacement = ({ entry, visibleRange }) => {
|
|
16
16
|
const visibleDayCount = differenceInCalendarDays(visibleRange.endDateExclusive, visibleRange.startDate);
|
|
@@ -27,7 +27,7 @@ const getResourceCalendarPlacement = ({ entry, visibleRange }) => {
|
|
|
27
27
|
const assertConsecutiveCalendarDates = (dates) => {
|
|
28
28
|
if (dates.length === 0) throw new RangeError("A resource calendar needs at least one date column");
|
|
29
29
|
const first = dates.at(0);
|
|
30
|
-
if (first === void 0 ||
|
|
30
|
+
if (first === void 0 || toPlainDate(first) === null) throw new RangeError("Resource calendar date columns must be consecutive normalized dates");
|
|
31
31
|
for (let index = 1; index < dates.length; index += 1) {
|
|
32
32
|
const previous = dates.at(index - 1);
|
|
33
33
|
const current = dates.at(index);
|
|
@@ -35,11 +35,10 @@ const assertConsecutiveCalendarDates = (dates) => {
|
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
37
|
const nextCalendarDate = (value) => {
|
|
38
|
-
const date =
|
|
38
|
+
const date = toPlainDate(value);
|
|
39
39
|
if (date === null) throw new RangeError("Calendar dates must use normalized YYYY-MM-DD values");
|
|
40
|
-
date.
|
|
41
|
-
|
|
42
|
-
if (toUTCDate(nextDate) === null) throw new RangeError("Calendar dates must have a following normalized YYYY-MM-DD value");
|
|
40
|
+
const nextDate = date.add({ days: 1 }).toString();
|
|
41
|
+
if (toPlainDate(nextDate) === null) throw new RangeError("Calendar dates must have a following normalized YYYY-MM-DD value");
|
|
43
42
|
return nextDate;
|
|
44
43
|
};
|
|
45
44
|
const layoutResourceCalendarEntries = (entries, visibleRange) => {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
declare const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
|
|
12
12
|
declare const buttonVariants: (props?: ({
|
|
13
|
-
size?: "default" | "
|
|
14
|
-
variant?: "
|
|
13
|
+
size?: "default" | "xs" | "sm" | "icon" | "chip" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "xl" | null | undefined;
|
|
14
|
+
variant?: "default" | "link" | "destructive" | "outline" | "destructive-outline" | "ghost" | "secondary" | null | undefined;
|
|
15
15
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
16
16
|
//#endregion
|
|
17
17
|
export { buttonAccessibleDisabledClass, buttonVariants };
|
|
@@ -3,17 +3,22 @@ import { cn } from "../lib/utils.js";
|
|
|
3
3
|
import { Button } from "./button.js";
|
|
4
4
|
import { DirectionalIcon } from "./directional-icon.js";
|
|
5
5
|
import { getLocaleWeekInfo, getWeekendDays } from "../lib/week.js";
|
|
6
|
-
import { localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth } from "./date-picker-popover.logic.js";
|
|
6
|
+
import { localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth, shiftCalendarDate } from "./date-picker-popover.logic.js";
|
|
7
7
|
import { Popover, PopoverContent as PopoverPopup, PopoverTrigger } from "./popover.js";
|
|
8
8
|
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { useCallback, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
11
|
+
import { Temporal } from "temporal-polyfill/full";
|
|
11
12
|
//#region src/components/date-picker-popover.tsx
|
|
12
|
-
const toISODate = (date) => date.
|
|
13
|
+
const toISODate = (date) => date.toString();
|
|
14
|
+
const toUTCDateTime = (date) => date.toZonedDateTime({
|
|
15
|
+
plainTime: Temporal.PlainTime.from("00:00"),
|
|
16
|
+
timeZone: "UTC"
|
|
17
|
+
}).epochMilliseconds;
|
|
13
18
|
const HYDRATION_DATE = "1970-01-01";
|
|
14
19
|
const HYDRATION_LOCALE = "en";
|
|
15
20
|
const noopSubscribe = (_onStoreChange) => () => void 0;
|
|
16
|
-
const getLocalToday = () => localDateFromTimestamp(
|
|
21
|
+
const getLocalToday = () => localDateFromTimestamp(Temporal.Now.instant().epochMilliseconds);
|
|
17
22
|
const localDateListeners = /* @__PURE__ */ new Set();
|
|
18
23
|
let localDateTimeoutId;
|
|
19
24
|
const notifyLocalDateListeners = () => {
|
|
@@ -24,7 +29,7 @@ const scheduleNextLocalDate = () => {
|
|
|
24
29
|
localDateTimeoutId = setTimeout(() => {
|
|
25
30
|
notifyLocalDateListeners();
|
|
26
31
|
if (localDateListeners.size > 0) scheduleNextLocalDate();
|
|
27
|
-
}, millisecondsUntilNextLocalDate(
|
|
32
|
+
}, millisecondsUntilNextLocalDate(Temporal.Now.instant().epochMilliseconds));
|
|
28
33
|
};
|
|
29
34
|
const refreshLocalDateEnvironment = () => {
|
|
30
35
|
notifyLocalDateListeners();
|
|
@@ -58,19 +63,21 @@ const getFirstDayOfWeek = (locale) => {
|
|
|
58
63
|
};
|
|
59
64
|
const getMonthDays = (year, month, firstDow, weekendDays, today) => {
|
|
60
65
|
const days = [];
|
|
61
|
-
const first =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
const first = Temporal.PlainDate.from({
|
|
67
|
+
year,
|
|
68
|
+
month: month + 1,
|
|
69
|
+
day: 1
|
|
70
|
+
});
|
|
71
|
+
const startOffset = (first.dayOfWeek - 1 - firstDow + 7) % 7;
|
|
72
|
+
const start = first.subtract({ days: startOffset });
|
|
65
73
|
for (let i = 0; i < 42; i++) {
|
|
66
|
-
const d =
|
|
67
|
-
d.setUTCDate(d.getUTCDate() + i);
|
|
74
|
+
const d = start.add({ days: i });
|
|
68
75
|
const iso = toISODate(d);
|
|
69
76
|
days.push({
|
|
70
77
|
date: iso,
|
|
71
|
-
isCurrentMonth: d.
|
|
78
|
+
isCurrentMonth: d.month === month + 1,
|
|
72
79
|
isToday: iso === today,
|
|
73
|
-
isWeekend: weekendDays.has(d.
|
|
80
|
+
isWeekend: weekendDays.has(d.dayOfWeek % 7)
|
|
74
81
|
});
|
|
75
82
|
}
|
|
76
83
|
return days;
|
|
@@ -87,14 +94,21 @@ const getWeekdayFormatter = (locale) => {
|
|
|
87
94
|
const getWeekdayLabels = (locale, firstDow, weekendDays) => {
|
|
88
95
|
const fmt = getWeekdayFormatter(locale);
|
|
89
96
|
return Array.from({ length: 7 }, (_, i) => {
|
|
90
|
-
const d =
|
|
97
|
+
const d = Temporal.PlainDate.from("2024-01-01").add({ days: (i + firstDow) % 7 });
|
|
91
98
|
return {
|
|
92
|
-
isWeekend: weekendDays.has(d.
|
|
93
|
-
label: fmt.format(d)
|
|
99
|
+
isWeekend: weekendDays.has(d.dayOfWeek % 7),
|
|
100
|
+
label: fmt.format(toUTCDateTime(d))
|
|
94
101
|
};
|
|
95
102
|
});
|
|
96
103
|
};
|
|
97
104
|
const monthFormatters = /* @__PURE__ */ new Map();
|
|
105
|
+
const dateFormatters = /* @__PURE__ */ new Map();
|
|
106
|
+
const getDateFormatter = (locale, options) => {
|
|
107
|
+
const key = `${locale}:${JSON.stringify(options)}`;
|
|
108
|
+
const formatter = dateFormatters.get(key) ?? new Intl.DateTimeFormat(locale, options);
|
|
109
|
+
dateFormatters.set(key, formatter);
|
|
110
|
+
return formatter;
|
|
111
|
+
};
|
|
98
112
|
const getMonthFormatter = (locale, format) => {
|
|
99
113
|
const key = `${locale}:${format}`;
|
|
100
114
|
const fmt = monthFormatters.get(key) ?? new Intl.DateTimeFormat(locale, {
|
|
@@ -107,7 +121,11 @@ const getMonthFormatter = (locale, format) => {
|
|
|
107
121
|
};
|
|
108
122
|
const getMonthLabels = (locale, format = "long") => {
|
|
109
123
|
const fmt = getMonthFormatter(locale, format);
|
|
110
|
-
return Array.from({ length: 12 }, (_, i) => fmt.format(
|
|
124
|
+
return Array.from({ length: 12 }, (_, i) => fmt.format(toUTCDateTime(Temporal.PlainDate.from({
|
|
125
|
+
year: 2024,
|
|
126
|
+
month: i + 1,
|
|
127
|
+
day: 1
|
|
128
|
+
}))));
|
|
111
129
|
};
|
|
112
130
|
const monthYearFormatters = /* @__PURE__ */ new Map();
|
|
113
131
|
const getMonthYearFormatter = (locale) => {
|
|
@@ -120,7 +138,11 @@ const getMonthYearFormatter = (locale) => {
|
|
|
120
138
|
monthYearFormatters.set(locale, fmt);
|
|
121
139
|
return fmt;
|
|
122
140
|
};
|
|
123
|
-
const formatMonthYear = (locale, year, month) => getMonthYearFormatter(locale).format(
|
|
141
|
+
const formatMonthYear = (locale, year, month) => getMonthYearFormatter(locale).format(toUTCDateTime(Temporal.PlainDate.from({
|
|
142
|
+
year,
|
|
143
|
+
month: month + 1,
|
|
144
|
+
day: 1
|
|
145
|
+
})));
|
|
124
146
|
const relativeTimeFormatters = /* @__PURE__ */ new Map();
|
|
125
147
|
const getRelativeTimeFormatter = (locale) => {
|
|
126
148
|
const fmt = relativeTimeFormatters.get(locale) ?? new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
|
@@ -134,14 +156,10 @@ const deriveTodayLabel = (locale) => {
|
|
|
134
156
|
};
|
|
135
157
|
const normalizeDate = (v) => {
|
|
136
158
|
if (v === null || v === void 0) return "";
|
|
137
|
-
if (v instanceof Date) return v.
|
|
159
|
+
if (v instanceof Date) return Temporal.Instant.fromEpochMilliseconds(v.getTime()).toZonedDateTimeISO("UTC").toPlainDate().toString();
|
|
138
160
|
return v.length >= 10 ? v.slice(0, 10) : v;
|
|
139
161
|
};
|
|
140
|
-
const addDays = (iso, n) => {
|
|
141
|
-
const d = /* @__PURE__ */ new Date(`${iso}T00:00:00Z`);
|
|
142
|
-
d.setUTCDate(d.getUTCDate() + n);
|
|
143
|
-
return toISODate(d);
|
|
144
|
-
};
|
|
162
|
+
const addDays = (iso, n) => Temporal.PlainDate.from(iso).add({ days: n }).toString();
|
|
145
163
|
const isBefore = (a, b) => a < b;
|
|
146
164
|
const isAfter = (a, b) => a > b;
|
|
147
165
|
/** Round down to the start of a decade (e.g. 2026 → 2020). */
|
|
@@ -186,22 +204,22 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
186
204
|
maxDate,
|
|
187
205
|
isDateDisabled
|
|
188
206
|
]);
|
|
189
|
-
const displayLabel = value ? (
|
|
207
|
+
const displayLabel = value ? getDateFormatter(locale, {
|
|
190
208
|
month: "short",
|
|
191
209
|
day: "numeric",
|
|
192
210
|
year: "numeric",
|
|
193
211
|
calendar: "gregory",
|
|
194
212
|
timeZone: "UTC"
|
|
195
|
-
}) : placeholderLabel ?? "—";
|
|
196
|
-
const formatDayLabel = useCallback((iso) => (
|
|
213
|
+
}).format(toUTCDateTime(Temporal.PlainDate.from(value))) : placeholderLabel ?? "—";
|
|
214
|
+
const formatDayLabel = useCallback((iso) => getDateFormatter(locale, {
|
|
197
215
|
weekday: "long",
|
|
198
216
|
month: "long",
|
|
199
217
|
day: "numeric",
|
|
200
218
|
year: "numeric",
|
|
201
219
|
calendar: "gregory",
|
|
202
220
|
timeZone: "UTC"
|
|
203
|
-
}), [locale]);
|
|
204
|
-
const handleGridKeyDown =
|
|
221
|
+
}).format(toUTCDateTime(Temporal.PlainDate.from(iso))), [locale]);
|
|
222
|
+
const handleGridKeyDown = (e) => {
|
|
205
223
|
const firstDay = days.at(0);
|
|
206
224
|
if (!firstDay) return;
|
|
207
225
|
const current = focusedDate || value || firstDay.date;
|
|
@@ -212,22 +230,14 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
212
230
|
else if (e.key === "ArrowDown") next = addDays(current, 7);
|
|
213
231
|
else if (e.key === "ArrowUp") next = addDays(current, -7);
|
|
214
232
|
else if (e.key === "Home") {
|
|
215
|
-
const offset = ((
|
|
233
|
+
const offset = (Temporal.PlainDate.from(current).dayOfWeek - 1 - firstDow + 7) % 7;
|
|
216
234
|
next = addDays(current, -offset);
|
|
217
235
|
} else if (e.key === "End") {
|
|
218
|
-
const offset = ((
|
|
236
|
+
const offset = (Temporal.PlainDate.from(current).dayOfWeek - 1 - firstDow + 7) % 7;
|
|
219
237
|
next = addDays(current, 6 - offset);
|
|
220
|
-
} else if (e.key === "PageUp") {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
else d.setUTCMonth(d.getUTCMonth() - 1);
|
|
224
|
-
next = toISODate(d);
|
|
225
|
-
} else if (e.key === "PageDown") {
|
|
226
|
-
const d = /* @__PURE__ */ new Date(`${current}T00:00:00Z`);
|
|
227
|
-
if (e.shiftKey) d.setUTCFullYear(d.getUTCFullYear() + 1);
|
|
228
|
-
else d.setUTCMonth(d.getUTCMonth() + 1);
|
|
229
|
-
next = toISODate(d);
|
|
230
|
-
} else {
|
|
238
|
+
} else if (e.key === "PageUp") next = shiftCalendarDate(current, e.shiftKey ? { years: -1 } : { months: -1 });
|
|
239
|
+
else if (e.key === "PageDown") next = shiftCalendarDate(current, e.shiftKey ? { years: 1 } : { months: 1 });
|
|
240
|
+
else {
|
|
231
241
|
if (e.key === "Enter" || e.key === " ") {
|
|
232
242
|
e.preventDefault();
|
|
233
243
|
if (!isDayDisabled(current)) onChange(current);
|
|
@@ -237,9 +247,9 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
237
247
|
e.preventDefault();
|
|
238
248
|
if (next) {
|
|
239
249
|
setFocusedDate(next);
|
|
240
|
-
const nextDate =
|
|
241
|
-
const nextMonth = nextDate.
|
|
242
|
-
const nextYear = nextDate.
|
|
250
|
+
const nextDate = Temporal.PlainDate.from(next);
|
|
251
|
+
const nextMonth = nextDate.month - 1;
|
|
252
|
+
const nextYear = nextDate.year;
|
|
243
253
|
if (nextMonth !== viewMonth || nextYear !== viewYear) setViewMonthOverride({
|
|
244
254
|
month: nextMonth,
|
|
245
255
|
year: nextYear
|
|
@@ -248,17 +258,7 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
248
258
|
(gridRef.current?.querySelector(`[data-date="${next}"]`))?.focus();
|
|
249
259
|
});
|
|
250
260
|
}
|
|
251
|
-
}
|
|
252
|
-
focusedDate,
|
|
253
|
-
value,
|
|
254
|
-
days,
|
|
255
|
-
firstDow,
|
|
256
|
-
viewMonth,
|
|
257
|
-
viewYear,
|
|
258
|
-
isDayDisabled,
|
|
259
|
-
onChange,
|
|
260
|
-
setViewMonthOverride
|
|
261
|
-
]);
|
|
261
|
+
};
|
|
262
262
|
const handlePrev = () => {
|
|
263
263
|
if (view === "days") if (viewMonth === 0) setViewMonthOverride({
|
|
264
264
|
month: 11,
|
|
@@ -316,8 +316,8 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
316
316
|
setDecadeBaseOverride(decadeStart(year));
|
|
317
317
|
setView("months");
|
|
318
318
|
};
|
|
319
|
-
const selectedYear = value ? (
|
|
320
|
-
const selectedMonth = value ? (
|
|
319
|
+
const selectedYear = value ? Temporal.PlainDate.from(value).year : null;
|
|
320
|
+
const selectedMonth = value ? Temporal.PlainDate.from(value).month - 1 : null;
|
|
321
321
|
const handleOpenChange = (open) => {
|
|
322
322
|
onOpenChange?.(open);
|
|
323
323
|
if (!open) {
|
|
@@ -451,10 +451,10 @@ const DatePickerPopoverContent = ({ id, labelledBy, value: rawValue, onChange, l
|
|
|
451
451
|
children: [/* @__PURE__ */ jsx(Button, {
|
|
452
452
|
className: "flex-1",
|
|
453
453
|
onClick: () => {
|
|
454
|
-
const todayDate =
|
|
454
|
+
const todayDate = Temporal.PlainDate.from(today);
|
|
455
455
|
setViewMonthOverride({
|
|
456
|
-
month: todayDate.
|
|
457
|
-
year: todayDate.
|
|
456
|
+
month: todayDate.month - 1,
|
|
457
|
+
year: todayDate.year
|
|
458
458
|
});
|
|
459
459
|
setView("days");
|
|
460
460
|
},
|
|
@@ -487,9 +487,9 @@ const DatePickerPopover = (props) => {
|
|
|
487
487
|
const MONTHS_PER_ROW = 3;
|
|
488
488
|
const MonthGrid = ({ locale, viewYear, currentMonth, currentYear, onSelect, today }) => {
|
|
489
489
|
const labels = useMemo(() => getMonthLabels(locale, "short"), [locale]);
|
|
490
|
-
const now =
|
|
491
|
-
const todayMonth = now.
|
|
492
|
-
const todayYear = now.
|
|
490
|
+
const now = Temporal.PlainDate.from(today);
|
|
491
|
+
const todayMonth = now.month - 1;
|
|
492
|
+
const todayYear = now.year;
|
|
493
493
|
const rows = [];
|
|
494
494
|
for (let r = 0; r < 12; r += MONTHS_PER_ROW) rows.push(Array.from({ length: MONTHS_PER_ROW }, (_, c) => r + c));
|
|
495
495
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -518,7 +518,7 @@ const MonthGrid = ({ locale, viewYear, currentMonth, currentYear, onSelect, toda
|
|
|
518
518
|
};
|
|
519
519
|
const YEARS_PER_ROW = 3;
|
|
520
520
|
const YearGrid = ({ decadeBase, currentYear, onSelect, today }) => {
|
|
521
|
-
const todayYear = (
|
|
521
|
+
const todayYear = Temporal.PlainDate.from(today).year;
|
|
522
522
|
const startYear = decadeBase - 1;
|
|
523
523
|
const rows = [];
|
|
524
524
|
for (let r = 0; r < DECADE_SIZE; r += YEARS_PER_ROW) rows.push(Array.from({ length: Math.min(YEARS_PER_ROW, DECADE_SIZE - r) }, (_, c) => r + c));
|
|
@@ -9,6 +9,10 @@ declare const resolveCalendarViewMonth: ({ override, today, value }: {
|
|
|
9
9
|
today: string;
|
|
10
10
|
value: string;
|
|
11
11
|
}) => CalendarMonth;
|
|
12
|
+
declare const shiftCalendarDate: (date: string, options: {
|
|
13
|
+
months?: number;
|
|
14
|
+
years?: number;
|
|
15
|
+
}) => string;
|
|
12
16
|
declare const millisecondsUntilNextLocalDate: (timestamp: number) => number;
|
|
13
17
|
//#endregion
|
|
14
|
-
export { CalendarMonth, localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth };
|
|
18
|
+
export { CalendarMonth, localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth, shiftCalendarDate };
|
|
@@ -1,26 +1,31 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill/full";
|
|
1
2
|
//#region src/components/date-picker-popover.logic.ts
|
|
2
3
|
const DATE_ROLLOVER_EPSILON_MS = 50;
|
|
3
4
|
const padDatePart = (value) => value.toString().padStart(2, "0");
|
|
4
5
|
const localDateFromTimestamp = (timestamp) => {
|
|
5
|
-
const current =
|
|
6
|
+
const current = Temporal.Instant.fromEpochMilliseconds(timestamp).toZonedDateTimeISO(Temporal.Now.timeZoneId());
|
|
6
7
|
return [
|
|
7
|
-
current.
|
|
8
|
-
padDatePart(current.
|
|
9
|
-
padDatePart(current.
|
|
8
|
+
current.year,
|
|
9
|
+
padDatePart(current.month),
|
|
10
|
+
padDatePart(current.day)
|
|
10
11
|
].join("-");
|
|
11
12
|
};
|
|
12
13
|
const calendarMonthFromDate = (date) => {
|
|
13
|
-
const current =
|
|
14
|
+
const current = Temporal.PlainDate.from(date);
|
|
14
15
|
return {
|
|
15
|
-
month: current.
|
|
16
|
-
year: current.
|
|
16
|
+
month: current.month - 1,
|
|
17
|
+
year: current.year
|
|
17
18
|
};
|
|
18
19
|
};
|
|
19
20
|
const resolveCalendarViewMonth = ({ override, today, value }) => override ?? calendarMonthFromDate(value || today);
|
|
21
|
+
const shiftCalendarDate = (date, options) => Temporal.PlainDate.from(date).add(options).toString();
|
|
20
22
|
const millisecondsUntilNextLocalDate = (timestamp) => {
|
|
21
|
-
const current =
|
|
22
|
-
const nextLocalDate =
|
|
23
|
-
|
|
23
|
+
const current = Temporal.Instant.fromEpochMilliseconds(timestamp).toZonedDateTimeISO(Temporal.Now.timeZoneId());
|
|
24
|
+
const nextLocalDate = current.toPlainDate().add({ days: 1 }).toZonedDateTime({
|
|
25
|
+
plainTime: Temporal.PlainTime.from("00:00"),
|
|
26
|
+
timeZone: current.timeZoneId
|
|
27
|
+
});
|
|
28
|
+
return Math.max(0, nextLocalDate.epochMilliseconds - timestamp) + DATE_ROLLOVER_EPSILON_MS;
|
|
24
29
|
};
|
|
25
30
|
//#endregion
|
|
26
|
-
export { localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth };
|
|
31
|
+
export { localDateFromTimestamp, millisecondsUntilNextLocalDate, resolveCalendarViewMonth, shiftCalendarDate };
|
|
@@ -3,6 +3,7 @@ import { cn } from "../lib/utils.js";
|
|
|
3
3
|
import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { Temporal } from "temporal-polyfill/full";
|
|
6
7
|
//#region src/components/outline-rail.tsx
|
|
7
8
|
/**
|
|
8
9
|
* Outline rail — the shared right-edge navigation rail.
|
|
@@ -153,7 +154,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
|
|
|
153
154
|
if (!container || items.length === 0) return;
|
|
154
155
|
let raf = 0;
|
|
155
156
|
const compute = () => {
|
|
156
|
-
if (
|
|
157
|
+
if (Temporal.Now.instant().epochMilliseconds < manualLockUntil.current || container.scrollHeight <= 0) return;
|
|
157
158
|
const centrePct = (container.scrollTop + container.clientHeight / 2) / container.scrollHeight * 100;
|
|
158
159
|
let next = null;
|
|
159
160
|
for (const item of items) {
|
|
@@ -197,7 +198,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
|
|
|
197
198
|
if (!container) return;
|
|
198
199
|
if (activeId === void 0) {
|
|
199
200
|
setDerivedActive(id);
|
|
200
|
-
manualLockUntil.current =
|
|
201
|
+
manualLockUntil.current = Temporal.Now.instant().epochMilliseconds + 900;
|
|
201
202
|
}
|
|
202
203
|
onJumpRef.current(id, container);
|
|
203
204
|
}, [activeId, scrollContainerRef]);
|
package/dist/inspector/chrome.js
CHANGED
|
@@ -45,7 +45,7 @@ const InspectorRailCell = ({ className, ...props }) => /* @__PURE__ */ jsx("div"
|
|
|
45
45
|
});
|
|
46
46
|
/** Scroll owner for rail tabs and other middle actions. */
|
|
47
47
|
const InspectorRailContent = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
48
|
-
className: cn("
|
|
48
|
+
className: cn("flex min-h-0 flex-1 scrollbar-none flex-col overflow-x-hidden overflow-y-auto overscroll-contain", className),
|
|
49
49
|
"data-slot": "inspector-rail-content",
|
|
50
50
|
...props
|
|
51
51
|
});
|
package/dist/inspector/dock.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import "./pane-width.js";
|
|
3
|
+
import { EllipsisIcon } from "lucide-react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/inspector/dock.tsx
|
|
5
6
|
const InspectorDock = ({ children, className, onResetWidth, rail, resizeHandleLabel, resizeHandleProps, showPaneContent, width }) => {
|
|
@@ -18,14 +19,23 @@ const InspectorDock = ({ children, className, onResetWidth, rail, resizeHandleLa
|
|
|
18
19
|
className: "fixed inset-y-0 end-0 z-10 hidden h-svh md:flex",
|
|
19
20
|
"data-slot": "inspector-dock-pane",
|
|
20
21
|
style: { width: widthPx },
|
|
21
|
-
children: [showPaneContent && /* @__PURE__ */
|
|
22
|
+
children: [showPaneContent && /* @__PURE__ */ jsxs("div", {
|
|
22
23
|
"aria-label": resizeHandleLabel,
|
|
23
|
-
className: "
|
|
24
|
+
className: "group focus-visible:outline-primary absolute inset-y-0 -start-1.5 z-20 flex w-3 cursor-col-resize items-center justify-center focus-visible:outline-2",
|
|
24
25
|
"data-slot": "inspector-resize-handle",
|
|
25
26
|
role: "separator",
|
|
26
27
|
...resizeHandleProps,
|
|
27
28
|
tabIndex: 0,
|
|
28
|
-
onDoubleClick: onResetWidth
|
|
29
|
+
onDoubleClick: onResetWidth,
|
|
30
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
className: "bg-border group-hover:bg-primary group-active:bg-primary absolute inset-y-0 start-1/2 w-px -translate-x-1/2"
|
|
33
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
34
|
+
"aria-hidden": "true",
|
|
35
|
+
className: "bg-background/80 text-foreground-placeholder ring-border/60 group-hover:bg-accent group-hover:text-foreground group-focus-visible:bg-accent group-focus-visible:text-foreground relative flex h-5 w-3 items-center justify-center rounded-full opacity-0 shadow-xs ring-1 backdrop-blur-sm transition-[color,background-color,opacity] group-hover:opacity-100 group-focus-visible:opacity-100 group-active:opacity-100",
|
|
36
|
+
"data-slot": "inspector-resize-grip",
|
|
37
|
+
children: /* @__PURE__ */ jsx(EllipsisIcon, { className: "size-3 rotate-90" })
|
|
38
|
+
})]
|
|
29
39
|
}), showPaneContent || rail === void 0 ? /* @__PURE__ */ jsxs("div", {
|
|
30
40
|
className: "bg-sidebar flex h-full w-full flex-row",
|
|
31
41
|
children: [rail, /* @__PURE__ */ jsx("div", {
|
package/dist/lib/week.d.ts
CHANGED
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
declare const getLocaleWeekInfo: (locale: string) => Intl.WeekInfo | undefined;
|
|
12
12
|
/**
|
|
13
|
-
* First weekday as a
|
|
13
|
+
* First weekday as a day-of-week value (0 = Sunday … 6 = Saturday): Monday
|
|
14
14
|
* across most of Europe, Sunday in the US, Saturday across much of the Gulf.
|
|
15
15
|
* Falls back to Monday when the runtime lacks week info.
|
|
16
16
|
*/
|
|
17
17
|
declare const getFirstWeekday: (locale: string) => number;
|
|
18
18
|
/**
|
|
19
|
-
* Weekend weekdays as
|
|
19
|
+
* Weekend weekdays as day-of-week values (0 = Sunday … 6 = Saturday):
|
|
20
20
|
* Saturday/Sunday across the West, Friday/Saturday across much of the Gulf.
|
|
21
21
|
* Falls back to Saturday/Sunday when the runtime lacks week info.
|
|
22
22
|
*/
|
package/dist/lib/week.js
CHANGED
|
@@ -18,7 +18,7 @@ const getLocaleWeekInfo = (locale) => {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
-
* First weekday as a
|
|
21
|
+
* First weekday as a day-of-week value (0 = Sunday … 6 = Saturday): Monday
|
|
22
22
|
* across most of Europe, Sunday in the US, Saturday across much of the Gulf.
|
|
23
23
|
* Falls back to Monday when the runtime lacks week info.
|
|
24
24
|
*/
|
|
@@ -27,7 +27,7 @@ const getFirstWeekday = (locale) => {
|
|
|
27
27
|
return typeof firstDay === "number" ? firstDay % 7 : 1;
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
|
-
* Weekend weekdays as
|
|
30
|
+
* Weekend weekdays as day-of-week values (0 = Sunday … 6 = Saturday):
|
|
31
31
|
* Saturday/Sunday across the West, Friday/Saturday across much of the Gulf.
|
|
32
32
|
* Falls back to Saturday/Sunday when the runtime lacks week info.
|
|
33
33
|
*/
|
|
@@ -4,6 +4,7 @@ import { BidiText } from "../components/bidi-text.js";
|
|
|
4
4
|
import { ReviewAuthorAvatar } from "./review-author-avatar.js";
|
|
5
5
|
import { CheckIcon, RotateCcwIcon, Trash2Icon } from "lucide-react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { Temporal } from "temporal-polyfill/full";
|
|
7
8
|
//#region src/review/review-comment-card.tsx
|
|
8
9
|
/** One comment on a reviewed surface: who wrote it, when, what it says, what
|
|
9
10
|
* it points at, and the two things a reader can do to it. */
|
|
@@ -70,7 +71,7 @@ const ReviewCommentCard = ({ author, timestamp, formattedTime, body, anchorText,
|
|
|
70
71
|
* yields no attribute rather than throwing on `toISOString`. */
|
|
71
72
|
const toIsoInstant = (timestamp) => {
|
|
72
73
|
if (typeof timestamp === "string") return timestamp;
|
|
73
|
-
return Number.isNaN(timestamp.getTime()) ? void 0 : timestamp.
|
|
74
|
+
return Number.isNaN(timestamp.getTime()) ? void 0 : Temporal.Instant.fromEpochMilliseconds(timestamp.getTime()).toString({ fractionalSecondDigits: 3 });
|
|
74
75
|
};
|
|
75
76
|
//#endregion
|
|
76
77
|
export { ReviewCommentCard };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/ui",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "Stella's design system: bidi-aware React primitives built on Base UI, the dockable inspector pane, and the Tailwind v4 theme they are styled with.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"base-ui",
|
|
@@ -594,7 +594,8 @@
|
|
|
594
594
|
"clsx": "^2.1.1",
|
|
595
595
|
"input-otp": "^1.5.0",
|
|
596
596
|
"lucide-react": "1.39.0",
|
|
597
|
-
"tailwind-merge": "^3.6.0"
|
|
597
|
+
"tailwind-merge": "^3.6.0",
|
|
598
|
+
"temporal-polyfill": "1.0.4"
|
|
598
599
|
},
|
|
599
600
|
"devDependencies": {
|
|
600
601
|
"@atlaskit/pragmatic-drag-and-drop": "^3.1.0",
|
|
@@ -602,7 +603,7 @@
|
|
|
602
603
|
"@base-ui/react": "1.7.0",
|
|
603
604
|
"@dnd-kit/core": "^6.3.1",
|
|
604
605
|
"@dnd-kit/sortable": "^10.0.0",
|
|
605
|
-
"@playwright/test": "^1.
|
|
606
|
+
"@playwright/test": "^1.63.0",
|
|
606
607
|
"@stll/typescript-config": "0.0.0",
|
|
607
608
|
"@tailwindcss/vite": "4.3.3",
|
|
608
609
|
"@tanstack/react-virtual": "^3.14.10",
|