gridsmith-ui 0.25.0 → 0.26.0

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.
Files changed (55) hide show
  1. package/dist/charts.js.map +1 -1
  2. package/dist/chat.js +3 -3
  3. package/dist/{chunk-W7GCEJEH.js → chunk-CYN5O567.js} +5 -5
  4. package/dist/{chunk-W7GCEJEH.js.map → chunk-CYN5O567.js.map} +1 -1
  5. package/dist/{chunk-FHMCAOAH.js → chunk-EX64MN2U.js} +3 -3
  6. package/dist/{chunk-FHMCAOAH.js.map → chunk-EX64MN2U.js.map} +1 -1
  7. package/dist/{chunk-HYMXAIXN.js → chunk-HXVXJUGV.js} +7 -7
  8. package/dist/chunk-HXVXJUGV.js.map +1 -0
  9. package/dist/{chunk-3CUKJ4VP.js → chunk-KWLNEH53.js} +5 -5
  10. package/dist/{chunk-3CUKJ4VP.js.map → chunk-KWLNEH53.js.map} +1 -1
  11. package/dist/{chunk-QGSCF7PN.js → chunk-MYIJNVTK.js} +4 -4
  12. package/dist/{chunk-QGSCF7PN.js.map → chunk-MYIJNVTK.js.map} +1 -1
  13. package/dist/{chunk-2YSEME33.js → chunk-SFY2KMHG.js} +63 -6
  14. package/dist/chunk-SFY2KMHG.js.map +1 -0
  15. package/dist/{chunk-JXANCXK6.js → chunk-ZI3OQDET.js} +18 -4
  16. package/dist/chunk-ZI3OQDET.js.map +1 -0
  17. package/dist/{chunk-HZIP4EJI.js → chunk-ZYVXV4WC.js} +3 -3
  18. package/dist/{chunk-HZIP4EJI.js.map → chunk-ZYVXV4WC.js.map} +1 -1
  19. package/dist/data-grid.js +4 -4
  20. package/dist/editor-mentions.js +1 -1
  21. package/dist/editor.js +2 -2
  22. package/dist/{feather-W26ERBQV.js → feather-VFWLAILO.js} +4 -3
  23. package/dist/feather-VFWLAILO.js.map +1 -0
  24. package/dist/{heroicons-MY43466C.js → heroicons-H3Z7DFCF.js} +4 -3
  25. package/dist/heroicons-H3Z7DFCF.js.map +1 -0
  26. package/dist/icons.d.ts +1 -1
  27. package/dist/icons.js +1 -1
  28. package/dist/index.d.ts +53 -4
  29. package/dist/index.js +1192 -981
  30. package/dist/index.js.map +1 -1
  31. package/dist/kanban.js +4 -5
  32. package/dist/kanban.js.map +1 -1
  33. package/dist/{lucide-CMPVM3PS.js → lucide-MR32KM4G.js} +4 -3
  34. package/dist/lucide-MR32KM4G.js.map +1 -0
  35. package/dist/{phosphor-RMHZOW2O.js → phosphor-ABQQL5N5.js} +4 -3
  36. package/dist/phosphor-ABQQL5N5.js.map +1 -0
  37. package/dist/resizable.js +5 -6
  38. package/dist/resizable.js.map +1 -1
  39. package/dist/roots.css +18 -0
  40. package/dist/{tabler-MJVEYHA5.js → tabler-ANUSY4D2.js} +4 -3
  41. package/dist/tabler-ANUSY4D2.js.map +1 -0
  42. package/dist/tokens.css +6 -0
  43. package/dist/virtualized.js +4 -4
  44. package/dist-static/tokens.css +6 -0
  45. package/package.json +1 -1
  46. package/dist/chunk-2YSEME33.js.map +0 -1
  47. package/dist/chunk-HYMXAIXN.js.map +0 -1
  48. package/dist/chunk-JXANCXK6.js.map +0 -1
  49. package/dist/chunk-WOVYHFUD.js +0 -50
  50. package/dist/chunk-WOVYHFUD.js.map +0 -1
  51. package/dist/feather-W26ERBQV.js.map +0 -1
  52. package/dist/heroicons-MY43466C.js.map +0 -1
  53. package/dist/lucide-CMPVM3PS.js.map +0 -1
  54. package/dist/phosphor-RMHZOW2O.js.map +0 -1
  55. package/dist/tabler-MJVEYHA5.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-OTCEJN64.js';
2
- import { useWholeListCheck, pagerPart, Pagination, Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, Label, InlineFieldError, AddAnotherRowCtx, Field } from './chunk-W7GCEJEH.js';
3
- export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-W7GCEJEH.js';
4
- import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-3CUKJ4VP.js';
5
- export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-3CUKJ4VP.js';
6
- import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, preferenceActionKey } from './chunk-HZIP4EJI.js';
7
- export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, preferenceActionKey, usePreferences } from './chunk-HZIP4EJI.js';
2
+ import { useWholeListCheck, pagerPart, Pagination, Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, Label, InlineFieldError, AddAnotherRowCtx, Field } from './chunk-CYN5O567.js';
3
+ export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-CYN5O567.js';
4
+ import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-KWLNEH53.js';
5
+ export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-KWLNEH53.js';
6
+ import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, preferenceActionKey } from './chunk-ZYVXV4WC.js';
7
+ export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, preferenceActionKey, usePreferences } from './chunk-ZYVXV4WC.js';
8
8
  import { Skeleton, TITLE_TEXT, formatLocale } from './chunk-X2HEV7FB.js';
9
9
  export { Skeleton } from './chunk-X2HEV7FB.js';
10
10
  import { useScrollEdges, EDGE_FADE } from './chunk-3BRQRKDO.js';
@@ -14,18 +14,18 @@ import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } fro
14
14
  export { useFormContext } from './chunk-XVB55M3N.js';
15
15
  import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-RWKCAKN3.js';
16
16
  export { Avatar } from './chunk-RWKCAKN3.js';
17
- import { Link } from './chunk-QGSCF7PN.js';
18
- export { CodeBlock, Divider, Link } from './chunk-QGSCF7PN.js';
19
- import { useFillHeight, FillContext } from './chunk-2YSEME33.js';
17
+ import { Link } from './chunk-MYIJNVTK.js';
18
+ export { CodeBlock, Divider, Link } from './chunk-MYIJNVTK.js';
19
+ import { useFillHeight, FillContext, useKeyboardScroll, FOCUS_INSET } from './chunk-SFY2KMHG.js';
20
20
  import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-QM4SJKWV.js';
21
- import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, rovingTabStop, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus, useCloseOnFocusOut } from './chunk-JXANCXK6.js';
22
- export { useGridKeyboard } from './chunk-JXANCXK6.js';
23
- import { Badge } from './chunk-FHMCAOAH.js';
24
- export { Badge } from './chunk-FHMCAOAH.js';
21
+ import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, rovingTabStop, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus } from './chunk-ZI3OQDET.js';
22
+ export { useGridKeyboard } from './chunk-ZI3OQDET.js';
23
+ import { Badge } from './chunk-EX64MN2U.js';
24
+ export { Badge } from './chunk-EX64MN2U.js';
25
25
  import { useTextOverflow, Tooltip } from './chunk-MD2EFMES.js';
26
26
  export { Tooltip, useTextOverflow } from './chunk-MD2EFMES.js';
27
27
  import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
28
- import { Icon, slotIcon, warnOnce } from './chunk-HYMXAIXN.js';
28
+ import { Icon, slotIcon, warnOnce } from './chunk-HXVXJUGV.js';
29
29
  import { usePosition } from './chunk-HP7WUZPK.js';
30
30
  import { useLatest } from './chunk-WH2FUFAC.js';
31
31
  import { cn, GLASS_SURFACE, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ACTIVE_RING, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, FOCUS_WITHIN_RING } from './chunk-M2QRNBDQ.js';
@@ -247,7 +247,7 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
247
247
  const listboxId = useId();
248
248
  const pos = usePosition(triggerRef, listRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
249
249
  const filtered = options.filter(
250
- (o) => !o.disabled && foldForSearch(o.label).includes(foldForSearch(query))
250
+ (o) => !o.disabled && foldForSearch(o.keywords ? `${o.label} ${o.keywords}` : o.label).includes(foldForSearch(query))
251
251
  );
252
252
  useActiveOptionInView(listRef, open && filtered[highlightIdx] ? `${listboxId}-opt-${highlightIdx}` : void 0);
253
253
  const selectedLabel = options.find((o) => o.value === value)?.label ?? "";
@@ -485,6 +485,24 @@ function timeZones() {
485
485
  const zones = [...new Set(listed.map(canonicalTimeZone))].filter((z) => z !== "UTC").sort((a, b) => a.localeCompare(b));
486
486
  return ["UTC", ...zones];
487
487
  }
488
+ var RENAMED_CITIES = [
489
+ ["Kolkata", ["Asia/Calcutta", "Asia/Kolkata"]],
490
+ ["Kyiv", ["Europe/Kiev", "Europe/Kyiv"]],
491
+ ["Ho Chi Minh City", ["Asia/Saigon", "Asia/Ho_Chi_Minh"]],
492
+ ["Yangon", ["Asia/Rangoon", "Asia/Yangon"]],
493
+ ["Kathmandu", ["Asia/Katmandu", "Asia/Kathmandu"]],
494
+ ["Nuuk", ["America/Godthab", "America/Nuuk"]],
495
+ ["Faroe", ["Atlantic/Faeroe", "Atlantic/Faroe"]],
496
+ ["Chuuk", ["Pacific/Truk", "Pacific/Chuuk"]],
497
+ ["Pohnpei", ["Pacific/Ponape", "Pacific/Pohnpei"]],
498
+ ["Kanton", ["Pacific/Enderbury", "Pacific/Kanton"]],
499
+ ["Asmara", ["Africa/Asmera", "Africa/Asmara"]],
500
+ ["Atikokan", ["America/Coral_Harbour", "America/Atikokan"]],
501
+ ["Thimphu", ["Asia/Thimbu", "Asia/Thimphu"]],
502
+ ["Dhaka", ["Asia/Dacca", "Asia/Dhaka"]],
503
+ ["Ulaanbaatar", ["Asia/Ulan_Bator", "Asia/Ulaanbaatar"]]
504
+ ];
505
+ var CITY_OF = new Map(RENAMED_CITIES.flatMap(([city, zones]) => zones.map((zone) => [zone, city])));
488
506
  var labelOf = (zone, at) => {
489
507
  const offset = timeZoneOffset(zone, at);
490
508
  return offset ? `${zone.replace(/_/g, " ")} (${offset})` : zone.replace(/_/g, " ");
@@ -495,7 +513,7 @@ function zoneOptions() {
495
513
  const now = Date.now();
496
514
  if (!zoneList || now - zoneList.at > HOUR) {
497
515
  const at = new Date(now);
498
- zoneList = { at: now, options: timeZones().map((zone) => ({ value: zone, label: labelOf(zone, at) })) };
516
+ zoneList = { at: now, options: timeZones().map((zone) => ({ value: zone, label: labelOf(zone, at), keywords: CITY_OF.get(zone) })) };
499
517
  }
500
518
  return zoneList.options;
501
519
  }
@@ -506,9 +524,9 @@ function TimezoneSelect({ value, onChange, placeholder = "Search time zones\u202
506
524
  if (!stored) {
507
525
  const mine = browserTimeZone();
508
526
  const label = zones.find((o) => o.value === mine)?.label ?? labelOf(mine, /* @__PURE__ */ new Date());
509
- return [{ value: mine, label: `${label} \xB7 your time zone` }, ...zones.filter((o) => o.value !== mine)];
527
+ return [{ value: mine, label: `${label} \xB7 your time zone`, keywords: CITY_OF.get(mine) }, ...zones.filter((o) => o.value !== mine)];
510
528
  }
511
- return zones.some((o) => o.value === stored) ? zones : [{ value: stored, label: labelOf(stored, /* @__PURE__ */ new Date()) }, ...zones];
529
+ return zones.some((o) => o.value === stored) ? zones : [{ value: stored, label: labelOf(stored, /* @__PURE__ */ new Date()), keywords: CITY_OF.get(stored) }, ...zones];
512
530
  }, [zones, stored]);
513
531
  return /* @__PURE__ */ jsx(Combobox, { value: stored, onChange, options, placeholder, ...rest });
514
532
  }
@@ -877,6 +895,279 @@ function dateSegments(dayFirst, daysInMonth = 31) {
877
895
  const year = { key: "year", label: "Year", min: 1, max: 9999, maxDigits: 4, placeholder: "yyyy" };
878
896
  return dayFirst ? [day, month, year] : [month, day, year];
879
897
  }
898
+
899
+ // ../../showcase/src/components/Calendar/calendar-utils.ts
900
+ var formatters = /* @__PURE__ */ new Map();
901
+ function dtf(options) {
902
+ const locale = formatLocale();
903
+ const key = `${locale ?? ""}|${JSON.stringify(options)}`;
904
+ let f = formatters.get(key);
905
+ if (!f) {
906
+ f = new Intl.DateTimeFormat(locale, options);
907
+ formatters.set(key, f);
908
+ }
909
+ return f;
910
+ }
911
+ function monthName(month, width = "long") {
912
+ return dtf({ month: width }).format(new Date(2026, month, 1));
913
+ }
914
+ function weekdayNames(weekStartsOn, width = "short") {
915
+ const f = dtf({ weekday: width });
916
+ return Array.from({ length: 7 }, (_, i) => f.format(new Date(2026, 0, 4 + weekStartsOn + i)));
917
+ }
918
+ function isSameDay(a, b) {
919
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
920
+ }
921
+ function isSameMonth(a, b) {
922
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
923
+ }
924
+ function isToday(d) {
925
+ return isSameDay(d, /* @__PURE__ */ new Date());
926
+ }
927
+ function isWeekend(d) {
928
+ const day = d.getDay();
929
+ return day === 0 || day === 6;
930
+ }
931
+ function startOfDay(d) {
932
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
933
+ }
934
+ function startOfWeek(d, weekStartsOn = 0) {
935
+ const date = new Date(d);
936
+ const day = date.getDay();
937
+ const diff = (day - weekStartsOn + 7) % 7;
938
+ date.setDate(date.getDate() - diff);
939
+ return startOfDay(date);
940
+ }
941
+ function endOfWeek(d, weekStartsOn = 0) {
942
+ const start = startOfWeek(d, weekStartsOn);
943
+ const end = new Date(start);
944
+ end.setDate(end.getDate() + 6);
945
+ end.setHours(23, 59, 59, 999);
946
+ return end;
947
+ }
948
+ function addDays2(d, n) {
949
+ const result = new Date(d);
950
+ result.setDate(result.getDate() + n);
951
+ return result;
952
+ }
953
+ function addMonths(d, n) {
954
+ const result = new Date(d);
955
+ const day = result.getDate();
956
+ result.setDate(1);
957
+ result.setMonth(result.getMonth() + n);
958
+ result.setDate(Math.min(day, new Date(result.getFullYear(), result.getMonth() + 1, 0).getDate()));
959
+ return result;
960
+ }
961
+ function getMonthGrid(year, month, weekStartsOn = 0) {
962
+ const firstOfMonth = new Date(year, month, 1);
963
+ const gridStart = startOfWeek(firstOfMonth, weekStartsOn);
964
+ const weeks = [];
965
+ const current = new Date(gridStart);
966
+ for (let w = 0; w < 6; w++) {
967
+ const week = [];
968
+ for (let d = 0; d < 7; d++) {
969
+ week.push(new Date(current));
970
+ current.setDate(current.getDate() + 1);
971
+ }
972
+ weeks.push(week);
973
+ }
974
+ return weeks;
975
+ }
976
+ function getWeekDays(date, weekStartsOn = 0) {
977
+ const start = startOfWeek(date, weekStartsOn);
978
+ const days = [];
979
+ for (let i = 0; i < 7; i++) {
980
+ days.push(addDays2(start, i));
981
+ }
982
+ return days;
983
+ }
984
+ var DAY_MS = 864e5;
985
+ function daysBetween(a, b) {
986
+ return Math.round((startOfDay(b).getTime() - startOfDay(a).getTime()) / DAY_MS);
987
+ }
988
+ function eventSpan(e) {
989
+ const first = startOfDay(e.start);
990
+ if (!e.end) return { first, last: first };
991
+ const lastInstant = e.allDay ? e.end : new Date(e.end.getTime() - 1);
992
+ const last = startOfDay(lastInstant);
993
+ return { first, last: last < first ? first : last };
994
+ }
995
+ function isBarEvent(e) {
996
+ if (e.allDay) return true;
997
+ if (!e.end) return false;
998
+ const dayLater = new Date(e.start);
999
+ dayLater.setDate(dayLater.getDate() + 1);
1000
+ return e.end.getTime() >= dayLater.getTime();
1001
+ }
1002
+ function getEventsForDay(events, date) {
1003
+ const day = startOfDay(date).getTime();
1004
+ return events.filter((e) => {
1005
+ const { first, last } = eventSpan(e);
1006
+ return first.getTime() <= day && day <= last.getTime();
1007
+ });
1008
+ }
1009
+ function dayPieces(events, date) {
1010
+ const dayStart = startOfDay(date);
1011
+ const next = new Date(dayStart.getFullYear(), dayStart.getMonth(), dayStart.getDate() + 1);
1012
+ const pieces = [];
1013
+ for (const event of events) {
1014
+ if (isBarEvent(event)) continue;
1015
+ const ownDayEnd = new Date(event.start.getFullYear(), event.start.getMonth(), event.start.getDate() + 1);
1016
+ const end = event.end && event.end > event.start ? event.end : new Date(Math.min(event.start.getTime() + 36e5, ownDayEnd.getTime()));
1017
+ const start = event.start < dayStart ? dayStart : event.start;
1018
+ const stop = end > next ? next : end;
1019
+ if (stop > start) pieces.push({ event, start, end: stop });
1020
+ }
1021
+ return pieces.sort((a, b) => a.start.getTime() - b.start.getTime() || b.end.getTime() - a.end.getTime());
1022
+ }
1023
+ function twelveHour() {
1024
+ return /^h1[12]$/.test(dtf({ hour: "numeric" }).resolvedOptions().hourCycle ?? "");
1025
+ }
1026
+ var timeOptions = () => ({ hour: twelveHour() ? "numeric" : "2-digit", minute: "2-digit" });
1027
+ function formatTime(d) {
1028
+ return dtf(timeOptions()).format(d);
1029
+ }
1030
+ function formatHourLabel(hour) {
1031
+ const at = new Date(2026, 0, 5, hour);
1032
+ return twelveHour() ? dtf({ hour: "numeric" }).format(at) : dtf({ hour: "2-digit", minute: "2-digit" }).format(at);
1033
+ }
1034
+ function dayOffset(days) {
1035
+ return new Intl.NumberFormat(formatLocale(), { signDisplay: "always" }).format(days);
1036
+ }
1037
+ function formatTimeRange(start, end) {
1038
+ if (!end) return formatTime(start);
1039
+ const f = dtf(timeOptions());
1040
+ if (daysBetween(start, end) === 0) return f.formatRange(start, end);
1041
+ const sep = f.formatRangeToParts(start, end).find((p) => p.source === "shared" && p.type === "literal")?.value ?? " \u2013 ";
1042
+ const days = endDays(start, end);
1043
+ return days === 0 ? `${f.format(start)}${sep}${f.format(end)}` : `${f.format(start)}${sep}${f.format(end)} (${dayOffset(days)})`;
1044
+ }
1045
+ function endDays(start, end) {
1046
+ return end > start ? daysBetween(start, new Date(end.getTime() - 1)) : daysBetween(start, end);
1047
+ }
1048
+ function speakTimeRange(start, end) {
1049
+ if (!end) return formatTime(start);
1050
+ const days = endDays(start, end);
1051
+ const to = `${formatTime(start)} to ${formatTime(end)}`;
1052
+ return days === 0 ? to : days === 1 ? `${to} the next day` : `${to}, ${days} days later`;
1053
+ }
1054
+ function formatMonthYear(d) {
1055
+ return dtf({ month: "long", year: "numeric" }).format(d);
1056
+ }
1057
+ function formatDayLong(d) {
1058
+ return dtf({ weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
1059
+ }
1060
+ function formatDayShort(d) {
1061
+ return dtf({ weekday: "short", day: "numeric", month: "short" }).format(d);
1062
+ }
1063
+ function formatDayMedium(d) {
1064
+ return dtf({ day: "numeric", month: "short", year: "numeric" }).format(d);
1065
+ }
1066
+ function formatDayRange(start, end) {
1067
+ return dtf({ day: "numeric", month: "short", year: "numeric" }).formatRange(startOfDay(start), startOfDay(end));
1068
+ }
1069
+ function formatMonthRange(start, end) {
1070
+ return dtf({ month: "long", year: "numeric" }).formatRange(new Date(start.getFullYear(), start.getMonth(), 1), new Date(end.getFullYear(), end.getMonth(), 1));
1071
+ }
1072
+ function formatWeekRange(d, weekStartsOn = 0) {
1073
+ return dtf({ day: "numeric", month: "short", year: "numeric" }).formatRange(startOfWeek(d, weekStartsOn), startOfDay(endOfWeek(d, weekStartsOn)));
1074
+ }
1075
+ function formatEventWhen(e, spoken = false) {
1076
+ const { first, last } = eventSpan(e);
1077
+ const multiDay = last > first;
1078
+ if (e.allDay) {
1079
+ if (!multiDay) return spoken ? formatDayLong(first) : formatDayShort(first);
1080
+ return dtf(spoken ? { weekday: "long", day: "numeric", month: "long" } : { day: "numeric", month: "short" }).formatRange(first, last);
1081
+ }
1082
+ if (!e.end || !isBarEvent(e)) {
1083
+ const day = spoken ? formatDayLong(e.start) : formatDayShort(e.start);
1084
+ const end = e.end && e.end > e.start ? e.end : void 0;
1085
+ return `${day}, ${spoken ? speakTimeRange(e.start, end) : formatTimeRange(e.start, end)}`;
1086
+ }
1087
+ return dtf({ weekday: "short", day: "numeric", month: "short", hour: "numeric", minute: "2-digit" }).formatRange(e.start, e.end);
1088
+ }
1089
+ function layoutWeek(events, days, capacity) {
1090
+ const weekStart = startOfDay(days[0]);
1091
+ const weekEnd = startOfDay(days[days.length - 1]);
1092
+ const items = [];
1093
+ for (const event of events) {
1094
+ const { first, last } = eventSpan(event);
1095
+ if (last < weekStart || first > weekEnd) continue;
1096
+ items.push({
1097
+ event,
1098
+ startCol: Math.max(0, daysBetween(weekStart, first)),
1099
+ endCol: Math.min(days.length - 1, daysBetween(weekStart, last)),
1100
+ before: first < weekStart,
1101
+ after: last > weekEnd,
1102
+ length: daysBetween(first, last) + 1
1103
+ });
1104
+ }
1105
+ items.sort((a, b) => {
1106
+ const barA = isBarEvent(a.event) || a.length > 1, barB = isBarEvent(b.event) || b.length > 1;
1107
+ if (barA !== barB) return barA ? -1 : 1;
1108
+ if (barA && a.length !== b.length) return b.length - a.length;
1109
+ return a.event.start.getTime() - b.event.start.getTime() || a.event.title.localeCompare(b.event.title);
1110
+ });
1111
+ const taken = [];
1112
+ const laneOf = /* @__PURE__ */ new Map();
1113
+ for (const item of items) {
1114
+ let lane = 0;
1115
+ while (Array.from({ length: item.endCol - item.startCol + 1 }, (_, k) => taken[lane]?.[item.startCol + k]).some(Boolean)) lane++;
1116
+ taken[lane] ??= [];
1117
+ for (let c = item.startCol; c <= item.endCol; c++) taken[lane][c] = true;
1118
+ laneOf.set(item, lane);
1119
+ }
1120
+ const cap = Math.max(1, capacity);
1121
+ const overflow = days.map((_, c) => items.some((it) => laneOf.get(it) >= cap && it.startCol <= c && c <= it.endCol));
1122
+ const more = days.map((_, c) => overflow[c] ? items.filter((it) => laneOf.get(it) >= cap - 1 && it.startCol <= c && c <= it.endCol).map((it) => it.event) : []);
1123
+ const segments = [];
1124
+ for (const item of items) {
1125
+ const lane = laneOf.get(item);
1126
+ if (lane >= cap) continue;
1127
+ if (lane < cap - 1) {
1128
+ segments.push({ event: item.event, startCol: item.startCol, span: item.endCol - item.startCol + 1, lane, continuesBefore: item.before, continuesAfter: item.after });
1129
+ continue;
1130
+ }
1131
+ let runStart = -1;
1132
+ for (let c = item.startCol; c <= item.endCol + 1; c++) {
1133
+ const free = c <= item.endCol && !overflow[c];
1134
+ if (free && runStart < 0) runStart = c;
1135
+ if (!free && runStart >= 0) {
1136
+ segments.push({ event: item.event, startCol: runStart, span: c - runStart, lane, continuesBefore: runStart > item.startCol || item.before, continuesAfter: c - 1 < item.endCol || item.after });
1137
+ runStart = -1;
1138
+ }
1139
+ }
1140
+ }
1141
+ const lanes = Math.min(cap, taken.length);
1142
+ return { segments, more, lanes };
1143
+ }
1144
+ function layoutPieces(pieces, minMinutes = 0) {
1145
+ const out = [];
1146
+ let group = [];
1147
+ let columnEnds = [];
1148
+ let groupEnd = -Infinity;
1149
+ const close = () => {
1150
+ for (const placed of group) placed.columns = columnEnds.length;
1151
+ out.push(...group);
1152
+ group = [];
1153
+ columnEnds = [];
1154
+ };
1155
+ for (const piece of pieces) {
1156
+ const at = piece.start.getTime();
1157
+ const end = Math.max(piece.end.getTime(), at + minMinutes * 6e4);
1158
+ if (group.length && at >= groupEnd) close();
1159
+ let column = columnEnds.findIndex((e) => e <= at);
1160
+ if (column < 0) column = columnEnds.push(0) - 1;
1161
+ columnEnds[column] = end;
1162
+ groupEnd = Math.max(group.length ? groupEnd : -Infinity, end);
1163
+ group.push({ piece, column, columns: 0 });
1164
+ }
1165
+ if (group.length) close();
1166
+ return out;
1167
+ }
1168
+ function timeToFraction(d) {
1169
+ return (d.getHours() * 60 + d.getMinutes()) / (24 * 60);
1170
+ }
880
1171
  var CONTROL_HEIGHT2 = {
881
1172
  sm: "h-[var(--ds-control-height-sm)]",
882
1173
  md: "h-[var(--ds-control-height-md)]",
@@ -887,21 +1178,19 @@ var TEXT_SIZE2 = {
887
1178
  md: "text-sm",
888
1179
  lg: "text-base"
889
1180
  };
890
- var DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
891
- var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
892
- var MONTHS = [
893
- "January",
894
- "February",
895
- "March",
896
- "April",
897
- "May",
898
- "June",
899
- "July",
900
- "August",
901
- "September",
902
- "October",
903
- "November",
904
- "December"
1181
+ var ENGLISH_MONTHS = [
1182
+ "january",
1183
+ "february",
1184
+ "march",
1185
+ "april",
1186
+ "may",
1187
+ "june",
1188
+ "july",
1189
+ "august",
1190
+ "september",
1191
+ "october",
1192
+ "november",
1193
+ "december"
905
1194
  ];
906
1195
  function getDaysInMonth(year, month) {
907
1196
  return new Date(year, month + 1, 0).getDate();
@@ -909,12 +1198,9 @@ function getDaysInMonth(year, month) {
909
1198
  function getFirstDayOfWeek(year, month) {
910
1199
  return new Date(year, month, 1).getDay();
911
1200
  }
912
- function isSameDay(a, b) {
1201
+ function isSameDay2(a, b) {
913
1202
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
914
1203
  }
915
- function formatDate(d) {
916
- return `${MONTHS[d.getMonth()].slice(0, 3)} ${d.getDate()}, ${d.getFullYear()}`;
917
- }
918
1204
  function localeDayFirst() {
919
1205
  try {
920
1206
  const parts = new Intl.DateTimeFormat(formatLocale()).formatToParts(new Date(2e3, 11, 25));
@@ -923,23 +1209,32 @@ function localeDayFirst() {
923
1209
  return true;
924
1210
  }
925
1211
  }
926
- function validDate(year, monthIndex, day) {
927
- if (monthIndex < 0 || monthIndex > 11 || day < 1) return null;
928
- const d = new Date(year, monthIndex, day);
929
- return d.getFullYear() === year && d.getMonth() === monthIndex && d.getDate() === day ? d : null;
1212
+ function validDate(year, monthIndex2, day) {
1213
+ if (monthIndex2 < 0 || monthIndex2 > 11 || day < 1) return null;
1214
+ const d = new Date(year, monthIndex2, day);
1215
+ return d.getFullYear() === year && d.getMonth() === monthIndex2 && d.getDate() === day ? d : null;
1216
+ }
1217
+ function monthIndex(name) {
1218
+ const typed = name.toLocaleLowerCase().replace(/\.$/, "");
1219
+ const fold = (m) => m.toLocaleLowerCase().replace(/\.$/, "");
1220
+ const lists = [ENGLISH_MONTHS, Array.from({ length: 12 }, (_, i) => fold(monthName(i, "long"))), Array.from({ length: 12 }, (_, i) => fold(monthName(i, "short")))];
1221
+ for (const list of lists) {
1222
+ const i = list.findIndex((m) => m.startsWith(typed) || typed.length >= 3 && typed.startsWith(m));
1223
+ if (i >= 0) return i;
1224
+ }
1225
+ return -1;
930
1226
  }
931
1227
  function parseTypedDate(text, dayFirst = localeDayFirst()) {
932
1228
  const t = text.trim().replace(/\s+/g, " ");
933
1229
  if (!t) return null;
934
1230
  let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(t);
935
1231
  if (m) return validDate(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
936
- const monthIndex = (name) => MONTHS.findIndex((mm) => mm.toLowerCase().startsWith(name.toLowerCase().replace(/\.$/, "")));
937
- m = /^([A-Za-z]{3,})\.?\s+(\d{1,2}),?\s+(\d{4})$/.exec(t);
1232
+ m = /^(\p{L}{3,})\.?\s+(\d{1,2}),?\s+(\d{4})$/u.exec(t);
938
1233
  if (m) {
939
1234
  const mi = monthIndex(m[1]);
940
1235
  return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[2]));
941
1236
  }
942
- m = /^(\d{1,2})\s+([A-Za-z]{3,})\.?,?\s+(\d{4})$/.exec(t);
1237
+ m = /^(\d{1,2})\.?\s+(\p{L}{3,})\.?,?\s+(\d{4})$/u.exec(t);
943
1238
  if (m) {
944
1239
  const mi = monthIndex(m[2]);
945
1240
  return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[1]));
@@ -1059,7 +1354,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1059
1354
  const d = validDate(next.year, next.month - 1, next.day);
1060
1355
  const ok = !!d && (!min || d >= min) && (!max || d <= max);
1061
1356
  setTypedInvalid(!ok);
1062
- if (d && ok && !(value && isSameDay(value, d))) {
1357
+ if (d && ok && !(value && isSameDay2(value, d))) {
1063
1358
  lastCommitted.current = d.getTime();
1064
1359
  onChange(d);
1065
1360
  setViewDate(d);
@@ -1139,7 +1434,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1139
1434
  };
1140
1435
  const rangeA = range2?.start ?? null;
1141
1436
  const rangeB = range2?.end ?? null;
1142
- const otherEnd = value ? rangeA && !isSameDay(rangeA, value) ? rangeA : rangeB : rangeA ?? rangeB;
1437
+ const otherEnd = value ? rangeA && !isSameDay2(rangeA, value) ? rangeA : rangeB : rangeA ?? rangeB;
1143
1438
  const thisEnd = value ?? (otherEnd && hover ? hover : null);
1144
1439
  const [bandStart, bandEnd] = otherEnd && thisEnd ? otherEnd <= thisEnd ? [otherEnd, thisEnd] : [thisEnd, otherEnd] : [null, null];
1145
1440
  const inBand = (d) => !!(bandStart && bandEnd && d > bandStart && d < bandEnd);
@@ -1256,11 +1551,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1256
1551
  style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1257
1552
  children: [
1258
1553
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: prevMonth, "aria-label": "Previous month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "rotate-180" }) }),
1259
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: [
1260
- MONTHS[month],
1261
- " ",
1262
- year
1263
- ] }),
1554
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthYear(viewDate) }),
1264
1555
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: nextMonth, "aria-label": "Next month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) })
1265
1556
  ]
1266
1557
  }
@@ -1270,18 +1561,18 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1270
1561
  {
1271
1562
  ref: gridRef,
1272
1563
  role: "grid",
1273
- "aria-label": `${MONTHS[month]} ${year}`,
1564
+ "aria-label": formatMonthYear(viewDate),
1274
1565
  onKeyDown: grid.onKeyDown,
1275
1566
  className: "flex flex-col gap-ds-0.5 tabular-nums",
1276
1567
  style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1277
1568
  children: [
1278
- /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: DAYS.map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": DAY_NAMES[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, d)) }),
1569
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: weekdayNames(0, "narrow").map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": weekdayNames(0, "long")[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1279
1570
  weeks.map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5", onMouseLeave: () => setHover(null), children: week.map((day, i) => {
1280
1571
  if (day === null) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, `empty-${i}`);
1281
1572
  const dateObj = new Date(year, month, day);
1282
- const isSelected = !!value && isSameDay(dateObj, value);
1283
- const isOtherEnd = !!otherEnd && isSameDay(dateObj, otherEnd);
1284
- const isToday2 = isSameDay(dateObj, today);
1573
+ const isSelected = !!value && isSameDay2(dateObj, value);
1574
+ const isOtherEnd = !!otherEnd && isSameDay2(dateObj, otherEnd);
1575
+ const isToday2 = isSameDay2(dateObj, today);
1285
1576
  const between = inBand(dateObj);
1286
1577
  const dis = isDisabledDay(dateObj);
1287
1578
  return /* @__PURE__ */ jsx(
@@ -1294,12 +1585,12 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1294
1585
  "aria-disabled": dis || void 0,
1295
1586
  onClick: () => selectDay(day),
1296
1587
  onFocus: () => {
1297
- if (!isSameDay(dateObj, focusDate)) setFocusDate(dateObj);
1588
+ if (!isSameDay2(dateObj, focusDate)) setFocusDate(dateObj);
1298
1589
  },
1299
1590
  onMouseEnter: () => setHover(dateObj),
1300
1591
  "aria-selected": isSelected,
1301
1592
  "aria-current": isToday2 ? "date" : void 0,
1302
- "aria-label": formatDate(dateObj),
1593
+ "aria-label": formatDayLong(dateObj),
1303
1594
  className: cn(
1304
1595
  "w-full aspect-square flex items-center justify-center text-xs",
1305
1596
  "transition-colors cursor-pointer",
@@ -1355,174 +1646,6 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1355
1646
  )
1356
1647
  ] });
1357
1648
  }
1358
-
1359
- // ../../showcase/src/components/Calendar/calendar-utils.ts
1360
- var MONTH_NAMES = [
1361
- "January",
1362
- "February",
1363
- "March",
1364
- "April",
1365
- "May",
1366
- "June",
1367
- "July",
1368
- "August",
1369
- "September",
1370
- "October",
1371
- "November",
1372
- "December"
1373
- ];
1374
- var MONTH_NAMES_SHORT = [
1375
- "Jan",
1376
- "Feb",
1377
- "Mar",
1378
- "Apr",
1379
- "May",
1380
- "Jun",
1381
- "Jul",
1382
- "Aug",
1383
- "Sep",
1384
- "Oct",
1385
- "Nov",
1386
- "Dec"
1387
- ];
1388
- var DAY_NAMES_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1389
- var DAY_NAMES_MIN = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
1390
- function getOrderedDayNames(weekStartsOn) {
1391
- if (weekStartsOn === 1) return [...DAY_NAMES_MIN.slice(1), DAY_NAMES_MIN[0]];
1392
- return DAY_NAMES_MIN;
1393
- }
1394
- function getOrderedDayNamesShort(weekStartsOn) {
1395
- if (weekStartsOn === 1) return [...DAY_NAMES_SHORT.slice(1), DAY_NAMES_SHORT[0]];
1396
- return DAY_NAMES_SHORT;
1397
- }
1398
- function isSameDay2(a, b) {
1399
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1400
- }
1401
- function isSameMonth(a, b) {
1402
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
1403
- }
1404
- function isToday(d) {
1405
- return isSameDay2(d, /* @__PURE__ */ new Date());
1406
- }
1407
- function isWeekend(d) {
1408
- const day = d.getDay();
1409
- return day === 0 || day === 6;
1410
- }
1411
- function startOfDay(d) {
1412
- return new Date(d.getFullYear(), d.getMonth(), d.getDate());
1413
- }
1414
- function startOfWeek(d, weekStartsOn = 0) {
1415
- const date = new Date(d);
1416
- const day = date.getDay();
1417
- const diff = (day - weekStartsOn + 7) % 7;
1418
- date.setDate(date.getDate() - diff);
1419
- return startOfDay(date);
1420
- }
1421
- function endOfWeek(d, weekStartsOn = 0) {
1422
- const start = startOfWeek(d, weekStartsOn);
1423
- const end = new Date(start);
1424
- end.setDate(end.getDate() + 6);
1425
- end.setHours(23, 59, 59, 999);
1426
- return end;
1427
- }
1428
- function addDays2(d, n) {
1429
- const result = new Date(d);
1430
- result.setDate(result.getDate() + n);
1431
- return result;
1432
- }
1433
- function addMonths(d, n) {
1434
- const result = new Date(d);
1435
- result.setMonth(result.getMonth() + n);
1436
- return result;
1437
- }
1438
- function getMonthGrid(year, month, weekStartsOn = 0) {
1439
- const firstOfMonth = new Date(year, month, 1);
1440
- const gridStart = startOfWeek(firstOfMonth, weekStartsOn);
1441
- const weeks = [];
1442
- const current = new Date(gridStart);
1443
- for (let w = 0; w < 6; w++) {
1444
- const week = [];
1445
- for (let d = 0; d < 7; d++) {
1446
- week.push(new Date(current));
1447
- current.setDate(current.getDate() + 1);
1448
- }
1449
- weeks.push(week);
1450
- }
1451
- return weeks;
1452
- }
1453
- function getWeekDays(date, weekStartsOn = 0) {
1454
- const start = startOfWeek(date, weekStartsOn);
1455
- const days = [];
1456
- for (let i = 0; i < 7; i++) {
1457
- days.push(addDays2(start, i));
1458
- }
1459
- return days;
1460
- }
1461
- function getEventsForDay(events, date) {
1462
- const dayStart = startOfDay(date);
1463
- const dayEnd = new Date(dayStart);
1464
- dayEnd.setHours(23, 59, 59, 999);
1465
- return events.filter((e) => {
1466
- const eStart = new Date(e.start);
1467
- const eEnd = e.end ? new Date(e.end) : eStart;
1468
- return eStart <= dayEnd && eEnd >= dayStart;
1469
- });
1470
- }
1471
- function formatTime(d) {
1472
- const h = d.getHours();
1473
- const m = d.getMinutes();
1474
- const ampm = h >= 12 ? "PM" : "AM";
1475
- const hour = h % 12 || 12;
1476
- return m === 0 ? `${hour} ${ampm}` : `${hour}:${String(m).padStart(2, "0")} ${ampm}`;
1477
- }
1478
- function formatTimeRange(start, end) {
1479
- if (!end) return formatTime(start);
1480
- return `${formatTime(start)} \u2013 ${formatTime(end)}`;
1481
- }
1482
- function formatWeekRange(d, weekStartsOn = 0) {
1483
- const start = startOfWeek(d, weekStartsOn);
1484
- const end = endOfWeek(d, weekStartsOn);
1485
- const sameMonth = start.getMonth() === end.getMonth();
1486
- if (sameMonth) {
1487
- return `${MONTH_NAMES_SHORT[start.getMonth()]} ${start.getDate()} \u2013 ${end.getDate()}, ${start.getFullYear()}`;
1488
- }
1489
- const sameYear = start.getFullYear() === end.getFullYear();
1490
- if (sameYear) {
1491
- return `${MONTH_NAMES_SHORT[start.getMonth()]} ${start.getDate()} \u2013 ${MONTH_NAMES_SHORT[end.getMonth()]} ${end.getDate()}, ${start.getFullYear()}`;
1492
- }
1493
- return `${MONTH_NAMES_SHORT[start.getMonth()]} ${start.getDate()}, ${start.getFullYear()} \u2013 ${MONTH_NAMES_SHORT[end.getMonth()]} ${end.getDate()}, ${end.getFullYear()}`;
1494
- }
1495
- function getOverlappingGroups(events) {
1496
- const timed = events.filter((e) => !e.allDay && e.end).sort((a, b) => a.start.getTime() - b.start.getTime());
1497
- if (timed.length === 0) return [];
1498
- const result = [];
1499
- const columns = [];
1500
- for (const event of timed) {
1501
- const eStart = event.start.getTime();
1502
- const eEnd = (event.end ?? event.start).getTime();
1503
- let placed = false;
1504
- for (let col = 0; col < columns.length; col++) {
1505
- if (columns[col].end <= eStart) {
1506
- columns[col].end = eEnd;
1507
- result.push({ event, column: col, totalColumns: 0 });
1508
- placed = true;
1509
- break;
1510
- }
1511
- }
1512
- if (!placed) {
1513
- columns.push({ end: eEnd });
1514
- result.push({ event, column: columns.length - 1, totalColumns: 0 });
1515
- }
1516
- }
1517
- const totalCols = columns.length;
1518
- for (const item of result) {
1519
- item.totalColumns = totalCols;
1520
- }
1521
- return result;
1522
- }
1523
- function timeToFraction(d) {
1524
- return (d.getHours() * 60 + d.getMinutes()) / (24 * 60);
1525
- }
1526
1649
  var CONTROL_HEIGHT3 = {
1527
1650
  sm: "h-[var(--ds-control-height-sm)]",
1528
1651
  md: "h-[var(--ds-control-height-md)]",
@@ -1533,10 +1656,6 @@ var TEXT_SIZE3 = {
1533
1656
  md: "text-sm",
1534
1657
  lg: "text-base"
1535
1658
  };
1536
- var MONTHS2 = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
1537
- function fmt(d) {
1538
- return `${MONTHS2[d.getMonth()].slice(0, 3)} ${d.getDate()}, ${d.getFullYear()}`;
1539
- }
1540
1659
  var DEFAULT_DATE_RANGE_PRESETS = [
1541
1660
  { label: "Today", range: () => {
1542
1661
  const t = startOfDay(/* @__PURE__ */ new Date());
@@ -1591,7 +1710,8 @@ function DateRangePicker({
1591
1710
  const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
1592
1711
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1593
1712
  const today = useMemo(() => startOfDay(/* @__PURE__ */ new Date()), []);
1594
- const dayNames = getOrderedDayNamesShort(weekStartsOn);
1713
+ const dayNames = weekdayNames(weekStartsOn, "narrow");
1714
+ const dayNamesLong = weekdayNames(weekStartsOn, "long");
1595
1715
  const gridRef = useRef(null);
1596
1716
  const [focusDate, setFocusDate] = useState(() => startOfDay(value.start ?? /* @__PURE__ */ new Date()));
1597
1717
  const [prevOpen, setPrevOpen] = useState(open);
@@ -1605,8 +1725,8 @@ function DateRangePicker({
1605
1725
  }
1606
1726
  const showDay = (d) => {
1607
1727
  setFocusDate(d);
1608
- const monthIndex = (x) => x.getFullYear() * 12 + x.getMonth();
1609
- const offset = monthIndex(d) - monthIndex(viewDate);
1728
+ const monthIndex2 = (x) => x.getFullYear() * 12 + x.getMonth();
1729
+ const offset = monthIndex2(d) - monthIndex2(viewDate);
1610
1730
  if (offset < 0) setViewDate(new Date(d.getFullYear(), d.getMonth(), 1));
1611
1731
  else if (offset > 1) setViewDate(new Date(d.getFullYear(), d.getMonth() - 1, 1));
1612
1732
  };
@@ -1668,7 +1788,7 @@ function DateRangePicker({
1668
1788
  const rangeEnd2 = value.end ?? previewEnd;
1669
1789
  const inRange = (d) => !!(rangeStart && rangeEnd2 && d > rangeStart && d < rangeEnd2);
1670
1790
  const months = [viewDate, addMonths(viewDate, 1)];
1671
- const summary = value.start && value.end ? `${fmt(value.start)} \u2013 ${fmt(value.end)}` : value.start ? `${fmt(value.start)} \u2013 \u2026` : null;
1791
+ const summary = value.start && value.end ? formatDayRange(value.start, value.end) : value.start ? `${formatDayMedium(value.start)} \u2013 \u2026` : null;
1672
1792
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1673
1793
  label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1674
1794
  control.requiredNote,
@@ -1735,23 +1855,15 @@ function DateRangePicker({
1735
1855
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1736
1856
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between bg-[var(--ds-bg-muted)] rounded-tr-[var(--ds-radius-md)] px-ds-3 py-ds-2", children: [
1737
1857
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: () => pageMonths(-1), "aria-label": "Previous month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "rotate-180" }) }),
1738
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: [
1739
- MONTHS2[months[0].getMonth()],
1740
- " ",
1741
- months[0].getFullYear(),
1742
- " \u2013 ",
1743
- MONTHS2[months[1].getMonth()],
1744
- " ",
1745
- months[1].getFullYear()
1746
- ] }),
1858
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthRange(months[0], months[1]) }),
1747
1859
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: () => pageMonths(1), "aria-label": "Next month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) })
1748
1860
  ] }),
1749
- /* @__PURE__ */ jsx("div", { ref: gridRef, onKeyDown: grid.onKeyDown, className: "flex gap-ds-4 px-ds-3 py-ds-2", onMouseLeave: () => setHover(null), children: months.map((m) => /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": `${MONTHS2[m.getMonth()]} ${m.getFullYear()}`, className: "w-[224px] flex flex-col gap-ds-0.5", children: [
1750
- /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: dayNames.map((d) => /* @__PURE__ */ jsx("div", { role: "columnheader", className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, d)) }),
1861
+ /* @__PURE__ */ jsx("div", { ref: gridRef, onKeyDown: grid.onKeyDown, className: "flex gap-ds-4 px-ds-3 py-ds-2", onMouseLeave: () => setHover(null), children: months.map((m) => /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": formatMonthYear(m), className: "w-[224px] flex flex-col gap-ds-0.5", children: [
1862
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: dayNames.map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": dayNamesLong[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1751
1863
  getMonthGrid(m.getFullYear(), m.getMonth(), weekStartsOn).map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5", children: week.map((d) => {
1752
1864
  if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
1753
- const isStart = !!rangeStart && isSameDay2(d, rangeStart);
1754
- const isEnd = !!rangeEnd2 && isSameDay2(d, rangeEnd2);
1865
+ const isStart = !!rangeStart && isSameDay(d, rangeStart);
1866
+ const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
1755
1867
  const between = inRange(d);
1756
1868
  const dis = isDisabledDay(d);
1757
1869
  return /* @__PURE__ */ jsx(
@@ -1764,17 +1876,17 @@ function DateRangePicker({
1764
1876
  "aria-disabled": dis || void 0,
1765
1877
  onClick: () => selectDay(d),
1766
1878
  onFocus: () => {
1767
- if (!isSameDay2(d, focusDate)) setFocusDate(d);
1879
+ if (!isSameDay(d, focusDate)) setFocusDate(d);
1768
1880
  },
1769
1881
  onMouseEnter: () => setHover(d),
1770
1882
  "aria-selected": isStart || isEnd || between,
1771
- "aria-current": isSameDay2(d, today) ? "date" : void 0,
1772
- "aria-label": fmt(d),
1883
+ "aria-current": isSameDay(d, today) ? "date" : void 0,
1884
+ "aria-label": formatDayLong(d),
1773
1885
  className: cn(
1774
1886
  "w-full aspect-square flex items-center justify-center text-xs",
1775
1887
  "transition-colors cursor-pointer",
1776
1888
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
1777
- isStart || isEnd ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : isSameDay2(d, today) ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-primary)] text-[var(--ds-accent-primary)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1889
+ isStart || isEnd ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : isSameDay(d, today) ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-primary)] text-[var(--ds-accent-primary)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1778
1890
  dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1779
1891
  ),
1780
1892
  children: d.getDate()
@@ -6393,593 +6505,755 @@ function SegmentedControl({
6393
6505
  }
6394
6506
  );
6395
6507
  }
6396
- function EventPopover({ event, category, open, anchorRef }) {
6397
- const floatingRef = useRef(null);
6398
- const pos = usePosition(anchorRef, floatingRef, open, {
6399
- side: "top",
6400
- align: "center",
6401
- offset: 8,
6402
- flip: true
6403
- });
6404
- if (!open) return null;
6405
- const color = category?.color ?? "var(--ds-accent-primary)";
6406
- return createPortal(
6407
- /* @__PURE__ */ jsxs(
6408
- "div",
6508
+
6509
+ // ../../showcase/src/components/Calendar/event-colours.ts
6510
+ function eventColours(category) {
6511
+ const colour = category?.color;
6512
+ if (colour === void 0 || colour === "accent" || colour === "var(--ds-accent-primary)") return ACCENT;
6513
+ if (colour === "neutral") return NEUTRAL;
6514
+ if (typeof colour === "number") return Number.isInteger(colour) && colour >= 1 ? supporting(colour) : NEUTRAL;
6515
+ const named = SUPPORTING_TOKEN.exec(colour.trim());
6516
+ if (named) return supporting(Number(named[1]));
6517
+ const chart = CHART_TOKEN.exec(colour.trim());
6518
+ if (chart) return Number(chart[1]) === 1 ? ACCENT : supporting(Number(chart[1]) - 1);
6519
+ return { ...NEUTRAL, edge: colour };
6520
+ }
6521
+ var SUPPORTING_TOKEN = /^var\(\s*--ds-palette-(\d+)-[a-z-]+\s*\)$/;
6522
+ var CHART_TOKEN = /^var\(\s*--ds-chart-(\d+)\s*\)$/;
6523
+ var supporting = (n) => ({
6524
+ fill: `var(--ds-palette-${n}-bg, ${NEUTRAL.fill})`,
6525
+ text: `var(--ds-palette-${n}-text, ${NEUTRAL.text})`,
6526
+ edge: `var(--ds-palette-${n}-chart, ${NEUTRAL.edge})`
6527
+ });
6528
+ var ACCENT = {
6529
+ fill: "var(--ds-accent-bg, var(--ds-accent-subtle))",
6530
+ text: "var(--ds-accent-text, var(--ds-text-primary))",
6531
+ edge: "var(--ds-chart-1)"
6532
+ };
6533
+ var NEUTRAL = {
6534
+ fill: "var(--ds-bg-muted)",
6535
+ text: "var(--ds-text-primary)",
6536
+ edge: "var(--ds-input-border)"
6537
+ };
6538
+ function EventBar({ event, category, compact, onOpen, inert, showTime = true, continuesBefore, continuesAfter, className, style }) {
6539
+ const ref = useRef(null);
6540
+ const colours = eventColours(category);
6541
+ const name = eventName(event, category);
6542
+ const look = cn(
6543
+ "border-[length:var(--ds-border-width)] border-solid rounded-[var(--ds-radius-sm)]",
6544
+ continuesBefore && "rounded-s-none border-s-0",
6545
+ continuesAfter && "rounded-e-none border-e-0"
6546
+ );
6547
+ const paint = { backgroundColor: colours.fill, color: colours.text, borderColor: colours.edge, ...style };
6548
+ if (inert) {
6549
+ return /* @__PURE__ */ jsx(
6550
+ "span",
6409
6551
  {
6410
- ref: floatingRef,
6411
- className: cn(
6412
- "fixed z-[var(--ds-z-popover)] w-64 pointer-events-none",
6413
- "bg-[var(--ds-bg-surface-elevated)]",
6414
- "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
6415
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6416
- "rounded-[var(--ds-radius-md)]",
6417
- "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
6418
- "p-ds-3 space-y-ds-2"
6419
- ),
6420
- style: { top: pos.top, left: pos.left },
6421
- role: "tooltip",
6422
- children: [
6423
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-ds-2", children: [
6424
- /* @__PURE__ */ jsx("p", { className: "font-[var(--ds-weight-heading)] text-sm text-[var(--ds-text-primary)] leading-snug", children: event.title }),
6425
- category && /* @__PURE__ */ jsxs(Badge, { variant: "outline", size: "sm", className: "shrink-0", children: [
6426
- /* @__PURE__ */ jsx(
6427
- "span",
6428
- {
6429
- className: "inline-block w-1.5 h-1.5 rounded-[var(--ds-radius-full)] mr-ds-1",
6430
- style: { backgroundColor: color }
6431
- }
6432
- ),
6433
- category.label
6434
- ] })
6435
- ] }),
6436
- /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-secondary)]", children: event.allDay ? "All day" : formatTimeRange(event.start, event.end) }),
6437
- event.location && /* @__PURE__ */ jsxs("p", { className: "text-xs text-[var(--ds-text-muted)] flex items-center gap-ds-1", children: [
6438
- /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
6439
- /* @__PURE__ */ jsx("path", { d: "M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" }),
6440
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "10", r: "3" })
6441
- ] }),
6442
- event.location
6443
- ] }),
6444
- event.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)] line-clamp-2", children: event.description })
6445
- ]
6552
+ className: cn("block w-full text-left text-xs leading-tight px-ds-1.5 py-ds-0.5 truncate", look, className),
6553
+ style: paint,
6554
+ children: event.title
6446
6555
  }
6447
- ),
6448
- document.body
6449
- );
6450
- }
6451
- function EventBar({ event, category, compact, onClick, className }) {
6452
- const [hovered, setHovered] = useState(false);
6453
- const barRef = useRef(null);
6454
- const hoverTimeout = useRef(void 0);
6455
- const color = category?.color ?? "var(--ds-accent-primary)";
6456
- const handleMouseEnter = () => {
6457
- hoverTimeout.current = setTimeout(() => setHovered(true), 300);
6458
- };
6459
- const handleMouseLeave = () => {
6460
- clearTimeout(hoverTimeout.current);
6461
- setHovered(false);
6462
- };
6556
+ );
6557
+ }
6463
6558
  if (compact) {
6464
6559
  return /* @__PURE__ */ jsx(
6465
6560
  "button",
6466
6561
  {
6467
- ref: barRef,
6468
- className: cn(
6469
- "w-1.5 h-1.5 rounded-[var(--ds-radius-full)] shrink-0 cursor-pointer",
6470
- className
6471
- ),
6472
- style: { backgroundColor: color },
6473
- onMouseEnter: handleMouseEnter,
6474
- onMouseLeave: handleMouseLeave,
6562
+ ref,
6563
+ type: "button",
6564
+ className: cn("w-1.5 h-1.5 rounded-[var(--ds-radius-full)] shrink-0 cursor-pointer", className),
6565
+ style: { backgroundColor: colours.edge },
6475
6566
  onClick: (e) => {
6476
6567
  e.stopPropagation();
6477
- onClick?.(event);
6568
+ onOpen?.(event, e.currentTarget);
6478
6569
  },
6479
- "aria-label": event.title,
6480
- children: /* @__PURE__ */ jsx(
6481
- EventPopover,
6482
- {
6483
- event,
6484
- category,
6485
- open: hovered,
6486
- anchorRef: barRef
6487
- }
6488
- )
6570
+ "aria-label": name
6489
6571
  }
6490
6572
  );
6491
6573
  }
6492
6574
  return /* @__PURE__ */ jsxs(
6493
6575
  "button",
6494
6576
  {
6495
- ref: barRef,
6577
+ ref,
6578
+ type: "button",
6579
+ "data-calendar-event": event.id,
6496
6580
  className: cn(
6497
- "w-full text-left text-xs leading-tight px-ds-1.5 py-ds-0.5 rounded-[var(--ds-radius-sm)]",
6498
- "truncate cursor-pointer transition-opacity hover:opacity-80",
6581
+ "w-full text-left text-xs leading-tight px-ds-1.5 py-ds-0.5",
6582
+ look,
6583
+ "truncate cursor-pointer transition-[filter] hover:brightness-95",
6499
6584
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
6500
6585
  className
6501
6586
  ),
6502
- style: {
6503
- backgroundColor: `color-mix(in oklch, ${color} 18%, transparent)`,
6504
- color,
6505
- borderLeft: `2px solid ${color}`
6506
- },
6507
- onMouseEnter: handleMouseEnter,
6508
- onMouseLeave: handleMouseLeave,
6587
+ style: paint,
6509
6588
  onClick: (e) => {
6510
6589
  e.stopPropagation();
6511
- onClick?.(event);
6590
+ onOpen?.(event, e.currentTarget);
6512
6591
  },
6513
- title: event.title,
6592
+ "aria-label": name,
6514
6593
  children: [
6515
- !event.allDay && /* @__PURE__ */ jsx("span", { className: "font-medium mr-ds-1 opacity-70", children: formatTime(event.start) }),
6516
- event.title,
6517
- /* @__PURE__ */ jsx(
6518
- EventPopover,
6519
- {
6520
- event,
6521
- category,
6522
- open: hovered,
6523
- anchorRef: barRef
6524
- }
6525
- )
6594
+ showTime && !event.allDay && /* @__PURE__ */ jsx("span", { className: "font-medium mr-ds-1", children: formatTime(event.start) }),
6595
+ event.title
6526
6596
  ]
6527
6597
  }
6528
6598
  );
6529
6599
  }
6530
- var MAX_VISIBLE_EVENTS = 3;
6531
- function OverflowPopover({
6532
- events,
6533
- categories,
6534
- date,
6535
- onEventClick
6536
- }) {
6537
- const [open, setOpen] = useState(false);
6538
- const triggerRef = useRef(null);
6539
- const floatingRef = useRef(null);
6540
- const pos = usePosition(triggerRef, floatingRef, open, {
6541
- side: "bottom",
6542
- align: "start",
6543
- offset: 4,
6544
- flip: true
6545
- });
6546
- const { isTop, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: floatingRef, close: () => setOpen(false), tabExits: "edges" });
6600
+ function eventName(event, category) {
6601
+ return [event.title, formatEventWhen(event, true), category?.label].filter(Boolean).join(", ");
6602
+ }
6603
+ var CALENDAR_LAYER = "data-calendar-layer";
6604
+ function EventCard({ event, category, anchor, actions, onClose, fallbackFocus }) {
6605
+ const anchorRef = useMemo(() => ({ current: anchor }), [anchor]);
6606
+ const panelRef = useRef(null);
6607
+ const titleId = useId();
6608
+ const pos = usePosition(anchorRef, panelRef, true, { side: "bottom", align: "start", offset: 4, flip: true });
6609
+ const { isTop, returnFocus } = usePopupFocus(true, { trigger: anchorRef, panel: panelRef, close: onClose, tabExits: "edges" });
6610
+ const colours = eventColours(category);
6611
+ const [attendeeScroll, setAttendeeScroll] = useState(null);
6612
+ useKeyboardScroll(attendeeScroll, "Attendees");
6613
+ const closeRef = useLatest(onClose);
6614
+ useEffect(() => {
6615
+ const raf = requestAnimationFrame(() => {
6616
+ const panel = panelRef.current;
6617
+ if (!panel) return;
6618
+ const actions2 = Array.from(panel.querySelectorAll("[data-card-action]"));
6619
+ const safe = actions2.find((el) => el.getAttribute("data-variant") !== "destructive");
6620
+ if (safe) safe.focus();
6621
+ else if (actions2.length) panel.focus();
6622
+ else focusInitial(panel);
6623
+ });
6624
+ return () => cancelAnimationFrame(raf);
6625
+ }, [anchor]);
6547
6626
  useEffect(() => {
6548
- if (!open) return;
6549
- const raf = requestAnimationFrame(() => focusInitial(floatingRef.current));
6550
6627
  const onKey = (e) => {
6551
6628
  if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
6552
6629
  e.preventDefault();
6553
6630
  returnFocus();
6554
- setOpen(false);
6631
+ closeRef.current();
6555
6632
  };
6556
6633
  const onDown = (e) => {
6557
- if (!triggerRef.current?.contains(e.target)) setOpen(false);
6634
+ if (!isTop()) return;
6635
+ const t = e.target;
6636
+ if (!(t instanceof Node) || panelRef.current?.contains(t) || anchor.contains(t)) return;
6637
+ if (t instanceof Element && t.closest(`[${CALENDAR_LAYER}]`)) return;
6638
+ closeRef.current();
6558
6639
  };
6559
6640
  document.addEventListener("keydown", onKey);
6560
- document.addEventListener("mousedown", onDown);
6641
+ document.addEventListener("pointerdown", onDown, true);
6561
6642
  return () => {
6562
- cancelAnimationFrame(raf);
6563
6643
  document.removeEventListener("keydown", onKey);
6564
- document.removeEventListener("mousedown", onDown);
6644
+ document.removeEventListener("pointerdown", onDown, true);
6565
6645
  };
6566
- }, [open, isTop, returnFocus]);
6567
- return /* @__PURE__ */ jsxs(Fragment, { children: [
6646
+ }, [anchor, isTop, returnFocus, closeRef]);
6647
+ const run = (action) => {
6648
+ const list = anchor.closest(`[${CALENDAR_LAYER}]`);
6649
+ returnFocus();
6650
+ onClose();
6651
+ action.onClick(event);
6652
+ requestAnimationFrame(() => {
6653
+ const now = document.activeElement;
6654
+ if (now && now !== document.body && now.isConnected) return;
6655
+ if (list?.isConnected && focusInitial(list)) return;
6656
+ fallbackFocus?.()?.focus();
6657
+ });
6658
+ };
6659
+ return createPortal(
6568
6660
  /* @__PURE__ */ jsxs(
6569
- "button",
6661
+ "div",
6570
6662
  {
6571
- ref: triggerRef,
6572
- "aria-haspopup": "dialog",
6573
- "aria-expanded": open,
6574
- className: "text-2xs text-[var(--ds-accent-primary)] font-medium cursor-pointer hover:underline px-ds-1.5",
6575
- onClick: (e) => {
6576
- e.stopPropagation();
6577
- setOpen(!open);
6578
- },
6663
+ ref: panelRef,
6664
+ role: "dialog",
6665
+ "aria-labelledby": titleId,
6666
+ ...{ [CALENDAR_LAYER]: "" },
6667
+ "data-calendar-card": event.id,
6668
+ tabIndex: -1,
6669
+ className: cn(
6670
+ "fixed z-[var(--ds-z-popover)] w-72 max-w-[calc(100vw-2*var(--ds-space-3))] outline-none",
6671
+ "bg-[var(--ds-bg-surface-elevated)]",
6672
+ "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
6673
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6674
+ "rounded-[var(--ds-radius-md)]",
6675
+ "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
6676
+ "p-ds-3 space-y-ds-2"
6677
+ ),
6678
+ style: { top: pos.top, left: pos.left },
6579
6679
  children: [
6580
- "+",
6581
- events.length,
6582
- " more"
6680
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-ds-2", children: [
6681
+ /* @__PURE__ */ jsx("h3", { id: titleId, className: "font-[var(--ds-weight-heading)] text-sm text-[var(--ds-text-primary)] leading-snug min-w-0 break-words", children: event.title }),
6682
+ /* @__PURE__ */ jsx(
6683
+ Button,
6684
+ {
6685
+ variant: "ghost",
6686
+ size: "icon-sm",
6687
+ "aria-label": "Close",
6688
+ ...{ [OVERLAY_CLOSE_ATTR]: "" },
6689
+ onClick: () => {
6690
+ returnFocus();
6691
+ onClose();
6692
+ },
6693
+ className: "-mt-ds-1 -mr-ds-1 shrink-0",
6694
+ children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 })
6695
+ }
6696
+ )
6697
+ ] }),
6698
+ category && /* @__PURE__ */ jsxs("p", { className: "inline-flex items-center gap-ds-1.5 text-xs text-[var(--ds-text-secondary)]", children: [
6699
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-block w-2 h-2 rounded-[var(--ds-radius-full)]", style: { backgroundColor: colours.edge } }),
6700
+ category.label
6701
+ ] }),
6702
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-[var(--ds-text-secondary)] tabular-nums", children: [
6703
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: formatEventWhen(event) }),
6704
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: formatEventWhen(event, true) })
6705
+ ] }),
6706
+ event.location && /* @__PURE__ */ jsxs("p", { className: "text-xs text-[var(--ds-text-secondary)] flex items-start gap-ds-1", children: [
6707
+ /* @__PURE__ */ jsx(Icon, { name: "map-pin", size: 14, className: "shrink-0 mt-px" }),
6708
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: event.location })
6709
+ ] }),
6710
+ event.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-secondary)] whitespace-pre-line break-words", children: event.description }),
6711
+ event.attendees && event.attendees.length > 0 && /* @__PURE__ */ jsx("div", { ref: setAttendeeScroll, className: "max-h-32 overflow-y-auto", children: /* @__PURE__ */ jsx("ul", { "aria-label": "Attendees", className: "space-y-ds-1", children: event.attendees.map((person, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-ds-2 text-xs text-[var(--ds-text-primary)]", children: [
6712
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "shrink-0", children: /* @__PURE__ */ jsx(Avatar, { size: "sm", initials: person.initials ?? person.name }) }),
6713
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: person.name })
6714
+ ] }, `${person.name}-${i}`)) }) }),
6715
+ actions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-ds-2 pt-ds-1", children: actions.map((action) => /* @__PURE__ */ jsx(Button, { "data-card-action": "", size: "sm", variant: action.variant ?? "secondary", iconLeft: action.iconLeft, onClick: () => run(action), children: action.label }, action.label)) })
6583
6716
  ]
6584
6717
  }
6585
6718
  ),
6586
- open && createPortal(
6587
- /* @__PURE__ */ jsxs(
6588
- "div",
6589
- {
6590
- ref: floatingRef,
6591
- role: "dialog",
6592
- "aria-label": date.toLocaleDateString(formatLocale(), { weekday: "long", month: "long", day: "numeric" }),
6593
- className: cn(
6594
- "fixed z-[var(--ds-z-popover)] w-56 max-h-48 overflow-y-auto",
6595
- "bg-[var(--ds-bg-surface-elevated)]",
6596
- "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
6597
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6598
- "rounded-[var(--ds-radius-md)]",
6599
- "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
6600
- "p-ds-2 space-y-ds-1"
6601
- ),
6602
- style: { top: pos.top, left: pos.left },
6603
- onClick: (e) => e.stopPropagation(),
6604
- onMouseDown: (e) => e.stopPropagation(),
6605
- children: [
6606
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-1", children: date.toLocaleDateString(formatLocale(), { weekday: "short", month: "short", day: "numeric" }) }),
6607
- events.map((event) => /* @__PURE__ */ jsx(
6608
- EventBar,
6609
- {
6610
- event,
6611
- category: categories[event.category ?? ""],
6612
- onClick: onEventClick
6613
- },
6614
- event.id
6615
- ))
6616
- ]
6617
- }
6618
- ),
6619
- document.body
6620
- )
6621
- ] });
6622
- }
6623
- function MonthView({
6624
- date,
6625
- events,
6626
- categories,
6627
- selectedDate,
6628
- onDayClick,
6629
- onEventClick,
6630
- weekStartsOn
6631
- }) {
6632
- const weeks = useMemo(
6633
- () => getMonthGrid(date.getFullYear(), date.getMonth(), weekStartsOn),
6634
- [date, weekStartsOn]
6719
+ document.body
6635
6720
  );
6636
- const dayNames = useMemo(() => getOrderedDayNames(weekStartsOn), [weekStartsOn]);
6637
- const currentMonth = date.getMonth();
6638
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
6639
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: dayNames.map((name) => /* @__PURE__ */ jsxs(
6721
+ }
6722
+ function DayList({ day, events, categories, anchor, onOpenEvent, onNewEvent, onClose }) {
6723
+ const anchorRef = useMemo(() => ({ current: anchor }), [anchor]);
6724
+ const panelRef = useRef(null);
6725
+ const pos = usePosition(anchorRef, panelRef, true, { side: "bottom", align: "start", offset: 4, flip: true });
6726
+ const { isTop, returnFocus } = usePopupFocus(true, { trigger: anchorRef, panel: panelRef, close: onClose, tabExits: "edges" });
6727
+ const closeRef = useLatest(onClose);
6728
+ useEffect(() => {
6729
+ const raf = requestAnimationFrame(() => focusInitial(panelRef.current));
6730
+ return () => cancelAnimationFrame(raf);
6731
+ }, [anchor]);
6732
+ useEffect(() => {
6733
+ const onKey = (e) => {
6734
+ if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
6735
+ e.preventDefault();
6736
+ returnFocus();
6737
+ closeRef.current();
6738
+ };
6739
+ const onDown = (e) => {
6740
+ if (!isTop()) return;
6741
+ const t = e.target;
6742
+ if (!(t instanceof Node) || panelRef.current?.contains(t) || anchor.contains(t)) return;
6743
+ if (t instanceof Element && t.closest(`[${CALENDAR_LAYER}]`)) return;
6744
+ closeRef.current();
6745
+ };
6746
+ document.addEventListener("keydown", onKey);
6747
+ document.addEventListener("pointerdown", onDown, true);
6748
+ return () => {
6749
+ document.removeEventListener("keydown", onKey);
6750
+ document.removeEventListener("pointerdown", onDown, true);
6751
+ };
6752
+ }, [anchor, isTop, returnFocus, closeRef]);
6753
+ return createPortal(
6754
+ /* @__PURE__ */ jsxs(
6640
6755
  "div",
6641
6756
  {
6642
- className: "text-center text-xs font-medium text-[var(--ds-text-muted)] py-ds-2 px-ds-1",
6757
+ ref: panelRef,
6758
+ role: "dialog",
6759
+ "aria-label": formatDayLong(day),
6760
+ ...{ [CALENDAR_LAYER]: "" },
6761
+ className: cn(
6762
+ "fixed z-[var(--ds-z-popover)] w-60 max-h-72 overflow-y-auto",
6763
+ "bg-[var(--ds-bg-surface-elevated)]",
6764
+ "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
6765
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6766
+ "rounded-[var(--ds-radius-md)]",
6767
+ "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
6768
+ "p-ds-2 space-y-ds-1"
6769
+ ),
6770
+ style: { top: pos.top, left: pos.left },
6771
+ onClick: (e) => e.stopPropagation(),
6643
6772
  children: [
6644
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: name }),
6645
- /* @__PURE__ */ jsx("span", { className: "sm:hidden", children: name[0] })
6773
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-1", children: formatDayShort(day) }),
6774
+ events.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-secondary)] px-ds-1", children: "No events" }),
6775
+ events.map((event) => /* @__PURE__ */ jsx(EventBar, { event, category: categories[event.category ?? ""], onOpen: onOpenEvent }, event.id)),
6776
+ onNewEvent && /* @__PURE__ */ jsx(
6777
+ Button,
6778
+ {
6779
+ variant: "ghost",
6780
+ size: "sm",
6781
+ iconLeft: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 14 }),
6782
+ className: "w-full justify-start mt-ds-1",
6783
+ onClick: () => {
6784
+ returnFocus();
6785
+ onClose();
6786
+ onNewEvent(day);
6787
+ },
6788
+ children: "New event"
6789
+ }
6790
+ )
6646
6791
  ]
6647
- },
6648
- name
6649
- )) }),
6650
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 flex-1 auto-rows-fr", children: weeks.map(
6651
- (week, wi) => week.map((day, di) => {
6652
- const dayEvents = getEventsForDay(events, day);
6653
- const isCurrentMonth = day.getMonth() === currentMonth;
6654
- const today = isToday(day);
6655
- const selected = selectedDate && isSameDay2(day, selectedDate);
6656
- const weekend = isWeekend(day);
6657
- const visibleEvents = dayEvents.slice(0, MAX_VISIBLE_EVENTS);
6658
- const overflowEvents = dayEvents.slice(MAX_VISIBLE_EVENTS);
6659
- const lastCol = di === 6;
6792
+ }
6793
+ ),
6794
+ document.body
6795
+ );
6796
+ }
6797
+
6798
+ // ../../showcase/src/components/Calendar/styles.ts
6799
+ var TODAY_CIRCLE = "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)] font-bold";
6800
+ var LANES = 3;
6801
+ var LANE_H = 20;
6802
+ var LANE_GAP = 4;
6803
+ var NUMBER_ROW = 30;
6804
+ var INSET = "var(--ds-space-1)";
6805
+ function MonthView({ date, events, categories, selectedDate, onDayClick, canStartEvent, onOpenEvent, onNavigate, weekStartsOn }) {
6806
+ const weeks = useMemo(() => getMonthGrid(date.getFullYear(), date.getMonth(), weekStartsOn), [date, weekStartsOn]);
6807
+ const phone = useBreakpoint("sm");
6808
+ const names = { short: weekdayNames(weekStartsOn, "short"), narrow: weekdayNames(weekStartsOn, "narrow"), long: weekdayNames(weekStartsOn, "long") };
6809
+ const layouts = useMemo(() => weeks.map((week) => layoutWeek(events, week, LANES)), [weeks, events]);
6810
+ const currentMonth = date.getMonth();
6811
+ const gridRef = useRef(null);
6812
+ const [list, setList] = useState(null);
6813
+ const inMonth = (d) => !!d && d.getMonth() === currentMonth && d.getFullYear() === date.getFullYear();
6814
+ const fallback = inMonth(selectedDate) ? selectedDate : inMonth(/* @__PURE__ */ new Date()) ? /* @__PURE__ */ new Date() : new Date(date.getFullYear(), currentMonth, 1);
6815
+ const [activeDay, setActiveDay] = useState(fallback);
6816
+ const active = inMonth(activeDay) ? activeDay : fallback;
6817
+ const openList = (day, anchor) => setList({ day, anchor });
6818
+ const choose = (day, anchor) => phone ? openList(day, anchor) : onDayClick(day);
6819
+ const grid = useGridKeyboard({
6820
+ container: gridRef,
6821
+ active,
6822
+ setActive: (d) => {
6823
+ setActiveDay(d);
6824
+ if (!inMonth(d)) onNavigate(d);
6825
+ },
6826
+ keyOf: dayKey,
6827
+ move: (from, e) => moveDay(from, e.key, e.shiftKey, weekStartsOn),
6828
+ onActivate: (day, e) => choose(day, e.currentTarget.querySelector(`[data-grid-key="${dayKey(day)}"]`) ?? e.target)
6829
+ });
6830
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
6831
+ /* @__PURE__ */ jsxs("div", { ref: gridRef, role: "grid", "aria-label": formatMonthYear(date), onKeyDown: grid.onKeyDown, className: "flex flex-col flex-1 min-h-0", children: [
6832
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: names.short.map((name, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": names.long[i], className: "text-center text-xs font-medium text-[var(--ds-text-muted)] py-ds-2 px-ds-1", children: phone ? names.narrow[i] : name }, i)) }),
6833
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col flex-1", children: weeks.map((week, wi) => {
6834
+ const layout = layouts[wi];
6660
6835
  const lastRow = wi === weeks.length - 1;
6661
- return /* @__PURE__ */ jsxs(
6662
- "div",
6663
- {
6664
- className: cn(
6665
- "min-h-[80px] sm:min-h-[100px] border-[var(--ds-border-subtle)]",
6666
- !lastRow && "border-b-[length:var(--ds-divider-width)]",
6667
- !lastCol && "border-r-[length:var(--ds-divider-width)]",
6668
- "p-ds-1 flex flex-col cursor-pointer transition-colors",
6669
- "hover:bg-[var(--ds-bg-muted)]",
6670
- !isCurrentMonth && "opacity-40",
6671
- selected && "bg-[var(--ds-accent-whisper)]",
6672
- weekend && isCurrentMonth && "bg-[var(--ds-bg-muted)]/30"
6673
- ),
6674
- onClick: () => onDayClick?.(day),
6675
- children: [
6676
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-ds-0.5", children: /* @__PURE__ */ jsx(
6677
- "span",
6678
- {
6679
- className: cn(
6680
- "text-xs w-6 h-6 flex items-center justify-center rounded-[var(--ds-radius-full)]",
6681
- today ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)] font-bold" : "text-[var(--ds-text-secondary)]"
6682
- ),
6683
- children: day.getDate()
6684
- }
6685
- ) }),
6686
- /* @__PURE__ */ jsxs("div", { className: "hidden sm:flex flex-col gap-ds-0.5 flex-1 overflow-hidden", children: [
6687
- visibleEvents.map((event) => /* @__PURE__ */ jsx(
6688
- EventBar,
6689
- {
6690
- event,
6691
- category: categories[event.category ?? ""],
6692
- onClick: onEventClick
6693
- },
6694
- event.id
6695
- )),
6696
- overflowEvents.length > 0 && /* @__PURE__ */ jsx(
6697
- OverflowPopover,
6698
- {
6699
- events: dayEvents,
6700
- categories,
6701
- date: day,
6702
- onEventClick
6703
- }
6704
- )
6705
- ] }),
6706
- /* @__PURE__ */ jsxs("div", { className: "flex sm:hidden gap-ds-0.5 flex-wrap mt-ds-0.5", children: [
6707
- dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ jsx(
6708
- EventBar,
6709
- {
6710
- event,
6711
- category: categories[event.category ?? ""],
6712
- compact: true,
6713
- onClick: onEventClick
6714
- },
6715
- event.id
6716
- )),
6717
- dayEvents.length > 4 && /* @__PURE__ */ jsxs("span", { className: "text-2xs text-[var(--ds-text-muted)]", children: [
6718
- "+",
6719
- dayEvents.length - 4
6836
+ return /* @__PURE__ */ jsx("div", { role: "row", className: "relative grid grid-cols-7 flex-1", children: week.map((day, di) => {
6837
+ const dayEvents = getEventsForDay(events, day);
6838
+ const isCurrentMonth = day.getMonth() === currentMonth;
6839
+ const today = isToday(day);
6840
+ const selected = !!selectedDate && isSameDay(day, selectedDate);
6841
+ const weekend = isWeekend(day);
6842
+ const more = layout.more[di];
6843
+ const label = phone && dayEvents.length > 0 ? `${formatDayLong(day)}, ${dayEvents.length} ${dayEvents.length === 1 ? "event" : "events"}` : formatDayLong(day);
6844
+ return /* @__PURE__ */ jsxs(
6845
+ "div",
6846
+ {
6847
+ role: "gridcell",
6848
+ "data-grid-key": dayKey(day),
6849
+ tabIndex: grid.tabIndexOf(day),
6850
+ "aria-label": label,
6851
+ "aria-selected": selected,
6852
+ "aria-current": today ? "date" : void 0,
6853
+ onFocus: () => {
6854
+ if (!isSameDay(day, active)) setActiveDay(day);
6855
+ },
6856
+ onClick: (e) => choose(day, e.currentTarget),
6857
+ className: cn(
6858
+ "relative min-h-[80px] sm:min-h-[100px] border-[var(--ds-border-subtle)]",
6859
+ !lastRow && "border-b-[length:var(--ds-divider-width)]",
6860
+ di < 6 && "border-r-[length:var(--ds-divider-width)]",
6861
+ "p-ds-1 flex flex-col cursor-pointer transition-colors",
6862
+ FOCUS_INSET,
6863
+ "hover:bg-[var(--ds-bg-muted)]",
6864
+ !isCurrentMonth && "[&>.gs-day-number]:opacity-40",
6865
+ selected && "bg-[var(--ds-accent-whisper)]",
6866
+ weekend && isCurrentMonth && !selected && "bg-[var(--ds-bg-muted)]/30"
6867
+ ),
6868
+ children: [
6869
+ /* @__PURE__ */ jsx("span", { className: cn("gs-day-number text-xs w-6 h-6 flex items-center justify-center rounded-[var(--ds-radius-full)] shrink-0", today ? TODAY_CIRCLE : "text-[var(--ds-text-secondary)]"), children: day.getDate() }),
6870
+ phone ? (
6871
+ // dots, a day's presence of events: the day itself is what a tap opens (D46)
6872
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "flex gap-ds-0.5 flex-wrap mt-ds-0.5", children: [
6873
+ dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-[var(--ds-radius-full)]", style: { backgroundColor: eventColours(categories[event.category ?? ""]).edge } }, event.id)),
6874
+ dayEvents.length > 4 && /* @__PURE__ */ jsxs("span", { className: "text-2xs text-[var(--ds-text-muted)] leading-none", children: [
6875
+ "+",
6876
+ dayEvents.length - 4
6877
+ ] })
6878
+ ] })
6879
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
6880
+ layout.segments.filter((s) => s.startCol === di).map((s) => /* @__PURE__ */ jsx(
6881
+ EventBar,
6882
+ {
6883
+ event: s.event,
6884
+ category: categories[s.event.category ?? ""],
6885
+ onOpen: onOpenEvent,
6886
+ continuesBefore: s.continuesBefore,
6887
+ continuesAfter: s.continuesAfter,
6888
+ className: "absolute z-[1] flex items-center",
6889
+ style: {
6890
+ top: NUMBER_ROW + s.lane * (LANE_H + LANE_GAP),
6891
+ height: LANE_H,
6892
+ left: s.continuesBefore ? 0 : INSET,
6893
+ width: `calc(${s.span} * (100% + var(--ds-divider-width)) - var(--ds-divider-width) - ${s.continuesBefore ? "0px" : INSET} - ${s.continuesAfter ? "0px" : INSET})`
6894
+ }
6895
+ },
6896
+ `${s.event.id}-${s.startCol}`
6897
+ )),
6898
+ more.length > 0 && /* @__PURE__ */ jsxs(
6899
+ "button",
6900
+ {
6901
+ type: "button",
6902
+ "aria-haspopup": "dialog",
6903
+ "aria-expanded": !!list && isSameDay(list.day, day),
6904
+ className: "absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
6905
+ style: { top: NUMBER_ROW + (LANES - 1) * (LANE_H + LANE_GAP), left: INSET, height: LANE_H },
6906
+ onClick: (e) => {
6907
+ e.stopPropagation();
6908
+ openList(day, e.currentTarget);
6909
+ },
6910
+ children: [
6911
+ "+",
6912
+ more.length,
6913
+ " more"
6914
+ ]
6915
+ }
6916
+ )
6720
6917
  ] })
6721
- ] })
6722
- ]
6723
- },
6724
- `${wi}-${di}`
6725
- );
6726
- })
6727
- ) })
6918
+ ]
6919
+ },
6920
+ dayKey(day)
6921
+ );
6922
+ }) }, dayKey(week[0]));
6923
+ }) })
6924
+ ] }),
6925
+ list && /* @__PURE__ */ jsx(
6926
+ DayList,
6927
+ {
6928
+ day: list.day,
6929
+ events: getEventsForDay(events, list.day),
6930
+ categories,
6931
+ anchor: list.anchor,
6932
+ onOpenEvent,
6933
+ onNewEvent: phone && canStartEvent ? onDayClick : void 0,
6934
+ onClose: () => setList(null)
6935
+ }
6936
+ )
6728
6937
  ] });
6729
6938
  }
6730
6939
  var HOUR_HEIGHT = 40;
6731
6940
  var HOURS = Array.from({ length: 24 }, (_, i) => i);
6732
6941
  var GRID_HEIGHT = HOURS.length * HOUR_HEIGHT;
6733
- function formatHour(h) {
6734
- if (h === 0) return "12 AM";
6735
- if (h < 12) return `${h} AM`;
6736
- if (h === 12) return "12 PM";
6737
- return `${h - 12} PM`;
6738
- }
6739
- function CurrentTimeLine() {
6740
- const [now, setNow] = useState(/* @__PURE__ */ new Date());
6942
+ var MIN_EVENT_PX = 24;
6943
+ var MIN_EVENT_MINUTES = MIN_EVENT_PX / HOUR_HEIGHT * 60;
6944
+ var ALL_DAY_LANES = 3;
6945
+ var LANE_H2 = 20;
6946
+ var LANE_GAP2 = 4;
6947
+ var WORKING_DAY = 8;
6948
+ var HOUR_LABEL_ROOM = 8;
6949
+ var FADE = 24;
6950
+ var MIN_DAY_WIDTH = 100;
6951
+ var hh = (hour) => String(hour).padStart(2, "0");
6952
+ var cellKey = (day, hour) => hour === null ? dayKey(day) : `${dayKey(day)}T${hh(hour)}`;
6953
+ function parseKey(key) {
6954
+ const [d, h] = key.split("T");
6955
+ const [y, m, dd] = d.split("-").map(Number);
6956
+ return { day: new Date(y, m - 1, dd), hour: h === void 0 ? null : Number(h) };
6957
+ }
6958
+ function NowLine() {
6959
+ const [now, setNow] = useState(() => /* @__PURE__ */ new Date());
6741
6960
  useEffect(() => {
6742
6961
  const id = setInterval(() => setNow(/* @__PURE__ */ new Date()), 6e4);
6743
6962
  return () => clearInterval(id);
6744
6963
  }, []);
6745
- const top = timeToFraction(now) * GRID_HEIGHT;
6746
- return /* @__PURE__ */ jsx(
6747
- "div",
6748
- {
6749
- className: "absolute left-0 right-0 z-10 pointer-events-none",
6750
- style: { top },
6751
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
6752
- /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-[var(--ds-radius-full)] bg-[var(--ds-status-error)] -ml-1" }),
6753
- /* @__PURE__ */ jsx("div", { className: "flex-1 h-[2px] bg-[var(--ds-status-error)]" })
6754
- ] })
6755
- }
6756
- );
6757
- }
6758
- function TimedEvent({
6759
- event,
6760
- category,
6761
- column,
6762
- totalColumns,
6763
- onEventClick
6764
- }) {
6765
- const start = new Date(event.start);
6766
- const end = event.end ? new Date(event.end) : new Date(start.getTime() + 60 * 6e4);
6767
- const top = timeToFraction(start) * GRID_HEIGHT;
6768
- const bottom = timeToFraction(end) * GRID_HEIGHT;
6769
- const height = Math.max(bottom - top, 20);
6770
- const color = category?.color ?? "var(--ds-accent-primary)";
6771
- const width = `calc(${100 / totalColumns}% - 2px)`;
6772
- const left = `calc(${column / totalColumns * 100}% + 1px)`;
6964
+ return /* @__PURE__ */ jsx("div", { "data-now-line": "", "aria-hidden": "true", className: "absolute left-0 right-0 z-[2] pointer-events-none", style: { top: timeToFraction(now) * GRID_HEIGHT }, children: /* @__PURE__ */ jsx("div", { className: "relative border-t-2 border-[var(--ds-accent-primary)]", children: /* @__PURE__ */ jsx("span", { className: "absolute -left-1 -top-[5px] box-border w-2 h-2 rounded-[var(--ds-radius-full)] border-4 border-[var(--ds-accent-primary)]" }) }) });
6965
+ }
6966
+ function TimedEvent({ placed, category, onOpen, onFocusEvent }) {
6967
+ const { piece, column, columns } = placed;
6968
+ const { event } = piece;
6969
+ const top = timeToFraction(piece.start) * GRID_HEIGHT;
6970
+ const bottom = piece.end.getHours() === 0 && piece.end.getMinutes() === 0 && piece.end > piece.start ? GRID_HEIGHT : timeToFraction(piece.end) * GRID_HEIGHT;
6971
+ const height = Math.max(bottom - top, MIN_EVENT_PX);
6972
+ const drawnTop = Math.min(top, GRID_HEIGHT - height);
6973
+ const colours = eventColours(category);
6974
+ const end = event.end && event.end > event.start ? event.end : void 0;
6975
+ const cutTop = piece.start > event.start;
6976
+ const cutBottom = !!end && piece.end < end;
6773
6977
  return /* @__PURE__ */ jsxs(
6774
- "div",
6978
+ "button",
6775
6979
  {
6980
+ type: "button",
6981
+ "data-calendar-event": event.id,
6982
+ "aria-label": eventName(event, category),
6776
6983
  className: cn(
6777
- "absolute rounded-[var(--ds-radius-sm)] px-ds-1.5 py-ds-1 text-xs leading-tight",
6778
- "flex flex-col items-start justify-start",
6779
- "overflow-hidden cursor-pointer transition-opacity hover:opacity-80",
6780
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]"
6984
+ // a short event keeps its title whole: no padding above and below it
6985
+ "absolute pointer-events-auto text-left px-ds-1.5 text-xs leading-tight",
6986
+ height > 30 ? "py-ds-1" : "py-0 justify-center",
6987
+ "flex flex-col items-start overflow-hidden cursor-pointer transition-[filter] hover:brightness-95",
6988
+ height > 30 && "justify-start",
6989
+ "border-[length:var(--ds-border-width)] border-solid rounded-[var(--ds-radius-sm)]",
6990
+ cutTop && "rounded-t-none border-t-0",
6991
+ cutBottom && "rounded-b-none border-b-0",
6992
+ "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)] focus-visible:z-[3]"
6781
6993
  ),
6782
6994
  style: {
6783
- top,
6995
+ top: drawnTop,
6784
6996
  height,
6785
- left,
6786
- width,
6787
- backgroundColor: `color-mix(in oklch, ${color} 20%, transparent)`,
6788
- color,
6789
- borderLeft: `2px solid ${color}`
6997
+ left: `calc(${column / columns * 100}% + 1px)`,
6998
+ width: `calc(${100 / columns}% - 2px)`,
6999
+ backgroundColor: colours.fill,
7000
+ color: colours.text,
7001
+ borderColor: colours.edge
6790
7002
  },
6791
7003
  onClick: (e) => {
6792
7004
  e.stopPropagation();
6793
- onEventClick?.(event);
7005
+ onOpen(event, e.currentTarget);
6794
7006
  },
6795
- title: event.title,
6796
- role: "button",
6797
- tabIndex: 0,
7007
+ onFocus: onFocusEvent,
6798
7008
  children: [
6799
- /* @__PURE__ */ jsx("div", { className: "font-medium truncate w-full", children: event.title }),
6800
- height > 30 && /* @__PURE__ */ jsxs("div", { className: "text-2xs opacity-70 truncate w-full", children: [
6801
- formatTime(start),
6802
- " \u2013 ",
6803
- formatTime(end)
6804
- ] })
7009
+ /* @__PURE__ */ jsx("span", { className: "font-medium truncate w-full", children: event.title }),
7010
+ height > 30 && /* @__PURE__ */ jsx("span", { className: "text-2xs truncate w-full tabular-nums", children: formatTimeRange(event.start, end) })
6805
7011
  ]
6806
7012
  }
6807
7013
  );
6808
7014
  }
6809
- function WeekView({
6810
- date,
6811
- events,
6812
- categories,
6813
- selectedDate,
6814
- onDayClick,
6815
- onEventClick,
6816
- weekStartsOn
6817
- }) {
7015
+ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEvent, onNavigate, weekStartsOn }) {
6818
7016
  const days = useMemo(() => getWeekDays(date, weekStartsOn), [date, weekStartsOn]);
6819
- const allDayEvents = useMemo(() => {
6820
- return days.map(
6821
- (day) => getEventsForDay(events, day).filter((e) => e.allDay)
6822
- );
6823
- }, [days, events]);
6824
- const hasAllDay = allDayEvents.some((d) => d.length > 0);
6825
- const containerRef = useRef(null);
7017
+ const names = weekdayNames(weekStartsOn, "short");
7018
+ const barEvents = useMemo(() => events.filter(isBarEvent), [events]);
7019
+ const allDay = useMemo(() => layoutWeek(barEvents, days, ALL_DAY_LANES), [barEvents, days]);
7020
+ const pieces = useMemo(() => days.map((day) => layoutPieces(dayPieces(events, day), MIN_EVENT_MINUTES)), [days, events]);
7021
+ const allDayHeight = Math.max(28, 4 + allDay.lanes * (LANE_H2 + LANE_GAP2) + 2);
7022
+ const rootRef = useRef(null);
7023
+ const scrollerRef = useRef(null);
6826
7024
  const syncingScroll = useRef(false);
6827
7025
  const { ref: edgesRef, measure: measureEdges, fadeProps: edgeFade } = useScrollEdges();
7026
+ const [list, setList] = useState(null);
6828
7027
  const handleScroll = useCallback((e) => {
6829
7028
  if (syncingScroll.current) return;
6830
7029
  syncingScroll.current = true;
6831
7030
  const source = e.target;
6832
- const scrollLeft = source.scrollLeft;
6833
- const panels = containerRef.current?.querySelectorAll("[data-week-scroll]");
6834
- panels?.forEach((panel) => {
6835
- if (panel !== source) panel.scrollLeft = scrollLeft;
7031
+ rootRef.current?.querySelectorAll("[data-week-scroll]").forEach((panel) => {
7032
+ if (panel !== source) panel.scrollLeft = source.scrollLeft;
6836
7033
  });
6837
7034
  syncingScroll.current = false;
6838
7035
  }, []);
6839
7036
  useEffect(() => {
6840
- const container = containerRef.current;
6841
- if (!container) return;
6842
- const panels = container.querySelectorAll("[data-week-scroll]");
6843
- panels.forEach((panel) => panel.addEventListener("scroll", handleScroll));
6844
- return () => {
6845
- panels.forEach((panel) => panel.removeEventListener("scroll", handleScroll));
7037
+ const panels = rootRef.current?.querySelectorAll("[data-week-scroll]");
7038
+ panels?.forEach((panel) => panel.addEventListener("scroll", handleScroll));
7039
+ return () => panels?.forEach((panel) => panel.removeEventListener("scroll", handleScroll));
7040
+ }, [handleScroll]);
7041
+ const starts = pieces.flat().filter((p) => p.piece.start.getTime() === p.piece.event.start.getTime()).map((p) => p.piece.start.getHours() + p.piece.start.getMinutes() / 60);
7042
+ const earliest = Math.floor(Math.min(WORKING_DAY, ...starts));
7043
+ const topOf = (hour) => Math.max(0, hour * HOUR_HEIGHT - HOUR_LABEL_ROOM);
7044
+ const openingHourRef = useLatest(earliest);
7045
+ const opened = useRef(null);
7046
+ const week = dayKey(days[0]);
7047
+ const weekRef = useLatest(week);
7048
+ const [openingHour] = useState(earliest);
7049
+ useLayoutEffect(() => {
7050
+ const scroller = scrollerRef.current;
7051
+ if (!scroller) return;
7052
+ let done = false;
7053
+ const open = () => {
7054
+ if (done || scroller.scrollHeight <= scroller.clientHeight + 1) return;
7055
+ done = true;
7056
+ scroller.scrollTo({ top: topOf(openingHourRef.current) });
7057
+ opened.current = { top: scroller.scrollTop, week: weekRef.current };
6846
7058
  };
6847
- }, [handleScroll, hasAllDay]);
6848
- const MIN_DAY_WIDTH = 100;
7059
+ open();
7060
+ if (done || typeof ResizeObserver === "undefined") return;
7061
+ const ro = new ResizeObserver(() => {
7062
+ open();
7063
+ if (done) ro.disconnect();
7064
+ });
7065
+ ro.observe(scroller);
7066
+ return () => ro.disconnect();
7067
+ }, [openingHour, openingHourRef, weekRef]);
7068
+ useLayoutEffect(() => {
7069
+ const scroller = scrollerRef.current;
7070
+ const o = opened.current;
7071
+ if (!scroller || !o || o.week !== week) return;
7072
+ if (Math.abs(scroller.scrollTop - o.top) > 1) {
7073
+ opened.current = null;
7074
+ return;
7075
+ }
7076
+ if (topOf(earliest) >= o.top) return;
7077
+ scroller.scrollTo({ top: topOf(earliest) });
7078
+ opened.current = { top: scroller.scrollTop, week };
7079
+ }, [earliest, week]);
7080
+ const inWeek = (d) => days.some((day) => isSameDay(day, d));
7081
+ const anchorDay = selectedDate && inWeek(selectedDate) ? selectedDate : days.find(isToday) ?? days[0];
7082
+ const [activeKey, setActiveKey] = useState(() => cellKey(anchorDay, openingHour));
7083
+ const active = inWeek(parseKey(activeKey).day) ? activeKey : cellKey(anchorDay, parseKey(activeKey).hour);
7084
+ const firstVisibleHour = () => Math.min(23, Math.ceil((scrollerRef.current?.scrollTop ?? 0) / HOUR_HEIGHT));
7085
+ const placeInView = (_key, el) => {
7086
+ const scroller = scrollerRef.current;
7087
+ if (scroller && scroller.contains(el) && scroller.scrollHeight > scroller.clientHeight + 1) {
7088
+ const top = el.offsetTop;
7089
+ if (top < scroller.scrollTop) scroller.scrollTop = top;
7090
+ else if (top + el.offsetHeight > scroller.scrollTop + scroller.clientHeight) scroller.scrollTop = top + el.offsetHeight - scroller.clientHeight;
7091
+ } else {
7092
+ el.scrollIntoView?.({ block: "nearest", inline: "nearest" });
7093
+ }
7094
+ const strip = el.closest("[data-week-scroll]");
7095
+ if (strip) {
7096
+ const cell = el.getBoundingClientRect();
7097
+ const box = strip.getBoundingClientRect();
7098
+ if (cell.left - FADE < box.left) strip.scrollLeft -= box.left - (cell.left - FADE);
7099
+ else if (cell.right + FADE > box.right) strip.scrollLeft += cell.right + FADE - box.right;
7100
+ }
7101
+ };
7102
+ const grid = useGridKeyboard({
7103
+ container: rootRef,
7104
+ active,
7105
+ setActive: (key) => {
7106
+ setActiveKey(key);
7107
+ const { day } = parseKey(key);
7108
+ if (!inWeek(day)) onNavigate(day);
7109
+ },
7110
+ keyOf: (key) => key,
7111
+ move: (from, e) => {
7112
+ const { day, hour } = parseKey(from);
7113
+ const col = days.findIndex((d) => isSameDay(d, day));
7114
+ switch (e.key) {
7115
+ case "ArrowUp":
7116
+ return hour === null ? from : hour === 0 ? cellKey(day, null) : cellKey(day, hour - 1);
7117
+ case "ArrowDown":
7118
+ return hour === null ? cellKey(day, firstVisibleHour()) : cellKey(day, Math.min(23, hour + 1));
7119
+ case "ArrowLeft":
7120
+ return col > 0 ? cellKey(days[col - 1], hour) : from;
7121
+ case "ArrowRight":
7122
+ return col >= 0 && col < days.length - 1 ? cellKey(days[col + 1], hour) : from;
7123
+ case "Home":
7124
+ return cellKey(days[0], hour);
7125
+ case "End":
7126
+ return cellKey(days[days.length - 1], hour);
7127
+ case "PageUp":
7128
+ return cellKey(addDays2(day, -7), hour);
7129
+ case "PageDown":
7130
+ return cellKey(addDays2(day, 7), hour);
7131
+ default:
7132
+ return null;
7133
+ }
7134
+ },
7135
+ onActivate: (key) => {
7136
+ const { day, hour } = parseKey(key);
7137
+ onDayClick(day, hour === null ? void 0 : { time: `${hh(hour)}:00` });
7138
+ },
7139
+ scrollIntoView: placeInView
7140
+ });
6849
7141
  const dayGridColumns = `repeat(${days.length}, minmax(${MIN_DAY_WIDTH}px, 1fr))`;
6850
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "flex flex-col h-full overflow-hidden", children: [
7142
+ const stripWidth = MIN_DAY_WIDTH * days.length;
7143
+ const chooseHour = (day, hour) => (e) => {
7144
+ const minutes = e.nativeEvent.offsetY >= HOUR_HEIGHT / 2 ? 30 : 0;
7145
+ onDayClick(day, { time: `${hh(hour)}:${minutes === 30 ? "30" : "00"}` });
7146
+ };
7147
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: "flex flex-col h-full overflow-hidden", onKeyDown: grid.onKeyDown, children: [
6851
7148
  /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
6852
7149
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]" }),
6853
- /* @__PURE__ */ jsx("div", { ref: edgesRef, onScroll: measureEdges, ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6854
- "div",
6855
- {
6856
- className: "grid",
6857
- style: { gridTemplateColumns: dayGridColumns, minWidth: MIN_DAY_WIDTH * days.length },
6858
- children: days.map((day, i) => {
6859
- const today = isToday(day);
6860
- const selected = selectedDate && isSameDay2(day, selectedDate);
6861
- return /* @__PURE__ */ jsxs(
6862
- "div",
6863
- {
6864
- className: cn(
6865
- "text-center py-ds-2 cursor-pointer transition-colors",
6866
- "hover:bg-[var(--ds-bg-muted)]",
6867
- i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]",
6868
- selected && "bg-[var(--ds-accent-whisper)]"
6869
- ),
6870
- onClick: () => onDayClick?.(day),
6871
- children: [
6872
- /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--ds-text-muted)] font-medium", children: day.toLocaleDateString(formatLocale(), { weekday: "short" }) }),
6873
- /* @__PURE__ */ jsx(
6874
- "div",
6875
- {
6876
- className: cn(
6877
- "text-lg font-semibold mt-ds-0.5 mx-auto w-8 h-8 flex items-center justify-center rounded-[var(--ds-radius-full)]",
6878
- today ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-primary)]"
6879
- ),
6880
- children: day.getDate()
6881
- }
6882
- )
6883
- ]
6884
- },
6885
- i
6886
- );
6887
- })
6888
- }
6889
- ) })
7150
+ /* @__PURE__ */ jsx("div", { ref: edgesRef, onScroll: measureEdges, ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx("div", { className: "grid", style: { gridTemplateColumns: dayGridColumns, minWidth: stripWidth }, children: days.map((day, i) => /* @__PURE__ */ jsxs("div", { className: cn("text-center py-ds-2", i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", selectedDate && isSameDay(day, selectedDate) && "bg-[var(--ds-accent-whisper)]"), children: [
7151
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--ds-text-muted)] font-medium", children: names[i] }),
7152
+ /* @__PURE__ */ jsx("div", { className: cn("text-lg font-semibold mt-ds-0.5 mx-auto w-8 h-8 flex items-center justify-center rounded-[var(--ds-radius-full)]", isToday(day) ? TODAY_CIRCLE : "text-[var(--ds-text-primary)]"), children: day.getDate() })
7153
+ ] }, i)) }) })
6890
7154
  ] }),
6891
- hasAllDay && /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-bg-muted)]/30", children: [
7155
+ /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-bg-muted)]/30", children: [
6892
7156
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] text-2xs text-[var(--ds-text-muted)] px-ds-1 py-ds-1 text-right", children: "all-day" }),
6893
- /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6894
- "div",
6895
- {
6896
- className: "grid",
6897
- style: { gridTemplateColumns: dayGridColumns, minWidth: MIN_DAY_WIDTH * days.length },
6898
- children: allDayEvents.map((dayEvts, i) => /* @__PURE__ */ jsx(
6899
- "div",
6900
- {
6901
- className: cn(
6902
- "p-ds-0.5 space-y-ds-0.5 min-h-[28px]",
6903
- i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"
6904
- ),
6905
- children: dayEvts.map((event) => /* @__PURE__ */ jsx(
7157
+ /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": "All day", "aria-readonly": "true", style: { minWidth: stripWidth }, children: /* @__PURE__ */ jsx("div", { role: "row", className: "grid", style: { gridTemplateColumns: dayGridColumns, height: allDayHeight }, children: days.map((day, i) => {
7158
+ const key = cellKey(day, null);
7159
+ const more = allDay.more[i];
7160
+ return /* @__PURE__ */ jsxs(
7161
+ "div",
7162
+ {
7163
+ role: "gridcell",
7164
+ "data-grid-key": key,
7165
+ tabIndex: grid.tabIndexOf(key),
7166
+ "aria-label": `${formatDayLong(day)}, all day`,
7167
+ onFocus: () => {
7168
+ if (active !== key) setActiveKey(key);
7169
+ },
7170
+ onClick: () => onDayClick(day),
7171
+ className: cn("relative cursor-pointer", FOCUS_INSET, i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"),
7172
+ children: [
7173
+ allDay.segments.filter((s) => s.startCol === i).map((s) => /* @__PURE__ */ jsx(
6906
7174
  EventBar,
6907
7175
  {
6908
- event,
6909
- category: categories[event.category ?? ""],
6910
- onClick: onEventClick
7176
+ event: s.event,
7177
+ category: categories[s.event.category ?? ""],
7178
+ onOpen: onOpenEvent,
7179
+ continuesBefore: s.continuesBefore,
7180
+ continuesAfter: s.continuesAfter,
7181
+ className: "absolute z-[1] flex items-center",
7182
+ style: {
7183
+ top: 2 + s.lane * (LANE_H2 + LANE_GAP2),
7184
+ height: LANE_H2,
7185
+ left: s.continuesBefore ? 0 : 2,
7186
+ width: `calc(${s.span} * (100% + var(--ds-divider-width)) - var(--ds-divider-width) - ${s.continuesBefore ? 0 : 2}px - ${s.continuesAfter ? 0 : 2}px)`
7187
+ }
6911
7188
  },
6912
- event.id
6913
- ))
7189
+ `${s.event.id}-${s.startCol}`
7190
+ )),
7191
+ more.length > 0 && /* @__PURE__ */ jsxs(
7192
+ "button",
7193
+ {
7194
+ type: "button",
7195
+ "aria-haspopup": "dialog",
7196
+ "aria-expanded": !!list && isSameDay(list.day, day),
7197
+ className: "absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)] focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
7198
+ style: { top: 2 + (ALL_DAY_LANES - 1) * (LANE_H2 + LANE_GAP2), left: 2, height: LANE_H2 },
7199
+ onClick: (e) => {
7200
+ e.stopPropagation();
7201
+ setList({ day, anchor: e.currentTarget });
7202
+ },
7203
+ children: [
7204
+ "+",
7205
+ more.length,
7206
+ " more"
7207
+ ]
7208
+ }
7209
+ )
7210
+ ]
7211
+ },
7212
+ i
7213
+ );
7214
+ }) }) }) })
7215
+ ] }),
7216
+ /* @__PURE__ */ jsxs("div", { ref: scrollerRef, className: "flex flex-1 min-h-0 overflow-y-auto", "data-week-hours": "", children: [
7217
+ /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 relative border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", style: { height: GRID_HEIGHT }, "aria-hidden": "true", children: HOURS.map((h) => /* @__PURE__ */ jsx("div", { className: "absolute right-0 pr-ds-2 text-2xs text-[var(--ds-text-muted)] -translate-y-1/2 tabular-nums", style: { top: h * HOUR_HEIGHT }, children: h > 0 && formatHourLabel(h) }, h)) }),
7218
+ /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 self-start overflow-x-auto overflow-y-hidden gs-scrollbar-none", style: { height: GRID_HEIGHT }, "data-week-scroll": true, children: /* @__PURE__ */ jsxs("div", { className: "relative", style: { height: GRID_HEIGHT, minWidth: stripWidth }, children: [
7219
+ /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": formatWeekRange(date, weekStartsOn), "aria-readonly": "true", className: "absolute inset-0", children: HOURS.map((hour) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid", style: { gridTemplateColumns: dayGridColumns, height: HOUR_HEIGHT }, children: days.map((day, i) => {
7220
+ const key = cellKey(day, hour);
7221
+ return /* @__PURE__ */ jsx(
7222
+ "div",
7223
+ {
7224
+ role: "gridcell",
7225
+ "data-grid-key": key,
7226
+ tabIndex: grid.tabIndexOf(key),
7227
+ "aria-label": `${formatDayLong(day)}, ${formatTime(new Date(day.getFullYear(), day.getMonth(), day.getDate(), hour))}`,
7228
+ onFocus: () => {
7229
+ if (active !== key) setActiveKey(key);
7230
+ },
7231
+ onClick: chooseHour(day, hour),
7232
+ className: cn(
7233
+ "cursor-pointer border-t-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] hover:bg-[var(--ds-bg-muted)]/40",
7234
+ FOCUS_INSET,
7235
+ i > 0 && "border-l-[length:var(--ds-divider-width)]"
7236
+ )
6914
7237
  },
6915
7238
  i
7239
+ );
7240
+ }) }, hour)) }),
7241
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid pointer-events-none", style: { gridTemplateColumns: dayGridColumns }, children: days.map((day, i) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
7242
+ isToday(day) && /* @__PURE__ */ jsx(NowLine, {}),
7243
+ pieces[i].map((placed) => /* @__PURE__ */ jsx(
7244
+ TimedEvent,
7245
+ {
7246
+ placed,
7247
+ category: categories[placed.piece.event.category ?? ""],
7248
+ onOpen: onOpenEvent,
7249
+ onFocusEvent: () => setActiveKey(cellKey(day, placed.piece.start.getHours()))
7250
+ },
7251
+ `${placed.piece.event.id}-${i}`
6916
7252
  ))
6917
- }
6918
- ) })
7253
+ ] }, i)) })
7254
+ ] }) })
6919
7255
  ] }),
6920
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0 overflow-y-auto", children: [
6921
- /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 relative border-r-[length:var(--ds-divider-width)] border-t-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", style: { height: GRID_HEIGHT }, children: HOURS.map((h) => /* @__PURE__ */ jsx(
6922
- "div",
6923
- {
6924
- className: "absolute right-0 pr-ds-2 text-2xs text-[var(--ds-text-muted)] -translate-y-1/2",
6925
- style: { top: h * HOUR_HEIGHT },
6926
- children: h > 0 && formatHour(h)
6927
- },
6928
- h
6929
- )) }),
6930
- /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx(
6931
- "div",
6932
- {
6933
- className: "relative",
6934
- style: { height: GRID_HEIGHT, minWidth: MIN_DAY_WIDTH * days.length },
6935
- children: /* @__PURE__ */ jsx(
6936
- "div",
6937
- {
6938
- className: "grid h-full",
6939
- style: { gridTemplateColumns: dayGridColumns },
6940
- children: days.map((day, i) => {
6941
- const dayEvents = getEventsForDay(events, day).filter((e) => !e.allDay);
6942
- const layout = getOverlappingGroups(dayEvents);
6943
- const today = isToday(day);
6944
- return /* @__PURE__ */ jsxs(
6945
- "div",
6946
- {
6947
- className: cn(
6948
- "relative",
6949
- i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"
6950
- ),
6951
- onClick: () => onDayClick?.(day),
6952
- children: [
6953
- HOURS.map((h) => /* @__PURE__ */ jsx(
6954
- "div",
6955
- {
6956
- className: "absolute left-0 right-0 border-t-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]",
6957
- style: { top: h * HOUR_HEIGHT }
6958
- },
6959
- h
6960
- )),
6961
- today && /* @__PURE__ */ jsx(CurrentTimeLine, {}),
6962
- layout.map(({ event, column, totalColumns }) => /* @__PURE__ */ jsx(
6963
- TimedEvent,
6964
- {
6965
- event,
6966
- category: categories[event.category ?? ""],
6967
- column,
6968
- totalColumns,
6969
- onEventClick
6970
- },
6971
- event.id
6972
- ))
6973
- ]
6974
- },
6975
- i
6976
- );
6977
- })
6978
- }
6979
- )
6980
- }
6981
- ) })
6982
- ] })
7256
+ list && /* @__PURE__ */ jsx(DayList, { day: list.day, events: getEventsForDay(barEvents, list.day), categories, anchor: list.anchor, onOpenEvent, onClose: () => setList(null) })
6983
7257
  ] });
6984
7258
  }
6985
7259
  var WEEKS_SHOWN = 6;
@@ -6990,7 +7264,6 @@ function DayCellWithPopover({
6990
7264
  events,
6991
7265
  categories,
6992
7266
  selectedDate,
6993
- onEventClick,
6994
7267
  isEvenRow
6995
7268
  }) {
6996
7269
  const [open, setOpen] = useState(false);
@@ -7006,7 +7279,7 @@ function DayCellWithPopover({
7006
7279
  const inMonth = day.getMonth() === month;
7007
7280
  const dayEvents = inMonth ? getEventsForDay(events, day) : [];
7008
7281
  const today = inMonth && isToday(day);
7009
- const selected = inMonth && selectedDate && isSameDay2(day, selectedDate);
7282
+ const selected = inMonth && selectedDate && isSameDay(day, selectedDate);
7010
7283
  const hasEvents = dayEvents.length > 0;
7011
7284
  const handleMouseEnter = () => {
7012
7285
  if (!hasEvents) return;
@@ -7028,7 +7301,6 @@ function DayCellWithPopover({
7028
7301
  inMonth && "cursor-pointer hover:bg-[var(--ds-bg-muted)]",
7029
7302
  !inMonth && "bg-[var(--ds-bg-muted)]/40",
7030
7303
  inMonth && isEvenRow && !today && !selected && "bg-[var(--ds-bg-muted)]/20",
7031
- today && "bg-[var(--ds-accent-subtle)]",
7032
7304
  selected && !today && "bg-[var(--ds-accent-whisper)]"
7033
7305
  ),
7034
7306
  children: [
@@ -7038,7 +7310,7 @@ function DayCellWithPopover({
7038
7310
  {
7039
7311
  className: cn(
7040
7312
  "text-2xs leading-none z-[1]",
7041
- today ? "font-bold text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-secondary)]"
7313
+ today ? cn("w-5 h-5 inline-flex items-center justify-center rounded-[var(--ds-radius-full)]", TODAY_CIRCLE) : "text-[var(--ds-text-secondary)]"
7042
7314
  ),
7043
7315
  children: day.getDate()
7044
7316
  }
@@ -7062,16 +7334,8 @@ function DayCellWithPopover({
7062
7334
  style: { top: pos.top, left: pos.left },
7063
7335
  role: "tooltip",
7064
7336
  children: [
7065
- /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-0.5", children: day.toLocaleDateString(formatLocale(), { weekday: "short", month: "short", day: "numeric" }) }),
7066
- dayEvents.map((event) => /* @__PURE__ */ jsx(
7067
- EventBar,
7068
- {
7069
- event,
7070
- category: categories[event.category ?? ""],
7071
- onClick: onEventClick
7072
- },
7073
- event.id
7074
- ))
7337
+ /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-0.5", children: formatDayShort(day) }),
7338
+ dayEvents.map((event) => /* @__PURE__ */ jsx(EventBar, { event, category: categories[event.category ?? ""], inert: true }, event.id))
7075
7339
  ]
7076
7340
  }
7077
7341
  ),
@@ -7088,7 +7352,6 @@ function MonthRow({
7088
7352
  categories,
7089
7353
  selectedDate,
7090
7354
  onClick,
7091
- onEventClick,
7092
7355
  weekStartsOn
7093
7356
  }) {
7094
7357
  const grid = useMemo(
@@ -7106,7 +7369,7 @@ function MonthRow({
7106
7369
  "border-r-[length:var(--ds-divider-width)] border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"
7107
7370
  ),
7108
7371
  onClick,
7109
- children: MONTH_NAMES_SHORT[month]
7372
+ children: monthName(month, "short")
7110
7373
  }
7111
7374
  ),
7112
7375
  flat.map((day, i) => /* @__PURE__ */ jsx(
@@ -7117,7 +7380,6 @@ function MonthRow({
7117
7380
  events,
7118
7381
  categories,
7119
7382
  selectedDate,
7120
- onEventClick,
7121
7383
  isEvenRow: month % 2 === 0
7122
7384
  },
7123
7385
  i
@@ -7130,11 +7392,10 @@ function YearView({
7130
7392
  categories,
7131
7393
  selectedDate,
7132
7394
  onMonthClick,
7133
- onEventClick,
7134
7395
  weekStartsOn
7135
7396
  }) {
7136
7397
  const year = date.getFullYear();
7137
- const dayNames = useMemo(() => getOrderedDayNames(weekStartsOn), [weekStartsOn]);
7398
+ const dayNames = weekdayNames(weekStartsOn, "narrow");
7138
7399
  const repeatedHeaders = useMemo(() => {
7139
7400
  const headers = [];
7140
7401
  for (let w = 0; w < WEEKS_SHOWN; w++) {
@@ -7149,7 +7410,7 @@ function YearView({
7149
7410
  {
7150
7411
  className: "grid min-w-[700px]",
7151
7412
  style: {
7152
- gridTemplateColumns: `48px repeat(${COLS}, minmax(22px, 1fr))`,
7413
+ gridTemplateColumns: `48px repeat(${COLS}, minmax(24px, 1fr))`,
7153
7414
  gridTemplateRows: `auto repeat(12, 36px)`
7154
7415
  },
7155
7416
  children: [
@@ -7162,7 +7423,7 @@ function YearView({
7162
7423
  "bg-[var(--ds-bg-muted)] border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]",
7163
7424
  h.isWeekStart && "border-l-[length:var(--ds-divider-width)]"
7164
7425
  ),
7165
- children: h.label[0]
7426
+ children: h.label
7166
7427
  },
7167
7428
  i
7168
7429
  )),
@@ -7175,7 +7436,6 @@ function YearView({
7175
7436
  categories,
7176
7437
  selectedDate,
7177
7438
  onClick: () => onMonthClick(month),
7178
- onEventClick,
7179
7439
  weekStartsOn
7180
7440
  },
7181
7441
  month
@@ -7184,191 +7444,83 @@ function YearView({
7184
7444
  }
7185
7445
  ) });
7186
7446
  }
7187
- function CompactDayCell({
7188
- day,
7189
- isCurrentMonth,
7190
- events,
7191
- categories,
7192
- selectedDate,
7193
- onDayClick,
7194
- onEventClick
7195
- }) {
7196
- const [open, setOpen] = useState(false);
7197
- const cellRef = useRef(null);
7198
- const floatingRef = useRef(null);
7199
- const pos = usePosition(cellRef, floatingRef, open, {
7200
- side: "bottom",
7201
- align: "center",
7202
- offset: 4,
7203
- flip: true
7447
+ function CompactMonthView({ date, events, categories, selectedDate, onDayClick, onOpenEvent, onNavigate, weekStartsOn }) {
7448
+ const weeks = useMemo(() => getMonthGrid(date.getFullYear(), date.getMonth(), weekStartsOn), [date, weekStartsOn]);
7449
+ const names = { narrow: weekdayNames(weekStartsOn, "narrow"), long: weekdayNames(weekStartsOn, "long") };
7450
+ const currentMonth = date.getMonth();
7451
+ const gridRef = useRef(null);
7452
+ const [list, setList] = useState(null);
7453
+ const inMonth = (d) => !!d && d.getMonth() === currentMonth && d.getFullYear() === date.getFullYear();
7454
+ const fallback = inMonth(selectedDate) ? selectedDate : inMonth(/* @__PURE__ */ new Date()) ? /* @__PURE__ */ new Date() : new Date(date.getFullYear(), currentMonth, 1);
7455
+ const [activeDay, setActiveDay] = useState(fallback);
7456
+ const active = inMonth(activeDay) ? activeDay : fallback;
7457
+ const grid = useGridKeyboard({
7458
+ container: gridRef,
7459
+ active,
7460
+ setActive: (d) => {
7461
+ setActiveDay(d);
7462
+ if (!inMonth(d)) onNavigate(d);
7463
+ },
7464
+ keyOf: dayKey,
7465
+ move: (from, e) => moveDay(from, e.key, e.shiftKey, weekStartsOn)
7204
7466
  });
7205
- const hoverTimeout = useRef(void 0);
7206
- const dayEvents = isCurrentMonth ? getEventsForDay(events, day) : [];
7207
- const today = isCurrentMonth && isToday(day);
7208
- const selected = isCurrentMonth && selectedDate && isSameDay2(day, selectedDate);
7209
- const hasEvents = dayEvents.length > 0;
7210
- const shown = open && hasEvents;
7211
- const isTop = useOverlayLayer(shown);
7212
- useCloseOnFocusOut(shown, cellRef, floatingRef, () => setOpen(false), isTop);
7213
- const eventColors = dayEvents.slice(0, 3).map((e) => categories[e.category ?? ""]?.color ?? "var(--ds-accent-primary)");
7214
- const handleMouseEnter = () => {
7215
- if (!hasEvents) return;
7216
- hoverTimeout.current = setTimeout(() => setOpen(true), 250);
7217
- };
7218
- const handleMouseLeave = () => {
7219
- clearTimeout(hoverTimeout.current);
7220
- setOpen(false);
7221
- };
7222
- const handleClick = () => {
7223
- onDayClick?.(day);
7224
- if (hasEvents) {
7225
- setOpen((prev) => !prev);
7226
- }
7227
- };
7228
- useEffect(() => {
7229
- if (!open) return;
7230
- const onKey = (e) => {
7231
- if (e.key !== "Escape" || e.defaultPrevented || !isTop()) return;
7232
- e.preventDefault();
7233
- setOpen(false);
7234
- };
7235
- const onClick = (e) => {
7236
- const t = e.target;
7237
- if (cellRef.current?.contains(t) || floatingRef.current?.contains(t)) return;
7238
- setOpen(false);
7239
- };
7240
- document.addEventListener("keydown", onKey);
7241
- document.addEventListener("mousedown", onClick);
7242
- return () => {
7243
- document.removeEventListener("keydown", onKey);
7244
- document.removeEventListener("mousedown", onClick);
7245
- };
7246
- }, [open, isTop]);
7247
- return /* @__PURE__ */ jsxs(Fragment, { children: [
7248
- /* @__PURE__ */ jsxs(
7249
- "button",
7250
- {
7251
- ref: cellRef,
7252
- type: "button",
7253
- onClick: handleClick,
7254
- onMouseEnter: handleMouseEnter,
7255
- onMouseLeave: handleMouseLeave,
7256
- className: cn(
7257
- "flex flex-col items-center justify-center py-ds-1.5 rounded-[var(--ds-radius-sm)] cursor-pointer transition-colors",
7258
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
7259
- !isCurrentMonth && "opacity-30",
7260
- selected && "bg-[var(--ds-accent-subtle)]",
7261
- !selected && "hover:bg-[var(--ds-bg-muted)]"
7262
- ),
7263
- children: [
7264
- /* @__PURE__ */ jsx(
7265
- "span",
7266
- {
7267
- className: cn(
7268
- "text-sm w-7 h-7 flex items-center justify-center rounded-[var(--ds-radius-full)]",
7269
- today ? "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)] font-bold" : "text-[var(--ds-text-primary)]"
7270
- ),
7271
- children: day.getDate()
7272
- }
7273
- ),
7274
- /* @__PURE__ */ jsx("div", { className: "flex gap-ds-0.5 mt-ds-0.5 h-[6px] items-center", children: eventColors.map((color, i) => /* @__PURE__ */ jsx(
7275
- "span",
7276
- {
7277
- className: "w-1.5 h-1.5 rounded-[var(--ds-radius-full)]",
7278
- style: { backgroundColor: color }
7467
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "var(--ds-space-3) var(--ds-space-2.5)" }, children: [
7468
+ /* @__PURE__ */ jsxs("div", { ref: gridRef, role: "grid", "aria-label": formatMonthYear(date), onKeyDown: grid.onKeyDown, className: "flex flex-col gap-y-ds-0.5", children: [
7469
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 mb-ds-1", children: names.narrow.map((name, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": names.long[i], className: "text-center text-xs font-medium text-[var(--ds-text-muted)] py-ds-1.5", children: name }, i)) }),
7470
+ weeks.map((week) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7", children: week.map((day) => {
7471
+ const isCurrentMonth = day.getMonth() === currentMonth;
7472
+ const dayEvents = isCurrentMonth ? getEventsForDay(events, day) : [];
7473
+ const today = isCurrentMonth && isToday(day);
7474
+ const selected = isCurrentMonth && !!selectedDate && isSameDay(day, selectedDate);
7475
+ return /* @__PURE__ */ jsxs(
7476
+ "button",
7477
+ {
7478
+ type: "button",
7479
+ role: "gridcell",
7480
+ "data-grid-key": dayKey(day),
7481
+ tabIndex: grid.tabIndexOf(day),
7482
+ "aria-label": dayEvents.length ? `${formatDayLong(day)}, ${dayEvents.length} ${dayEvents.length === 1 ? "event" : "events"}` : formatDayLong(day),
7483
+ "aria-selected": selected,
7484
+ "aria-current": today ? "date" : void 0,
7485
+ onFocus: () => {
7486
+ if (!isSameDay(day, active)) setActiveDay(day);
7279
7487
  },
7280
- i
7281
- )) })
7282
- ]
7283
- }
7284
- ),
7285
- open && hasEvents && createPortal(
7286
- /* @__PURE__ */ jsxs(
7287
- "div",
7288
- {
7289
- ref: floatingRef,
7290
- onMouseEnter: () => clearTimeout(hoverTimeout.current),
7291
- onMouseLeave: () => setOpen(false),
7292
- className: cn(
7293
- "fixed z-[var(--ds-z-popover)] w-52 max-h-40 overflow-y-auto",
7294
- "bg-[var(--ds-bg-surface-elevated)]",
7295
- "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
7296
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
7297
- "rounded-[var(--ds-radius-md)]",
7298
- "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
7299
- "p-ds-2 space-y-ds-1"
7300
- ),
7301
- style: { top: pos.top, left: pos.left },
7302
- role: "tooltip",
7303
- children: [
7304
- /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-0.5", children: day.toLocaleDateString(formatLocale(), { weekday: "short", month: "short", day: "numeric" }) }),
7305
- dayEvents.map((event) => /* @__PURE__ */ jsx(
7306
- EventBar,
7307
- {
7308
- event,
7309
- category: categories[event.category ?? ""],
7310
- onClick: onEventClick
7311
- },
7312
- event.id
7313
- ))
7314
- ]
7315
- }
7316
- ),
7317
- document.body
7318
- )
7319
- ] });
7320
- }
7321
- function CompactMonthView({
7322
- date,
7323
- events,
7324
- categories,
7325
- selectedDate,
7326
- onDayClick,
7327
- onEventClick,
7328
- weekStartsOn
7329
- }) {
7330
- const weeks = useMemo(
7331
- () => getMonthGrid(date.getFullYear(), date.getMonth(), weekStartsOn),
7332
- [date, weekStartsOn]
7333
- );
7334
- const dayNames = useMemo(() => getOrderedDayNames(weekStartsOn), [weekStartsOn]);
7335
- const currentMonth = date.getMonth();
7336
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { padding: "var(--ds-space-3) var(--ds-space-2.5)" }, children: [
7337
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 mb-ds-1.5", children: dayNames.map((name) => /* @__PURE__ */ jsx(
7338
- "div",
7339
- {
7340
- className: "text-center text-xs font-medium text-[var(--ds-text-muted)] py-ds-1.5",
7341
- children: name[0]
7342
- },
7343
- name
7344
- )) }),
7345
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-y-ds-0.5", children: weeks.map(
7346
- (week, wi) => week.map((day, di) => /* @__PURE__ */ jsx(
7347
- CompactDayCell,
7348
- {
7349
- day,
7350
- isCurrentMonth: day.getMonth() === currentMonth,
7351
- events,
7352
- categories,
7353
- selectedDate,
7354
- onDayClick,
7355
- onEventClick
7356
- },
7357
- `${wi}-${di}`
7358
- ))
7359
- ) })
7488
+ onClick: (e) => {
7489
+ onDayClick(day);
7490
+ const anchor = e.currentTarget;
7491
+ if (dayEvents.length) setList((cur) => cur && isSameDay(cur.day, day) ? null : { day, anchor });
7492
+ },
7493
+ className: cn(
7494
+ "flex flex-col items-center justify-center py-ds-1.5 rounded-[var(--ds-radius-sm)] cursor-pointer transition-colors",
7495
+ "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
7496
+ !isCurrentMonth && "opacity-30",
7497
+ selected && "bg-[var(--ds-accent-subtle)]",
7498
+ !selected && "hover:bg-[var(--ds-bg-muted)]"
7499
+ ),
7500
+ children: [
7501
+ /* @__PURE__ */ jsx("span", { className: cn("text-sm w-7 h-7 flex items-center justify-center rounded-[var(--ds-radius-full)]", today ? TODAY_CIRCLE : "text-[var(--ds-text-primary)]"), children: day.getDate() }),
7502
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex gap-ds-0.5 mt-ds-0.5 h-[6px] items-center", children: dayEvents.slice(0, 3).map((event) => /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-[var(--ds-radius-full)]", style: { backgroundColor: eventColours(categories[event.category ?? ""]).edge } }, event.id)) })
7503
+ ]
7504
+ },
7505
+ dayKey(day)
7506
+ );
7507
+ }) }, dayKey(week[0])))
7508
+ ] }),
7509
+ list && /* @__PURE__ */ jsx(DayList, { day: list.day, events: getEventsForDay(events, list.day), categories, anchor: list.anchor, onOpenEvent, onClose: () => setList(null) })
7360
7510
  ] });
7361
7511
  }
7362
7512
 
7363
7513
  // ../../showcase/src/components/Calendar/types.ts
7364
7514
  var DEFAULT_CATEGORIES = {
7365
- work: { color: "var(--ds-accent-primary)", label: "Work" },
7366
- personal: { color: "var(--ds-status-success)", label: "Personal" },
7367
- meeting: { color: "var(--ds-status-warning)", label: "Meeting" },
7368
- deadline: { color: "var(--ds-status-error)", label: "Deadline" },
7369
- travel: { color: "var(--ds-status-info)", label: "Travel" },
7370
- social: { color: "var(--ds-hue-secondary)", label: "Social" }
7515
+ work: { color: "accent", label: "Work" },
7516
+ personal: { color: 1, label: "Personal" },
7517
+ meeting: { color: 2, label: "Meeting" },
7518
+ deadline: { color: 3, label: "Deadline" },
7519
+ travel: { color: 4, label: "Travel" },
7520
+ social: { color: 5, label: "Social" }
7371
7521
  };
7522
+ var notADate = (d) => d !== void 0 && Number.isNaN(new Date(d).getTime());
7523
+ var WEEK_FLOOR = 160 + 4 * HOUR_HEIGHT;
7372
7524
  function useControllableState(controlled, defaultValue, onChange) {
7373
7525
  const [internal, setInternal] = useState(defaultValue);
7374
7526
  const value = controlled !== void 0 ? controlled : internal;
@@ -7448,9 +7600,7 @@ function MonthDropdown({
7448
7600
  "pl-ds-3 pr-ds-2 py-ds-1"
7449
7601
  ),
7450
7602
  children: [
7451
- MONTH_NAMES[currentMonth],
7452
- " ",
7453
- currentYear,
7603
+ formatMonthYear(currentDate),
7454
7604
  /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 12, className: cn("transition-transform", open && "rotate-180") })
7455
7605
  ]
7456
7606
  }
@@ -7492,7 +7642,7 @@ function MonthDropdown({
7492
7642
  }
7493
7643
  )
7494
7644
  ] }),
7495
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-ds-1", children: MONTH_NAMES_SHORT.map((name, i) => {
7645
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-ds-1", children: Array.from({ length: 12 }, (_, i) => monthName(i, "short")).map((name, i) => {
7496
7646
  const isActive2 = i === currentMonth && browseYear === currentYear;
7497
7647
  return /* @__PURE__ */ jsx(
7498
7648
  "button",
@@ -7510,7 +7660,7 @@ function MonthDropdown({
7510
7660
  },
7511
7661
  children: name
7512
7662
  },
7513
- name
7663
+ i
7514
7664
  );
7515
7665
  }) })
7516
7666
  ]
@@ -7663,6 +7813,7 @@ function Calendar({
7663
7813
  onDateRangeChange,
7664
7814
  onEventClick,
7665
7815
  onDayClick,
7816
+ eventActions,
7666
7817
  categories = DEFAULT_CATEGORIES,
7667
7818
  weekStartsOn = 1,
7668
7819
  size = "default",
@@ -7673,14 +7824,21 @@ function Calendar({
7673
7824
  defaultView,
7674
7825
  onViewChange
7675
7826
  );
7827
+ const today = useMemo(() => /* @__PURE__ */ new Date(), []);
7676
7828
  const [currentDate, setCurrentDate] = useControllableState(
7677
- controlledDate,
7678
- defaultDate ?? /* @__PURE__ */ new Date(),
7829
+ notADate(controlledDate) ? today : controlledDate,
7830
+ defaultDate !== void 0 && !notADate(defaultDate) ? defaultDate : today,
7679
7831
  onDateChange
7680
7832
  );
7833
+ const dateUnreadable = notADate(controlledDate) || notADate(defaultDate);
7834
+ useEffect(() => {
7835
+ if (dateUnreadable) warnOnce("Calendar", "its date (or defaultDate) isn't a date, so it shows today: pass a Date, or a date string Date reads.");
7836
+ }, [dateUnreadable]);
7681
7837
  const [selectedDate, setSelectedDate] = useState(null);
7682
7838
  const [isNarrow, setIsNarrow] = useState(false);
7839
+ const rootRef = useRef(null);
7683
7840
  const containerRef = useCallback((node) => {
7841
+ rootRef.current = node;
7684
7842
  if (!node) return;
7685
7843
  const ro = new ResizeObserver((entries) => {
7686
7844
  const width = entries[0]?.contentRect.width ?? 0;
@@ -7689,10 +7847,44 @@ function Calendar({
7689
7847
  ro.observe(node);
7690
7848
  return () => ro.disconnect();
7691
7849
  }, []);
7692
- const stableEvents = useMemo(
7693
- () => events.map((e) => ({ ...e, start: new Date(e.start), end: e.end ? new Date(e.end) : void 0 })),
7694
- [events]
7695
- );
7850
+ const fillRaw = useFillHeight(rootRef, size !== "compact" && currentView === "week");
7851
+ const fill2 = fillRaw !== null && fillRaw >= WEEK_FLOOR ? fillRaw : null;
7852
+ const inPane = useContext(FillContext) && currentView === "week";
7853
+ const [openEvent, setOpenEvent] = useState(null);
7854
+ const openCard = useCallback((event, from) => {
7855
+ if (onEventClick) {
7856
+ onEventClick(event);
7857
+ return;
7858
+ }
7859
+ setOpenEvent((cur) => cur && cur.event.id === event.id && cur.anchor === from ? null : { event, anchor: from });
7860
+ }, [onEventClick]);
7861
+ const actionsHidden = !!onEventClick && !!eventActions;
7862
+ useEffect(() => {
7863
+ if (actionsHidden) warnOnce("Calendar", "eventActions are never shown while onEventClick is set: the page opens a surface of its own for an event, so no card shows. Pass eventActions for the Calendar's card, or onEventClick for your own (Sprint 25.18).");
7864
+ }, [actionsHidden]);
7865
+ const shownKey = `${currentView}|${currentDate.toDateString()}`;
7866
+ const [cardShownKey, setCardShownKey] = useState(shownKey);
7867
+ if (cardShownKey !== shownKey) {
7868
+ setCardShownKey(shownKey);
7869
+ if (openEvent) setOpenEvent(null);
7870
+ }
7871
+ const { stableEvents, unreadable } = useMemo(() => {
7872
+ const read = [];
7873
+ const left = [];
7874
+ for (const e of events) {
7875
+ const start = new Date(e.start);
7876
+ if (Number.isNaN(start.getTime())) {
7877
+ left.push(String(e.id));
7878
+ continue;
7879
+ }
7880
+ const end = e.end ? new Date(e.end) : void 0;
7881
+ read.push({ ...e, start, end: end && !Number.isNaN(end.getTime()) ? end : void 0 });
7882
+ }
7883
+ return { stableEvents: read, unreadable: left.join(", ") };
7884
+ }, [events]);
7885
+ useEffect(() => {
7886
+ if (unreadable) warnOnce("Calendar", `events with a start that isn't a date aren't drawn (${unreadable}): give each a Date, or a date string Date reads.`);
7887
+ }, [unreadable]);
7696
7888
  useEffect(() => {
7697
7889
  if (!onDateRangeChange) return;
7698
7890
  let start, end;
@@ -7725,9 +7917,10 @@ function Calendar({
7725
7917
  setSelectedDate(/* @__PURE__ */ new Date());
7726
7918
  }, [setCurrentDate]);
7727
7919
  const handleDayClick = useCallback(
7728
- (date) => {
7920
+ (date, detail) => {
7729
7921
  setSelectedDate(date);
7730
- onDayClick?.(date);
7922
+ if (detail) onDayClick?.(date, detail);
7923
+ else onDayClick?.(date);
7731
7924
  },
7732
7925
  [onDayClick]
7733
7926
  );
@@ -7742,10 +7935,24 @@ function Calendar({
7742
7935
  if (currentView === "week") return formatWeekRange(currentDate, weekStartsOn);
7743
7936
  return void 0;
7744
7937
  }, [currentView, currentDate, weekStartsOn]);
7938
+ const live = openEvent && stableEvents.find((e) => e.id === openEvent.event.id);
7939
+ const card = openEvent && live ? /* @__PURE__ */ jsx(
7940
+ EventCard,
7941
+ {
7942
+ event: live,
7943
+ category: categories[live.category ?? ""],
7944
+ anchor: openEvent.anchor,
7945
+ actions: typeof eventActions === "function" ? eventActions(live) : eventActions ?? [],
7946
+ onClose: () => setOpenEvent(null),
7947
+ fallbackFocus: () => rootRef.current?.querySelector('[role="gridcell"][tabindex="0"]')
7948
+ },
7949
+ openEvent.event.id
7950
+ ) : null;
7745
7951
  if (size === "compact") {
7746
7952
  return /* @__PURE__ */ jsxs(
7747
7953
  "div",
7748
7954
  {
7955
+ ref: containerRef,
7749
7956
  className: cn(
7750
7957
  "flex flex-col",
7751
7958
  "bg-[var(--ds-bg-surface)]",
@@ -7775,16 +7982,12 @@ function Calendar({
7775
7982
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 16 })
7776
7983
  }
7777
7984
  ),
7778
- /* @__PURE__ */ jsxs(
7985
+ /* @__PURE__ */ jsx(
7779
7986
  "button",
7780
7987
  {
7781
7988
  onClick: goToToday,
7782
7989
  className: "text-sm font-medium text-[var(--ds-text-primary)] hover:text-[var(--ds-accent-primary)] transition-colors cursor-pointer px-ds-2",
7783
- children: [
7784
- MONTH_NAMES[currentDate.getMonth()],
7785
- " ",
7786
- currentDate.getFullYear()
7787
- ]
7990
+ children: formatMonthYear(currentDate)
7788
7991
  }
7789
7992
  ),
7790
7993
  /* @__PURE__ */ jsx(
@@ -7807,10 +8010,12 @@ function Calendar({
7807
8010
  categories,
7808
8011
  selectedDate,
7809
8012
  onDayClick: handleDayClick,
7810
- onEventClick,
8013
+ onOpenEvent: openCard,
8014
+ onNavigate: setCurrentDate,
7811
8015
  weekStartsOn
7812
8016
  }
7813
- )
8017
+ ),
8018
+ card
7814
8019
  ]
7815
8020
  }
7816
8021
  );
@@ -7819,6 +8024,7 @@ function Calendar({
7819
8024
  "div",
7820
8025
  {
7821
8026
  ref: containerRef,
8027
+ "data-fill": fill2 != null ? "" : void 0,
7822
8028
  className: cn(
7823
8029
  "flex flex-col",
7824
8030
  "bg-[var(--ds-bg-surface)]",
@@ -7826,8 +8032,10 @@ function Calendar({
7826
8032
  "rounded-[var(--ds-radius-lg)]",
7827
8033
  "shadow-[var(--ds-shadow-sm)]",
7828
8034
  "overflow-hidden",
8035
+ inPane && "h-full min-h-0",
7829
8036
  className
7830
8037
  ),
8038
+ style: fill2 != null ? { height: fill2 } : void 0,
7831
8039
  children: [
7832
8040
  isNarrow ? /* @__PURE__ */ jsxs(
7833
8041
  "div",
@@ -7886,7 +8094,7 @@ function Calendar({
7886
8094
  ]
7887
8095
  }
7888
8096
  ),
7889
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0", style: { minHeight: currentView === "week" ? 420 : currentView === "month" ? isNarrow ? 360 : 500 : void 0 }, children: [
8097
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0", style: { minHeight: currentView === "week" ? fill2 != null || inPane ? void 0 : 420 : currentView === "month" ? isNarrow ? 360 : 500 : void 0 }, children: [
7890
8098
  currentView === "month" && /* @__PURE__ */ jsx(
7891
8099
  MonthView,
7892
8100
  {
@@ -7895,7 +8103,9 @@ function Calendar({
7895
8103
  categories,
7896
8104
  selectedDate,
7897
8105
  onDayClick: handleDayClick,
7898
- onEventClick,
8106
+ canStartEvent: !!onDayClick,
8107
+ onOpenEvent: openCard,
8108
+ onNavigate: setCurrentDate,
7899
8109
  weekStartsOn
7900
8110
  }
7901
8111
  ),
@@ -7907,7 +8117,8 @@ function Calendar({
7907
8117
  categories,
7908
8118
  selectedDate,
7909
8119
  onDayClick: handleDayClick,
7910
- onEventClick,
8120
+ onOpenEvent: openCard,
8121
+ onNavigate: setCurrentDate,
7911
8122
  weekStartsOn
7912
8123
  }
7913
8124
  ),
@@ -7919,11 +8130,11 @@ function Calendar({
7919
8130
  categories,
7920
8131
  selectedDate,
7921
8132
  onMonthClick: handleMonthClick,
7922
- weekStartsOn,
7923
- onEventClick
8133
+ weekStartsOn
7924
8134
  }
7925
8135
  )
7926
- ] })
8136
+ ] }),
8137
+ card
7927
8138
  ]
7928
8139
  }
7929
8140
  );
@@ -9763,9 +9974,9 @@ var StackedList = forwardRef(function StackedList2({
9763
9974
  ] });
9764
9975
  });
9765
9976
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
9766
- var DAY_MS = 864e5;
9977
+ var DAY_MS2 = 864e5;
9767
9978
  var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
9768
- var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
9979
+ var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS2);
9769
9980
  function dayOfDate(d) {
9770
9981
  const t = d.getTime();
9771
9982
  if (Number.isNaN(t)) return NaN;
@@ -9774,7 +9985,7 @@ function dayOfDate(d) {
9774
9985
  first.setHours(0, 0, 0, 0);
9775
9986
  if (first.getTime() === t) return day;
9776
9987
  const ms = ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
9777
- return day + ms / DAY_MS;
9988
+ return day + ms / DAY_MS2;
9778
9989
  }
9779
9990
  function dayOf(value) {
9780
9991
  if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
@@ -9784,13 +9995,13 @@ function dayOf(value) {
9784
9995
  if (!m) return dayOfDate(new Date(value));
9785
9996
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
9786
9997
  const day = utcDay(y, mo, d);
9787
- const back = new Date(day * DAY_MS);
9998
+ const back = new Date(day * DAY_MS2);
9788
9999
  return back.getUTCFullYear() === y && back.getUTCMonth() === mo && back.getUTCDate() === d ? day : NaN;
9789
10000
  }
9790
10001
  function dateOfDay(day) {
9791
10002
  const whole = Math.floor(day);
9792
- const u = new Date(whole * DAY_MS);
9793
- return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole) * DAY_MS));
10003
+ const u = new Date(whole * DAY_MS2);
10004
+ return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole) * DAY_MS2));
9794
10005
  }
9795
10006
  function isWholeDay(value) {
9796
10007
  return typeof value === "string" ? DATE_ONLY2.test(value.trim()) : Number.isInteger(dayOf(value));
@@ -9809,7 +10020,7 @@ function periodStart(unit, day, weekStartsOn) {
9809
10020
  const d = Math.floor(day);
9810
10021
  if (unit === "day") return d;
9811
10022
  if (unit === "week") return d - (weekday(d) - weekStartsOn + 7) % 7;
9812
- const u = new Date(d * DAY_MS);
10023
+ const u = new Date(d * DAY_MS2);
9813
10024
  const y = u.getUTCFullYear();
9814
10025
  const m = u.getUTCMonth();
9815
10026
  if (unit === "month") return utcDay(y, m, 1);
@@ -9819,7 +10030,7 @@ function periodStart(unit, day, weekStartsOn) {
9819
10030
  function periodAfter(unit, start) {
9820
10031
  if (unit === "day") return start + 1;
9821
10032
  if (unit === "week") return start + 7;
9822
- const u = new Date(start * DAY_MS);
10033
+ const u = new Date(start * DAY_MS2);
9823
10034
  const y = u.getUTCFullYear();
9824
10035
  const m = u.getUTCMonth();
9825
10036
  if (unit === "month") return utcDay(y, m + 1, 1);
@@ -9838,11 +10049,11 @@ function formatter(given, options) {
9838
10049
  return f;
9839
10050
  }
9840
10051
  function format(day, locale, options) {
9841
- return formatter(locale, options).format(new Date(day * DAY_MS));
10052
+ return formatter(locale, options).format(new Date(day * DAY_MS2));
9842
10053
  }
9843
10054
  var pad22 = (n) => String(n).padStart(2, "0");
9844
10055
  function keyOf(unit, day) {
9845
- const u = new Date(day * DAY_MS);
10056
+ const u = new Date(day * DAY_MS2);
9846
10057
  const y = u.getUTCFullYear();
9847
10058
  const m = u.getUTCMonth();
9848
10059
  if (unit === "year") return String(y);
@@ -9857,7 +10068,7 @@ function labelsOf(unit, role, day, locale) {
9857
10068
  else if (unit === "week") forms = [f({ day: "numeric", month: "short" }), f({ day: "numeric" })];
9858
10069
  else if (unit === "month") {
9859
10070
  forms = role === "major" ? [f({ month: "long", year: "numeric" }), f({ month: "short", year: "numeric" }), f({ month: "short" }), f({ month: "narrow" })] : [f({ month: "long" }), f({ month: "short" }), f({ month: "narrow" })];
9860
- } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS).getUTCMonth() / 3) + 1}`];
10071
+ } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS2).getUTCMonth() / 3) + 1}`];
9861
10072
  else forms = [f({ year: "numeric" }), f({ year: "2-digit" })];
9862
10073
  return forms;
9863
10074
  }
@@ -9998,8 +10209,8 @@ function formatDateRange(start, end, words) {
9998
10209
  const timed = !isWholeDay(start) || !isWholeDay(until);
9999
10210
  const first = timed ? from : Math.floor(from);
10000
10211
  const last = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
10001
- const a = new Date(first * DAY_MS);
10002
- const b = new Date(last * DAY_MS);
10212
+ const a = new Date(first * DAY_MS2);
10213
+ const b = new Date(last * DAY_MS2);
10003
10214
  const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
10004
10215
  const f = formatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
10005
10216
  return first === last ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
@@ -10276,9 +10487,9 @@ function TimeScaleHeader({ todayLabel = "Today", className }) {
10276
10487
  }
10277
10488
  var ZOOM_NAMES = { day: "Days", week: "Weeks", month: "Months", quarter: "Quarters" };
10278
10489
  var SHADE_MIN = 4;
10279
- var INSET = 8;
10490
+ var INSET2 = 8;
10280
10491
  var MIN_INSET = 2;
10281
- var MIN_TITLE = 2 * INSET + 12;
10492
+ var MIN_TITLE = 2 * INSET2 + 12;
10282
10493
  var PHONE_BELOW = 640;
10283
10494
  var MIN_LABEL = 112;
10284
10495
  var ROW_HEIGHT = 40;
@@ -10632,7 +10843,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10632
10843
  let label = null;
10633
10844
  if (labels && hasLabels) {
10634
10845
  const widths = labels.map(measure);
10635
- const inset = Math.min(INSET, Math.max(MIN_INSET, (drawnWidth - borders.start - borders.end - Math.min(...widths)) / 2 - 0.5));
10846
+ const inset = Math.min(INSET2, Math.max(MIN_INSET, (drawnWidth - borders.start - borders.end - Math.min(...widths)) / 2 - 0.5));
10636
10847
  const fits = (i) => `clamp(0, calc((${inView} - ${widths[i] + 2 * inset} + 0.25) * 1000), 1)`;
10637
10848
  const shows = (i) => i === 0 ? fits(0) : `calc(${[fits(i), ...labels.slice(0, i).map((_, j) => `(1 - ${fits(j)})`)].join(" * ")})`;
10638
10849
  const named = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
@@ -10647,7 +10858,7 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10647
10858
  "data-time-bar-label": "",
10648
10859
  "data-gs-clip": "",
10649
10860
  className: "sticky flex min-w-0 items-center gap-ds-1 overflow-hidden whitespace-nowrap",
10650
- style: { ...sticky, paddingInline: INSET, opacity: `clamp(0, calc((${inView} - ${MIN_TITLE}) * 1000), 1)` },
10861
+ style: { ...sticky, paddingInline: INSET2, opacity: `clamp(0, calc((${inView} - ${MIN_TITLE}) * 1000), 1)` },
10651
10862
  children: typeof children === "string" ? /* @__PURE__ */ jsx("span", { className: "truncate", children }) : children
10652
10863
  }
10653
10864
  );