gridsmith-ui 0.26.0 → 0.27.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 (102) hide show
  1. package/CHANGELOG.md +969 -0
  2. package/README.md +21 -5
  3. package/dist/charts.d.ts +2 -2
  4. package/dist/charts.js +58 -43
  5. package/dist/charts.js.map +1 -1
  6. package/dist/chat.js +4 -3
  7. package/dist/chat.js.map +1 -1
  8. package/dist/{chunk-CYN5O567.js → chunk-2CKSSWYG.js} +9 -5
  9. package/dist/chunk-2CKSSWYG.js.map +1 -0
  10. package/dist/chunk-3BRQRKDO.js +1 -0
  11. package/dist/chunk-3BRQRKDO.js.map +1 -1
  12. package/dist/{chunk-RWKCAKN3.js → chunk-5TUVX4B2.js} +6 -5
  13. package/dist/chunk-5TUVX4B2.js.map +1 -0
  14. package/dist/{chunk-X2HEV7FB.js → chunk-64TXZ4L7.js} +23 -33
  15. package/dist/chunk-64TXZ4L7.js.map +1 -0
  16. package/dist/chunk-7Q46JXSK.js +1 -0
  17. package/dist/chunk-7Q46JXSK.js.map +1 -1
  18. package/dist/chunk-CRQ6KCRD.js +1 -0
  19. package/dist/chunk-CRQ6KCRD.js.map +1 -1
  20. package/dist/{chunk-ZYVXV4WC.js → chunk-EKBWWA6P.js} +53 -6
  21. package/dist/chunk-EKBWWA6P.js.map +1 -0
  22. package/dist/chunk-HP7WUZPK.js +1 -0
  23. package/dist/chunk-HP7WUZPK.js.map +1 -1
  24. package/dist/{chunk-MYIJNVTK.js → chunk-IXR4UEF4.js} +5 -4
  25. package/dist/chunk-IXR4UEF4.js.map +1 -0
  26. package/dist/chunk-M2QRNBDQ.js +1 -0
  27. package/dist/chunk-M2QRNBDQ.js.map +1 -1
  28. package/dist/chunk-MD2EFMES.js +1 -0
  29. package/dist/chunk-MD2EFMES.js.map +1 -1
  30. package/dist/chunk-NRHIYS5W.js +1 -0
  31. package/dist/chunk-NRHIYS5W.js.map +1 -1
  32. package/dist/chunk-OTCEJN64.js +1 -0
  33. package/dist/chunk-OTCEJN64.js.map +1 -1
  34. package/dist/chunk-OYNUU346.js +78 -0
  35. package/dist/chunk-OYNUU346.js.map +1 -0
  36. package/dist/{chunk-EX64MN2U.js → chunk-PO65I3MQ.js} +4 -3
  37. package/dist/chunk-PO65I3MQ.js.map +1 -0
  38. package/dist/chunk-QM4SJKWV.js +1 -0
  39. package/dist/chunk-QM4SJKWV.js.map +1 -1
  40. package/dist/{chunk-KWLNEH53.js → chunk-R5NFCEZ3.js} +10 -7
  41. package/dist/chunk-R5NFCEZ3.js.map +1 -0
  42. package/dist/chunk-SFY2KMHG.js +1 -0
  43. package/dist/chunk-SFY2KMHG.js.map +1 -1
  44. package/dist/{chunk-HXVXJUGV.js → chunk-TJEV7YAY.js} +38 -10
  45. package/dist/chunk-TJEV7YAY.js.map +1 -0
  46. package/dist/chunk-WH2FUFAC.js +1 -0
  47. package/dist/chunk-WH2FUFAC.js.map +1 -1
  48. package/dist/chunk-XVB55M3N.js +1 -0
  49. package/dist/chunk-XVB55M3N.js.map +1 -1
  50. package/dist/chunk-ZI3OQDET.js +1 -0
  51. package/dist/chunk-ZI3OQDET.js.map +1 -1
  52. package/dist/data-grid.d.ts +1 -1
  53. package/dist/data-grid.js +5 -3
  54. package/dist/data-grid.js.map +1 -1
  55. package/dist/editor-mentions.js +2 -1
  56. package/dist/editor-mentions.js.map +1 -1
  57. package/dist/editor.js +3 -2
  58. package/dist/editor.js.map +1 -1
  59. package/dist/{feather-VFWLAILO.js → feather-FNBQ74RR.js} +10 -17
  60. package/dist/feather-FNBQ74RR.js.map +1 -0
  61. package/dist/{heroicons-H3Z7DFCF.js → heroicons-DODCSYWD.js} +9 -8
  62. package/dist/heroicons-DODCSYWD.js.map +1 -0
  63. package/dist/icons.d.ts +31 -4
  64. package/dist/icons.js +2 -1
  65. package/dist/icons.js.map +1 -1
  66. package/dist/index.d.ts +418 -65
  67. package/dist/index.js +1893 -838
  68. package/dist/index.js.map +1 -1
  69. package/dist/kanban.js +3 -2
  70. package/dist/kanban.js.map +1 -1
  71. package/dist/{lucide-MR32KM4G.js → lucide-ZWE3EPGP.js} +9 -8
  72. package/dist/lucide-ZWE3EPGP.js.map +1 -0
  73. package/dist/{phosphor-ABQQL5N5.js → phosphor-DIHPA4AJ.js} +9 -8
  74. package/dist/phosphor-DIHPA4AJ.js.map +1 -0
  75. package/dist/resizable.js +7 -5
  76. package/dist/resizable.js.map +1 -1
  77. package/dist/roots.css +25 -7
  78. package/dist/{tabler-ANUSY4D2.js → tabler-D3VYTSGI.js} +9 -8
  79. package/dist/tabler-D3VYTSGI.js.map +1 -0
  80. package/dist/tokens.css +11 -8
  81. package/dist/{use-pagination-BnKOnBdz.d.ts → use-pagination-BxcRlNQG.d.ts} +6 -0
  82. package/dist/utils.d.ts +275 -1
  83. package/dist/utils.js +875 -1
  84. package/dist/utils.js.map +1 -1
  85. package/dist/virtualized.d.ts +1 -1
  86. package/dist/virtualized.js +5 -3
  87. package/dist/virtualized.js.map +1 -1
  88. package/dist-static/tokens.css +11 -8
  89. package/package.json +5 -3
  90. package/dist/chunk-CYN5O567.js.map +0 -1
  91. package/dist/chunk-EX64MN2U.js.map +0 -1
  92. package/dist/chunk-HXVXJUGV.js.map +0 -1
  93. package/dist/chunk-KWLNEH53.js.map +0 -1
  94. package/dist/chunk-MYIJNVTK.js.map +0 -1
  95. package/dist/chunk-RWKCAKN3.js.map +0 -1
  96. package/dist/chunk-X2HEV7FB.js.map +0 -1
  97. package/dist/chunk-ZYVXV4WC.js.map +0 -1
  98. package/dist/feather-VFWLAILO.js.map +0 -1
  99. package/dist/heroicons-H3Z7DFCF.js.map +0 -1
  100. package/dist/lucide-MR32KM4G.js.map +0 -1
  101. package/dist/phosphor-ABQQL5N5.js.map +0 -1
  102. package/dist/tabler-ANUSY4D2.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,31 +1,33 @@
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-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
- import { Skeleton, TITLE_TEXT, formatLocale } from './chunk-X2HEV7FB.js';
9
- export { Skeleton } from './chunk-X2HEV7FB.js';
1
+ "use client";
2
+ 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-R5NFCEZ3.js';
3
+ export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-R5NFCEZ3.js';
4
+ import { Skeleton, TITLE_TEXT, useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-64TXZ4L7.js';
5
+ export { Skeleton, useFormatLocale, useHydrated } from './chunk-64TXZ4L7.js';
10
6
  import { useScrollEdges, EDGE_FADE } from './chunk-3BRQRKDO.js';
7
+ import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-5TUVX4B2.js';
8
+ export { Avatar } from './chunk-5TUVX4B2.js';
9
+ import { Link } from './chunk-IXR4UEF4.js';
10
+ export { CodeBlock, Divider, Link } from './chunk-IXR4UEF4.js';
11
+ import { useFillHeight, FillContext, useKeyboardScroll, FOCUS_INSET } from './chunk-SFY2KMHG.js';
12
+ import { Badge } from './chunk-PO65I3MQ.js';
13
+ export { Badge } from './chunk-PO65I3MQ.js';
14
+ import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-OTCEJN64.js';
15
+ 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, OutsideField, NumberInput, InlineFieldError, Label, AddAnotherRowCtx, Field } from './chunk-2CKSSWYG.js';
16
+ export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-2CKSSWYG.js';
17
+ import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-EKBWWA6P.js';
18
+ export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-EKBWWA6P.js';
19
+ import { elementType, formatLocale, localeChoice } from './chunk-OYNUU346.js';
11
20
  import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
12
21
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
13
22
  import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-XVB55M3N.js';
14
23
  export { useFormContext } from './chunk-XVB55M3N.js';
15
- import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-RWKCAKN3.js';
16
- export { Avatar } from './chunk-RWKCAKN3.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
24
  import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-QM4SJKWV.js';
21
25
  import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, rovingTabStop, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus } from './chunk-ZI3OQDET.js';
22
26
  export { useGridKeyboard } from './chunk-ZI3OQDET.js';
23
- import { Badge } from './chunk-EX64MN2U.js';
24
- export { Badge } from './chunk-EX64MN2U.js';
25
27
  import { useTextOverflow, Tooltip } from './chunk-MD2EFMES.js';
26
28
  export { Tooltip, useTextOverflow } from './chunk-MD2EFMES.js';
27
29
  import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
28
- import { Icon, slotIcon, warnOnce } from './chunk-HXVXJUGV.js';
30
+ import { Icon, slotIcon, warnOnce } from './chunk-TJEV7YAY.js';
29
31
  import { usePosition } from './chunk-HP7WUZPK.js';
30
32
  import { useLatest } from './chunk-WH2FUFAC.js';
31
33
  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';
@@ -459,6 +461,27 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
459
461
  ] });
460
462
  return /* @__PURE__ */ jsx(Tooltip, { content: selectedLabel, disabled: !labelTruncated || open, children: triggerContent });
461
463
  }
464
+
465
+ // ../../showcase/src/lib/locale-cache.ts
466
+ var caches = /* @__PURE__ */ new Set();
467
+ function localeCache(limit) {
468
+ const map = /* @__PURE__ */ new Map();
469
+ caches.add(map);
470
+ return (key, make) => {
471
+ const hit = map.get(key);
472
+ if (hit !== void 0) {
473
+ map.delete(key);
474
+ map.set(key, hit);
475
+ return hit;
476
+ }
477
+ const made = make();
478
+ map.set(key, made);
479
+ if (map.size > limit) map.delete(map.keys().next().value);
480
+ return made;
481
+ };
482
+ }
483
+
484
+ // ../../showcase/src/lib/time-zones.ts
462
485
  function canonicalTimeZone(zone) {
463
486
  try {
464
487
  return new Intl.DateTimeFormat("en-US", { timeZone: zone }).resolvedOptions().timeZone;
@@ -473,9 +496,9 @@ function browserTimeZone() {
473
496
  return "UTC";
474
497
  }
475
498
  }
476
- function timeZoneOffset(zone, at = /* @__PURE__ */ new Date()) {
499
+ function timeZoneOffset(zone, at = /* @__PURE__ */ new Date(), locale = formatLocale()) {
477
500
  try {
478
- return new Intl.DateTimeFormat(formatLocale(), { timeZone: zone, timeZoneName: "shortOffset" }).formatToParts(at).find((p) => p.type === "timeZoneName")?.value ?? "";
501
+ return new Intl.DateTimeFormat(locale, { timeZone: zone, timeZoneName: "shortOffset" }).formatToParts(at).find((p) => p.type === "timeZoneName")?.value ?? "";
479
502
  } catch {
480
503
  return "";
481
504
  }
@@ -485,6 +508,9 @@ function timeZones() {
485
508
  const zones = [...new Set(listed.map(canonicalTimeZone))].filter((z) => z !== "UTC").sort((a, b) => a.localeCompare(b));
486
509
  return ["UTC", ...zones];
487
510
  }
511
+ function useBrowserTimeZone() {
512
+ return useHydrated() ? browserTimeZone() : void 0;
513
+ }
488
514
  var RENAMED_CITIES = [
489
515
  ["Kolkata", ["Asia/Calcutta", "Asia/Kolkata"]],
490
516
  ["Kyiv", ["Europe/Kiev", "Europe/Kyiv"]],
@@ -503,81 +529,47 @@ var RENAMED_CITIES = [
503
529
  ["Ulaanbaatar", ["Asia/Ulan_Bator", "Asia/Ulaanbaatar"]]
504
530
  ];
505
531
  var CITY_OF = new Map(RENAMED_CITIES.flatMap(([city, zones]) => zones.map((zone) => [zone, city])));
506
- var labelOf = (zone, at) => {
507
- const offset = timeZoneOffset(zone, at);
532
+ var labelOf = (zone, at, locale) => {
533
+ const offset = timeZoneOffset(zone, at, locale);
508
534
  return offset ? `${zone.replace(/_/g, " ")} (${offset})` : zone.replace(/_/g, " ");
509
535
  };
510
- var HOUR = 36e5;
511
- var zoneList = null;
512
- function zoneOptions() {
536
+ var HOUR2 = 36e5;
537
+ var zoneLists = localeCache(8);
538
+ function zoneOptions(locale) {
513
539
  const now = Date.now();
514
- if (!zoneList || now - zoneList.at > HOUR) {
540
+ const build2 = () => {
515
541
  const at = new Date(now);
516
- zoneList = { at: now, options: timeZones().map((zone) => ({ value: zone, label: labelOf(zone, at), keywords: CITY_OF.get(zone) })) };
517
- }
518
- return zoneList.options;
542
+ return { at: now, options: timeZones().map((zone) => ({ value: zone, label: labelOf(zone, at, locale), keywords: CITY_OF.get(zone) })) };
543
+ };
544
+ const key = `${locale ?? ""}|${Math.floor(now / HOUR2)}`;
545
+ return zoneLists(key, build2).options;
519
546
  }
520
547
  function TimezoneSelect({ value, onChange, placeholder = "Search time zones\u2026", ...rest }) {
521
- const zones = zoneOptions();
548
+ const locale = useFormatLocale();
549
+ const zones = zoneOptions(locale);
522
550
  const stored = value ? canonicalTimeZone(value) : "";
551
+ const mine = useBrowserTimeZone();
523
552
  const options = useMemo(() => {
524
553
  if (!stored) {
525
- const mine = browserTimeZone();
526
- const label = zones.find((o) => o.value === mine)?.label ?? labelOf(mine, /* @__PURE__ */ new Date());
554
+ if (!mine) return zones;
555
+ const label = zones.find((o) => o.value === mine)?.label ?? labelOf(mine, /* @__PURE__ */ new Date(), locale);
527
556
  return [{ value: mine, label: `${label} \xB7 your time zone`, keywords: CITY_OF.get(mine) }, ...zones.filter((o) => o.value !== mine)];
528
557
  }
529
- return zones.some((o) => o.value === stored) ? zones : [{ value: stored, label: labelOf(stored, /* @__PURE__ */ new Date()), keywords: CITY_OF.get(stored) }, ...zones];
530
- }, [zones, stored]);
558
+ return zones.some((o) => o.value === stored) ? zones : [{ value: stored, label: labelOf(stored, /* @__PURE__ */ new Date(), locale), keywords: CITY_OF.get(stored) }, ...zones];
559
+ }, [zones, stored, mine, locale]);
531
560
  return /* @__PURE__ */ jsx(Combobox, { value: stored, onChange, options, placeholder, ...rest });
532
561
  }
533
- var MINUTE = 6e4;
534
- var HOUR2 = 60 * MINUTE;
535
- var DAY = 24 * HOUR2;
536
562
  var LONGEST_WAIT = DAY;
537
563
  var CONTROL_ANCESTOR = 'a[href], button, summary, label, [role="button"], [role="link"], [role="option"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="tab"], [role="treeitem"], [role="row"], [role="gridcell"], [role="checkbox"], [role="radio"], [role="switch"]';
538
564
  var noChange = () => () => {
539
565
  };
540
566
  var inBrowser = () => true;
541
567
  var onServer = () => false;
542
- function toMillis(time) {
543
- if (time instanceof Date) return time.getTime();
544
- if (typeof time === "number") return time;
545
- if (typeof time !== "string") return NaN;
546
- const day = /^(\d{4})-(\d{2})-(\d{2})$/.exec(time.trim());
547
- if (day) return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3])).getTime();
548
- return Date.parse(time);
549
- }
550
- function thresholdOf(hours, from) {
551
- if (typeof hours === "number" && Number.isFinite(hours) && hours >= 0) return hours;
552
- const house = PREFERENCE_HOUSE_DEFAULTS.relativeThresholdHours;
553
- warnOnce(from, `a threshold of ${String(hours)} isn't a number of hours, so the house's ${house} is used.`);
554
- return house;
555
- }
556
- function midnight(t) {
557
- const d = new Date(t);
558
- d.setHours(0, 0, 0, 0);
559
- return d.getTime();
560
- }
561
- function formatRelativeTime(time, options) {
562
- const at = toMillis(time);
563
- if (!Number.isFinite(at)) return "";
564
- const now = options.now ?? Date.now();
565
- const diff = at - now;
566
- const age = Math.abs(diff);
567
- if (age > thresholdOf(options.thresholdHours, "formatRelativeTime") * HOUR2) return new Intl.DateTimeFormat(formatLocale(), { dateStyle: "medium", timeStyle: "short" }).format(at);
568
- const rtf = new Intl.RelativeTimeFormat(formatLocale(), { numeric: "auto" });
569
- if (age < MINUTE) return rtf.format(0, "second");
570
- if (age < HOUR2) return rtf.format(Math.trunc(diff / MINUTE), "minute");
571
- if (age < DAY) return rtf.format(Math.trunc(diff / HOUR2), "hour");
572
- return rtf.format(Math.round((midnight(at) - midnight(now)) / DAY), "day");
573
- }
574
568
  function useRelativeTimeFormat() {
575
569
  const thresholdHours = useShellPreferences().relativeThresholdHours ?? PREFERENCE_HOUSE_DEFAULTS.relativeThresholdHours;
576
- return (time) => formatRelativeTime(time, { thresholdHours });
577
- }
578
- function formatFullTime(time) {
579
- const at = toMillis(time);
580
- return Number.isFinite(at) ? new Intl.DateTimeFormat(formatLocale(), { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "short" }).format(at) : "";
570
+ const locale = useFormatLocale();
571
+ const hydrated = useHydrated();
572
+ return useCallback((time) => hydrated ? formatRelativeTime(time, { thresholdHours, locale }) : "", [hydrated, thresholdHours, locale]);
581
573
  }
582
574
  function nextChange(at, now, threshold) {
583
575
  const diff = at - now;
@@ -587,7 +579,7 @@ function nextChange(at, now, threshold) {
587
579
  if (diff <= 0) return null;
588
580
  wait = age - threshold + 1;
589
581
  } else {
590
- const step = age < HOUR2 ? MINUTE : HOUR2;
582
+ const step = age < HOUR ? MINUTE : HOUR;
591
583
  const untilStep = diff > 0 ? age % step + 1 : step - age % step;
592
584
  wait = diff > 0 ? untilStep : Math.min(untilStep, threshold - age + 1);
593
585
  }
@@ -604,6 +596,7 @@ function RelativeTime({ time, thresholdHours, className }) {
604
596
  return /* @__PURE__ */ jsx(RelativeTimeAt, { at, hours, className }, `${at}:${hours}`);
605
597
  }
606
598
  function RelativeTimeAt({ at, hours, className }) {
599
+ const locale = useFormatLocale();
607
600
  const inRows = useContext(RowsCtx);
608
601
  const [now, setNow] = useState(() => Date.now());
609
602
  const [inControl, setInControl] = useState(false);
@@ -614,12 +607,12 @@ function RelativeTimeAt({ at, hours, className }) {
614
607
  }, []);
615
608
  const mounted = useSyncExternalStore(noChange, inBrowser, onServer);
616
609
  useEffect(() => {
617
- const wait = nextChange(at, now, hours * HOUR2);
610
+ const wait = nextChange(at, now, hours * HOUR);
618
611
  if (wait === null) return;
619
612
  const timer = setTimeout(() => setNow(Date.now()), wait);
620
613
  return () => clearTimeout(timer);
621
614
  }, [at, now, hours]);
622
- const full = formatFullTime(at);
615
+ const full = formatFullTime(at, locale);
623
616
  return /* @__PURE__ */ jsx(Tooltip, { content: full, ref: placed, children: /* @__PURE__ */ jsxs(
624
617
  "time",
625
618
  {
@@ -629,7 +622,7 @@ function RelativeTimeAt({ at, hours, className }) {
629
622
  suppressHydrationWarning: true,
630
623
  className: cn("rounded-[var(--ds-radius-sm)] focus-visible:outline-2 focus-visible:outline-[var(--ds-accent-primary)] focus-visible:outline-offset-2", className),
631
624
  children: [
632
- formatRelativeTime(at, { thresholdHours: hours, now }),
625
+ formatRelativeTime(at, { thresholdHours: hours, now, locale }),
633
626
  /* @__PURE__ */ jsx("span", { className: "sr-only", suppressHydrationWarning: true, children: `, ${full}` })
634
627
  ]
635
628
  },
@@ -751,6 +744,7 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
751
744
  const prev = isVertical ? "ArrowUp" : "ArrowLeft";
752
745
  const next = isVertical ? "ArrowDown" : "ArrowRight";
753
746
  if (e.key !== prev && e.key !== next) return;
747
+ if (e.target.getAttribute("role") !== "radio") return;
754
748
  e.preventDefault();
755
749
  const currentIdx = enabledOptions.findIndex((o) => o.value === value);
756
750
  let nextIdx;
@@ -785,45 +779,48 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
785
779
  onKeyDown: handleKeyDown,
786
780
  children: options.map((option, i) => {
787
781
  const selected = option.value === value;
788
- return /* @__PURE__ */ jsxs(
789
- "button",
790
- {
791
- role: "radio",
792
- type: "button",
793
- "aria-checked": selected,
794
- "aria-disabled": option.disabled || void 0,
795
- "data-value": option.value,
796
- tabIndex: i === tabStop ? 0 : -1,
797
- disabled: option.disabled,
798
- onClick: () => {
799
- if (!option.disabled) onChange(option.value);
800
- },
801
- className: cn(
802
- "group inline-flex items-center gap-ds-2 cursor-pointer select-none text-left outline-none",
803
- option.disabled && "opacity-disabled cursor-not-allowed"
804
- ),
805
- children: [
806
- /* @__PURE__ */ jsx(
807
- "span",
808
- {
809
- className: cn(
810
- "flex items-center justify-center",
811
- "h-[18px] w-[18px] shrink-0 rounded-[var(--ds-radius-full)]",
812
- "border-[length:var(--ds-border-width)] border-[var(--ds-input-border)]",
813
- "bg-[var(--ds-input-bg)]",
814
- "transition-colors",
815
- selected && "border-[var(--ds-check-border)]",
816
- "group-focus-visible:shadow-[var(--ds-shadow-focus)]"
817
- ),
818
- children: selected && // Sprint 25.14 A20: the checked colour, as a checkbox's fill (a preset whose accent falls short of 3:1 sets it)
819
- /* @__PURE__ */ jsx("span", { className: cn("h-2.5 w-2.5 rounded-[var(--ds-radius-full)]", CHECK_FILL) })
820
- }
782
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
783
+ /* @__PURE__ */ jsxs(
784
+ "button",
785
+ {
786
+ role: "radio",
787
+ type: "button",
788
+ "aria-checked": selected,
789
+ "aria-disabled": option.disabled || void 0,
790
+ "data-value": option.value,
791
+ tabIndex: i === tabStop ? 0 : -1,
792
+ disabled: option.disabled,
793
+ onClick: () => {
794
+ if (!option.disabled) onChange(option.value);
795
+ },
796
+ className: cn(
797
+ "group inline-flex items-center gap-ds-2 cursor-pointer select-none text-left outline-none",
798
+ option.disabled && "opacity-disabled cursor-not-allowed"
821
799
  ),
822
- /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--ds-text-primary)]", children: option.label })
823
- ]
824
- },
825
- option.value
826
- );
800
+ children: [
801
+ /* @__PURE__ */ jsx(
802
+ "span",
803
+ {
804
+ className: cn(
805
+ "flex items-center justify-center",
806
+ "h-[18px] w-[18px] shrink-0 rounded-[var(--ds-radius-full)]",
807
+ "border-[length:var(--ds-border-width)] border-[var(--ds-input-border)]",
808
+ "bg-[var(--ds-input-bg)]",
809
+ "transition-colors",
810
+ selected && "border-[var(--ds-check-border)]",
811
+ "group-focus-visible:shadow-[var(--ds-shadow-focus)]"
812
+ ),
813
+ children: selected && // Sprint 25.14 A20: the checked colour, as a checkbox's fill (a preset whose accent falls short of 3:1 sets it)
814
+ /* @__PURE__ */ jsx("span", { className: cn("h-2.5 w-2.5 rounded-[var(--ds-radius-full)]", CHECK_FILL) })
815
+ }
816
+ ),
817
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--ds-text-primary)]", children: option.label })
818
+ ]
819
+ }
820
+ ),
821
+ selected && option.reveal != null && // under the option, lined up with its label (the 18 px radio and its gap), a rule on its left tying it to it
822
+ /* @__PURE__ */ jsx("div", { "data-radio-reveal": "", className: "ml-[9px] pl-[calc(9px+var(--ds-space-2))] border-l-[length:var(--ds-border-width)] border-[var(--ds-border-default)] py-ds-1", children: option.reveal })
823
+ ] }, option.value);
827
824
  })
828
825
  }
829
826
  )
@@ -836,8 +833,8 @@ function addDays(d, n) {
836
833
  }
837
834
  function addMonthsClamped(d, n) {
838
835
  const first = new Date(d.getFullYear(), d.getMonth() + n, 1);
839
- const last = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
840
- return new Date(first.getFullYear(), first.getMonth(), Math.min(d.getDate(), last));
836
+ const last2 = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
837
+ return new Date(first.getFullYear(), first.getMonth(), Math.min(d.getDate(), last2));
841
838
  }
842
839
  function dayKey(d) {
843
840
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
@@ -895,25 +892,55 @@ function dateSegments(dayFirst, daysInMonth = 31) {
895
892
  const year = { key: "year", label: "Year", min: 1, max: 9999, maxDigits: 4, placeholder: "yyyy" };
896
893
  return dayFirst ? [day, month, year] : [month, day, year];
897
894
  }
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);
895
+ var FIRST_DAY_BY_REGION = Object.fromEntries([
896
+ ...["AG", "AS", "BD", "BR", "BS", "BT", "BW", "BZ", "CA", "CO", "DM", "DO", "ET", "GT", "GU", "HK", "HN", "ID", "IL", "IN", "JM", "JP", "KE", "KH", "KR", "LA", "MH", "MM", "MO", "MT", "MX", "MZ", "NI", "NP", "PA", "PE", "PH", "PK", "PR", "PT", "PY", "SA", "SG", "SV", "TH", "TT", "TW", "UM", "US", "VE", "VI", "WS", "YE", "ZA", "ZW"].map((r) => [r, 0]),
897
+ ...["AF", "BH", "DJ", "DZ", "EG", "IQ", "IR", "JO", "KW", "LY", "OM", "QA", "SD", "SY"].map((r) => [r, 6]),
898
+ ["MV", 5]
899
+ ]);
900
+ function weekStartOf(tag) {
901
+ if (!tag) return void 0;
902
+ try {
903
+ const locale = new Intl.Locale(tag);
904
+ const info = typeof locale.getWeekInfo === "function" ? locale.getWeekInfo() : locale.weekInfo;
905
+ if (info && typeof info.firstDay === "number" && info.firstDay >= 1 && info.firstDay <= 7) return info.firstDay % 7;
906
+ const region = locale.region ?? locale.maximize().region;
907
+ if (!region) return void 0;
908
+ return FIRST_DAY_BY_REGION[region] ?? 1;
909
+ } catch {
910
+ return void 0;
908
911
  }
909
- return f;
910
912
  }
911
- function monthName(month, width = "long") {
912
- return dtf({ month: width }).format(new Date(2026, month, 1));
913
+ var last = null;
914
+ function firstDayFor(spoken, page) {
915
+ for (const tag of [...spoken, page]) {
916
+ const found = weekStartOf(tag);
917
+ if (found !== void 0) return found;
918
+ }
919
+ return 1;
920
+ }
921
+ function regionWeekStart() {
922
+ if (typeof document === "undefined") return 1;
923
+ const spoken = typeof navigator === "undefined" ? [] : navigator.languages?.length ? navigator.languages : navigator.language ? [navigator.language] : [];
924
+ const page = formatLocale();
925
+ const key = `${spoken.join(",")}|${page ?? ""}`;
926
+ if (last?.key === key) return last.day;
927
+ const day = firstDayFor(spoken, page);
928
+ last = { key, day };
929
+ return day;
930
+ }
931
+ function useWeekStart(own, locale) {
932
+ const readable = useHydrated();
933
+ const choice = useContext(LocaleContext);
934
+ if (own !== void 0) return own;
935
+ if (locale !== void 0) return weekStartOf(locale) ?? 1;
936
+ if (readable) return regionWeekStart();
937
+ return choice?.hasHeader ? firstDayFor(choice.spoken, choice.server) : 1;
913
938
  }
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)));
939
+
940
+ // ../../showcase/src/components/Calendar/calendar-utils.ts
941
+ var formatters = localeCache(512);
942
+ function dtfFor(locale, options) {
943
+ return formatters(`${locale ?? ""}|${JSON.stringify(options)}`, () => new Intl.DateTimeFormat(locale, options));
917
944
  }
918
945
  function isSameDay(a, b) {
919
946
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
@@ -921,9 +948,6 @@ function isSameDay(a, b) {
921
948
  function isSameMonth(a, b) {
922
949
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
923
950
  }
924
- function isToday(d) {
925
- return isSameDay(d, /* @__PURE__ */ new Date());
926
- }
927
951
  function isWeekend(d) {
928
952
  const day = d.getDay();
929
953
  return day === 0 || day === 6;
@@ -989,8 +1013,8 @@ function eventSpan(e) {
989
1013
  const first = startOfDay(e.start);
990
1014
  if (!e.end) return { first, last: first };
991
1015
  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 };
1016
+ const last2 = startOfDay(lastInstant);
1017
+ return { first, last: last2 < first ? first : last2 };
994
1018
  }
995
1019
  function isBarEvent(e) {
996
1020
  if (e.allDay) return true;
@@ -1002,10 +1026,24 @@ function isBarEvent(e) {
1002
1026
  function getEventsForDay(events, date) {
1003
1027
  const day = startOfDay(date).getTime();
1004
1028
  return events.filter((e) => {
1005
- const { first, last } = eventSpan(e);
1006
- return first.getTime() <= day && day <= last.getTime();
1029
+ const { first, last: last2 } = eventSpan(e);
1030
+ return first.getTime() <= day && day <= last2.getTime();
1007
1031
  });
1008
1032
  }
1033
+ function groupEventsByDay(events, from, to) {
1034
+ const out = /* @__PURE__ */ new Map();
1035
+ const lo = startOfDay(from), hi = startOfDay(to);
1036
+ for (const e of events) {
1037
+ const { first, last: last2 } = eventSpan(e);
1038
+ for (let day = first < lo ? lo : first; day <= last2 && day < hi; day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1)) {
1039
+ const key = dayKey(day);
1040
+ const list2 = out.get(key);
1041
+ if (list2) list2.push(e);
1042
+ else out.set(key, [e]);
1043
+ }
1044
+ }
1045
+ return out;
1046
+ }
1009
1047
  function dayPieces(events, date) {
1010
1048
  const dayStart = startOfDay(date);
1011
1049
  const next = new Date(dayStart.getFullYear(), dayStart.getMonth(), dayStart.getDate() + 1);
@@ -1020,86 +1058,100 @@ function dayPieces(events, date) {
1020
1058
  }
1021
1059
  return pieces.sort((a, b) => a.start.getTime() - b.start.getTime() || b.end.getTime() - a.end.getTime());
1022
1060
  }
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
1061
  function endDays(start, end) {
1046
1062
  return end > start ? daysBetween(start, new Date(end.getTime() - 1)) : daysBetween(start, end);
1047
1063
  }
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);
1064
+ function makeDateWords(locale) {
1065
+ const dtf = (options) => dtfFor(locale, options);
1066
+ const twelveHour = () => /^h1[12]$/.test(dtf({ hour: "numeric" }).resolvedOptions().hourCycle ?? "");
1067
+ const timeOptions = () => ({ hour: twelveHour() ? "numeric" : "2-digit", minute: "2-digit" });
1068
+ const dayOffset = (days) => new Intl.NumberFormat(locale, { signDisplay: "always" }).format(days);
1069
+ function monthName2(month, width = "long") {
1070
+ return dtf({ month: width }).format(new Date(2026, month, 1));
1071
+ }
1072
+ function weekdayNames(weekStartsOn, width = "short") {
1073
+ const f = dtf({ weekday: width });
1074
+ return Array.from({ length: 7 }, (_, i) => f.format(new Date(2026, 0, 4 + weekStartsOn + i)));
1081
1075
  }
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)}`;
1076
+ function formatTime2(d) {
1077
+ return dtf(timeOptions()).format(d);
1086
1078
  }
1087
- return dtf({ weekday: "short", day: "numeric", month: "short", hour: "numeric", minute: "2-digit" }).formatRange(e.start, e.end);
1079
+ function formatHourLabel(hour) {
1080
+ const at = new Date(2026, 0, 5, hour);
1081
+ return twelveHour() ? dtf({ hour: "numeric" }).format(at) : dtf({ hour: "2-digit", minute: "2-digit" }).format(at);
1082
+ }
1083
+ function formatTimeRange(start, end) {
1084
+ if (!end) return formatTime2(start);
1085
+ const f = dtf(timeOptions());
1086
+ if (daysBetween(start, end) === 0) return f.formatRange(start, end);
1087
+ const sep = f.formatRangeToParts(start, end).find((p) => p.source === "shared" && p.type === "literal")?.value ?? " \u2013 ";
1088
+ const days = endDays(start, end);
1089
+ return days === 0 ? `${f.format(start)}${sep}${f.format(end)}` : `${f.format(start)}${sep}${f.format(end)} (${dayOffset(days)})`;
1090
+ }
1091
+ function speakTimeRange(start, end) {
1092
+ if (!end) return formatTime2(start);
1093
+ const days = endDays(start, end);
1094
+ const to = `${formatTime2(start)} to ${formatTime2(end)}`;
1095
+ return days === 0 ? to : days === 1 ? `${to} the next day` : `${to}, ${days} days later`;
1096
+ }
1097
+ function formatMonthYear(d) {
1098
+ return dtf({ month: "long", year: "numeric" }).format(d);
1099
+ }
1100
+ function formatDayLong(d) {
1101
+ return dtf({ weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
1102
+ }
1103
+ function formatDayShort(d) {
1104
+ return dtf({ weekday: "short", day: "numeric", month: "short" }).format(d);
1105
+ }
1106
+ function formatDayMedium(d) {
1107
+ return dtf({ day: "numeric", month: "short", year: "numeric" }).format(d);
1108
+ }
1109
+ function formatDayRange(start, end) {
1110
+ return dtf({ day: "numeric", month: "short", year: "numeric" }).formatRange(startOfDay(start), startOfDay(end));
1111
+ }
1112
+ function formatMonthRange(start, end) {
1113
+ return dtf({ month: "long", year: "numeric" }).formatRange(new Date(start.getFullYear(), start.getMonth(), 1), new Date(end.getFullYear(), end.getMonth(), 1));
1114
+ }
1115
+ function formatWeekRange(d, weekStartsOn = 0) {
1116
+ return dtf({ day: "numeric", month: "short", year: "numeric" }).formatRange(startOfWeek(d, weekStartsOn), startOfDay(endOfWeek(d, weekStartsOn)));
1117
+ }
1118
+ function formatEventWhen(e, spoken = false) {
1119
+ const { first, last: last2 } = eventSpan(e);
1120
+ const multiDay = last2 > first;
1121
+ if (e.allDay) {
1122
+ if (!multiDay) return spoken ? formatDayLong(first) : formatDayShort(first);
1123
+ return dtf(spoken ? { weekday: "long", day: "numeric", month: "long" } : { day: "numeric", month: "short" }).formatRange(first, last2);
1124
+ }
1125
+ if (!e.end || !isBarEvent(e)) {
1126
+ const day = spoken ? formatDayLong(e.start) : formatDayShort(e.start);
1127
+ const end = e.end && e.end > e.start ? e.end : void 0;
1128
+ return `${day}, ${spoken ? speakTimeRange(e.start, end) : formatTimeRange(e.start, end)}`;
1129
+ }
1130
+ return dtf({ weekday: "short", day: "numeric", month: "short", hour: "numeric", minute: "2-digit" }).formatRange(e.start, e.end);
1131
+ }
1132
+ return { monthName: monthName2, weekdayNames, formatTime: formatTime2, formatHourLabel, formatTimeRange, speakTimeRange, formatMonthYear, formatDayLong, formatDayShort, formatDayMedium, formatDayRange, formatMonthRange, formatWeekRange, formatEventWhen };
1133
+ }
1134
+ var wordsByLocale = localeCache(32);
1135
+ function dateWords(locale) {
1136
+ return wordsByLocale(locale ?? "", () => makeDateWords(locale));
1137
+ }
1138
+ function useDateWords() {
1139
+ return dateWords(useFormatLocale());
1088
1140
  }
1089
1141
  function layoutWeek(events, days, capacity) {
1090
1142
  const weekStart = startOfDay(days[0]);
1091
1143
  const weekEnd = startOfDay(days[days.length - 1]);
1092
1144
  const items = [];
1093
1145
  for (const event of events) {
1094
- const { first, last } = eventSpan(event);
1095
- if (last < weekStart || first > weekEnd) continue;
1146
+ const { first, last: last2 } = eventSpan(event);
1147
+ if (last2 < weekStart || first > weekEnd) continue;
1096
1148
  items.push({
1097
1149
  event,
1098
1150
  startCol: Math.max(0, daysBetween(weekStart, first)),
1099
- endCol: Math.min(days.length - 1, daysBetween(weekStart, last)),
1151
+ endCol: Math.min(days.length - 1, daysBetween(weekStart, last2)),
1100
1152
  before: first < weekStart,
1101
- after: last > weekEnd,
1102
- length: daysBetween(first, last) + 1
1153
+ after: last2 > weekEnd,
1154
+ length: daysBetween(first, last2) + 1
1103
1155
  });
1104
1156
  }
1105
1157
  items.sort((a, b) => {
@@ -1195,48 +1247,50 @@ var ENGLISH_MONTHS = [
1195
1247
  function getDaysInMonth(year, month) {
1196
1248
  return new Date(year, month + 1, 0).getDate();
1197
1249
  }
1198
- function getFirstDayOfWeek(year, month) {
1199
- return new Date(year, month, 1).getDay();
1250
+ function getFirstDayOfWeek(year, month, weekStartsOn) {
1251
+ return (new Date(year, month, 1).getDay() - weekStartsOn + 7) % 7;
1200
1252
  }
1201
1253
  function isSameDay2(a, b) {
1202
1254
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1203
1255
  }
1204
- function localeDayFirst() {
1256
+ function localeDayFirst(locale = formatLocale()) {
1205
1257
  try {
1206
- const parts = new Intl.DateTimeFormat(formatLocale()).formatToParts(new Date(2e3, 11, 25));
1258
+ const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(2e3, 11, 25));
1207
1259
  return parts.findIndex((p) => p.type === "day") < parts.findIndex((p) => p.type === "month");
1208
1260
  } catch {
1209
1261
  return true;
1210
1262
  }
1211
1263
  }
1264
+ var NO_SEGMENTS = { day: null, month: null, year: null };
1212
1265
  function validDate(year, monthIndex2, day) {
1213
1266
  if (monthIndex2 < 0 || monthIndex2 > 11 || day < 1) return null;
1214
1267
  const d = new Date(year, monthIndex2, day);
1215
1268
  return d.getFullYear() === year && d.getMonth() === monthIndex2 && d.getDate() === day ? d : null;
1216
1269
  }
1217
- function monthIndex(name) {
1270
+ function monthIndex(name, locale) {
1218
1271
  const typed = name.toLocaleLowerCase().replace(/\.$/, "");
1219
1272
  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));
1273
+ const { monthName: monthName2 } = dateWords(locale);
1274
+ const lists = [ENGLISH_MONTHS, Array.from({ length: 12 }, (_, i) => fold(monthName2(i, "long"))), Array.from({ length: 12 }, (_, i) => fold(monthName2(i, "short")))];
1275
+ for (const list2 of lists) {
1276
+ const i = list2.findIndex((m) => m.startsWith(typed) || typed.length >= 3 && typed.startsWith(m));
1223
1277
  if (i >= 0) return i;
1224
1278
  }
1225
1279
  return -1;
1226
1280
  }
1227
- function parseTypedDate(text, dayFirst = localeDayFirst()) {
1281
+ function parseTypedDate(text, dayFirst = localeDayFirst(), locale = formatLocale()) {
1228
1282
  const t = text.trim().replace(/\s+/g, " ");
1229
1283
  if (!t) return null;
1230
1284
  let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(t);
1231
1285
  if (m) return validDate(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
1232
1286
  m = /^(\p{L}{3,})\.?\s+(\d{1,2}),?\s+(\d{4})$/u.exec(t);
1233
1287
  if (m) {
1234
- const mi = monthIndex(m[1]);
1288
+ const mi = monthIndex(m[1], locale);
1235
1289
  return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[2]));
1236
1290
  }
1237
1291
  m = /^(\d{1,2})\.?\s+(\p{L}{3,})\.?,?\s+(\d{4})$/u.exec(t);
1238
1292
  if (m) {
1239
- const mi = monthIndex(m[2]);
1293
+ const mi = monthIndex(m[2], locale);
1240
1294
  return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[1]));
1241
1295
  }
1242
1296
  m = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2,4})$/.exec(t);
@@ -1248,7 +1302,12 @@ function parseTypedDate(text, dayFirst = localeDayFirst()) {
1248
1302
  }
1249
1303
  return null;
1250
1304
  }
1251
- function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2, ...wiring }) {
1305
+ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date", disabled, className, ref, container, error = false, label, id, size: sizeProp, min, max, range: range2, weekStartsOn: weekStartsOnProp, ...wiring }) {
1306
+ const weekStartsOn = useWeekStart(weekStartsOnProp);
1307
+ const locale = useFormatLocale();
1308
+ const { formatDayLong, formatMonthYear, weekdayNames } = useDateWords();
1309
+ const hydrated = useHydrated();
1310
+ const today = useToday();
1252
1311
  const onChange = useReportedChange(onChangeProp, value);
1253
1312
  const contextSize = useContext(ControlSizeContext);
1254
1313
  const size = sizeProp ?? contextSize ?? "md";
@@ -1266,8 +1325,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1266
1325
  const year = viewDate.getFullYear();
1267
1326
  const month = viewDate.getMonth();
1268
1327
  const daysInMonth = getDaysInMonth(year, month);
1269
- const firstDay = getFirstDayOfWeek(year, month);
1270
- const today = useMemo(() => /* @__PURE__ */ new Date(), []);
1328
+ const firstDay = getFirstDayOfWeek(year, month, weekStartsOn);
1271
1329
  const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
1272
1330
  const isDisabledDay = (d) => !!min && d < startOf(min) || !!max && d > startOf(max);
1273
1331
  const [focusDate, setFocusDate] = useState(() => startOf(value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date()));
@@ -1275,7 +1333,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1275
1333
  if (open !== prevOpen) {
1276
1334
  setPrevOpen(open);
1277
1335
  if (open) {
1278
- const start = startOf(value ?? range2?.start ?? range2?.end ?? today);
1336
+ const start = startOf(value ?? range2?.start ?? range2?.end ?? today ?? /* @__PURE__ */ new Date());
1279
1337
  setFocusDate(start);
1280
1338
  setViewDate(new Date(start.getFullYear(), start.getMonth(), 1));
1281
1339
  }
@@ -1289,7 +1347,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1289
1347
  active: focusDate,
1290
1348
  setActive: showDay,
1291
1349
  keyOf: dayKey,
1292
- move: (from, e) => moveDay(from, e.key, e.shiftKey, 0)
1350
+ move: (from, e) => moveDay(from, e.key, e.shiftKey, weekStartsOn)
1293
1351
  });
1294
1352
  const prevMonth = () => {
1295
1353
  setViewDate(new Date(year, month - 1, 1));
@@ -1311,7 +1369,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1311
1369
  onChange(d);
1312
1370
  setOpen(false);
1313
1371
  };
1314
- const dayFirst = useMemo(() => localeDayFirst(), []);
1372
+ const dayFirst = useMemo(() => localeDayFirst(locale), [locale]);
1315
1373
  const [segs, setSegs] = useState(() => value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
1316
1374
  const [buffers, setBuffers] = useState({});
1317
1375
  const lastCommitted = useRef(null);
@@ -1320,7 +1378,7 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1320
1378
  setSegs(value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
1321
1379
  setBuffers({});
1322
1380
  }, [value]);
1323
- const specs = dateSegments(dayFirst, segs.month && segs.year ? getDaysInMonth(segs.year, segs.month - 1) : 31);
1381
+ const specs = dateSegments(dayFirst, hydrated && segs.month && segs.year ? getDaysInMonth(segs.year, segs.month - 1) : 31);
1324
1382
  const segmentRefs = useRef([]);
1325
1383
  const focusSegment = (i) => {
1326
1384
  segmentRefs.current[Math.max(0, Math.min(specs.length - 1, i))]?.focus();
@@ -1360,15 +1418,15 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1360
1418
  setViewDate(d);
1361
1419
  }
1362
1420
  };
1363
- const setSegment = (key, v, commit = true) => {
1421
+ const setSegment = (key, v, commit2 = true) => {
1364
1422
  setSegs((cur) => {
1365
1423
  const next = { ...cur, [key]: v };
1366
- if (commit) commitIfComplete(next);
1424
+ if (commit2) commitIfComplete(next);
1367
1425
  return next;
1368
1426
  });
1369
1427
  };
1370
1428
  const applyTyped = (text) => {
1371
- const d = parseTypedDate(text, dayFirst);
1429
+ const d = parseTypedDate(text, dayFirst, locale);
1372
1430
  if (!d) return;
1373
1431
  const next = { day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear() };
1374
1432
  setSegs(next);
@@ -1420,12 +1478,13 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1420
1478
  if (r.advance) focusSegment(index + 1);
1421
1479
  }
1422
1480
  };
1481
+ const drawn = hydrated ? segs : NO_SEGMENTS;
1423
1482
  const shown = (spec) => {
1424
1483
  const k = spec.key;
1425
1484
  const b = buffers[k];
1426
- if (b && segs[k] === null) return b;
1427
- if (segs[k] === null) return spec.placeholder;
1428
- return k === "year" ? String(segs[k]) : pad2(segs[k]);
1485
+ if (b && drawn[k] === null) return b;
1486
+ if (drawn[k] === null) return spec.placeholder;
1487
+ return k === "year" ? String(drawn[k]) : pad2(drawn[k]);
1429
1488
  };
1430
1489
  const selectDay = (day) => {
1431
1490
  const d = new Date(year, month, day);
@@ -1499,11 +1558,11 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1499
1558
  "aria-label": spec.label,
1500
1559
  "aria-valuemin": spec.min,
1501
1560
  "aria-valuemax": spec.max,
1502
- "aria-valuenow": segs[k] ?? void 0,
1503
- "aria-valuetext": segs[k] === null ? "empty" : String(segs[k]),
1561
+ "aria-valuenow": drawn[k] ?? void 0,
1562
+ "aria-valuetext": drawn[k] === null ? "empty" : String(drawn[k]),
1504
1563
  onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
1505
1564
  onClick: (e) => e.stopPropagation(),
1506
- className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
1565
+ className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-primary)] cursor-text", drawn[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
1507
1566
  children: shown(spec)
1508
1567
  }
1509
1568
  )
@@ -1566,13 +1625,13 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1566
1625
  className: "flex flex-col gap-ds-0.5 tabular-nums",
1567
1626
  style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1568
1627
  children: [
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)) }),
1628
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-ds-0.5 mb-ds-0.5", children: weekdayNames(weekStartsOn, "narrow").map((d, i) => /* @__PURE__ */ jsx("div", { role: "columnheader", "aria-label": weekdayNames(weekStartsOn, "long")[i], className: "text-center text-2xs font-medium text-[var(--ds-text-muted)] py-ds-1", children: d }, i)) }),
1570
1629
  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) => {
1571
1630
  if (day === null) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, `empty-${i}`);
1572
1631
  const dateObj = new Date(year, month, day);
1573
1632
  const isSelected = !!value && isSameDay2(dateObj, value);
1574
1633
  const isOtherEnd = !!otherEnd && isSameDay2(dateObj, otherEnd);
1575
- const isToday2 = isSameDay2(dateObj, today);
1634
+ const isToday = today !== null && isSameDay2(dateObj, today);
1576
1635
  const between = inBand(dateObj);
1577
1636
  const dis = isDisabledDay(dateObj);
1578
1637
  return /* @__PURE__ */ jsx(
@@ -1589,13 +1648,13 @@ function DatePicker({ value, onChange: onChangeProp, placeholder = "Pick a date"
1589
1648
  },
1590
1649
  onMouseEnter: () => setHover(dateObj),
1591
1650
  "aria-selected": isSelected,
1592
- "aria-current": isToday2 ? "date" : void 0,
1651
+ "aria-current": isToday ? "date" : void 0,
1593
1652
  "aria-label": formatDayLong(dateObj),
1594
1653
  className: cn(
1595
1654
  "w-full aspect-square flex items-center justify-center text-xs",
1596
1655
  "transition-colors cursor-pointer",
1597
1656
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
1598
- isSelected ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : isOtherEnd ? "bg-[var(--ds-accent-muted)] text-[var(--ds-accent-subtle-text)] font-semibold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : isToday2 ? "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)]",
1657
+ isSelected ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)] font-bold rounded-[var(--ds-radius-sm)]" : isOtherEnd ? "bg-[var(--ds-accent-muted)] text-[var(--ds-accent-subtle-text)] font-semibold rounded-[var(--ds-radius-sm)]" : between ? "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)] rounded-none" : isToday ? "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)]",
1599
1658
  dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1600
1659
  ),
1601
1660
  children: day
@@ -1692,10 +1751,14 @@ function DateRangePicker({
1692
1751
  min,
1693
1752
  max,
1694
1753
  presets = DEFAULT_DATE_RANGE_PRESETS,
1695
- weekStartsOn = 0,
1754
+ weekStartsOn: weekStartsOnProp,
1696
1755
  size: sizeProp,
1697
1756
  ...wiring
1698
1757
  }) {
1758
+ const weekStartsOn = useWeekStart(weekStartsOnProp);
1759
+ const { weekdayNames, formatDayLong, formatDayMedium, formatDayRange, formatMonthRange, formatMonthYear } = useDateWords();
1760
+ const hydrated = useHydrated();
1761
+ const now = useToday();
1699
1762
  const onChange = useReportedChange(onChangeProp, value);
1700
1763
  const contextSize = useContext(ControlSizeContext);
1701
1764
  const size = sizeProp ?? contextSize ?? "md";
@@ -1709,7 +1772,7 @@ function DateRangePicker({
1709
1772
  const triggerId = control.id ?? generatedId;
1710
1773
  const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
1711
1774
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1712
- const today = useMemo(() => startOfDay(/* @__PURE__ */ new Date()), []);
1775
+ const today = now && startOfDay(now);
1713
1776
  const dayNames = weekdayNames(weekStartsOn, "narrow");
1714
1777
  const dayNamesLong = weekdayNames(weekStartsOn, "long");
1715
1778
  const gridRef = useRef(null);
@@ -1718,7 +1781,7 @@ function DateRangePicker({
1718
1781
  if (open !== prevOpen) {
1719
1782
  setPrevOpen(open);
1720
1783
  if (open) {
1721
- const start = startOfDay(value.start ?? today);
1784
+ const start = startOfDay(value.start ?? today ?? /* @__PURE__ */ new Date());
1722
1785
  setFocusDate(start);
1723
1786
  setViewDate(new Date(start.getFullYear(), start.getMonth(), 1));
1724
1787
  }
@@ -1788,7 +1851,8 @@ function DateRangePicker({
1788
1851
  const rangeEnd2 = value.end ?? previewEnd;
1789
1852
  const inRange = (d) => !!(rangeStart && rangeEnd2 && d > rangeStart && d < rangeEnd2);
1790
1853
  const months = [viewDate, addMonths(viewDate, 1)];
1791
- const summary = value.start && value.end ? formatDayRange(value.start, value.end) : value.start ? `${formatDayMedium(value.start)} \u2013 \u2026` : null;
1854
+ const summary = !hydrated ? null : value.start && value.end ? formatDayRange(value.start, value.end) : value.start ? `${formatDayMedium(value.start)} \u2013 \u2026` : null;
1855
+ const pendingValue = !hydrated && !!value.start;
1792
1856
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1793
1857
  label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1794
1858
  control.requiredNote,
@@ -1823,92 +1887,1066 @@ function DateRangePicker({
1823
1887
  ),
1824
1888
  children: [
1825
1889
  /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)]" }),
1826
- /* @__PURE__ */ jsx("span", { className: "truncate", children: summary ?? placeholder })
1890
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: summary ?? (pendingValue ? "" : placeholder) })
1891
+ ]
1892
+ }
1893
+ ),
1894
+ open && createPortal(
1895
+ /* @__PURE__ */ jsxs(
1896
+ "div",
1897
+ {
1898
+ ref: panelRef,
1899
+ role: "dialog",
1900
+ "aria-label": "Choose a date range",
1901
+ tabIndex: -1,
1902
+ className: cn(
1903
+ "fixed z-[var(--ds-z-popover)] flex outline-none",
1904
+ "bg-[var(--ds-bg-surface-elevated)]",
1905
+ "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1906
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
1907
+ "rounded-[var(--ds-radius-md)]",
1908
+ "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]"
1909
+ ),
1910
+ style: { top: pos.top, left: pos.left },
1911
+ children: [
1912
+ presets.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-ds-0.5 p-ds-2 border-r-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] min-w-[140px]", children: presets.map((p) => /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "justify-start", onClick: () => {
1913
+ const r = p.range();
1914
+ returnFocus();
1915
+ onChange(r);
1916
+ setViewDate(r.start ?? /* @__PURE__ */ new Date());
1917
+ setOpen(false);
1918
+ }, children: p.label }, p.label)) }),
1919
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1920
+ /* @__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: [
1921
+ /* @__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" }) }),
1922
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthRange(months[0], months[1]) }),
1923
+ /* @__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 }) })
1924
+ ] }),
1925
+ /* @__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: [
1926
+ /* @__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)) }),
1927
+ 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) => {
1928
+ if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
1929
+ const isStart = !!rangeStart && isSameDay(d, rangeStart);
1930
+ const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
1931
+ const between = inRange(d);
1932
+ const dis = isDisabledDay(d);
1933
+ return /* @__PURE__ */ jsx(
1934
+ "button",
1935
+ {
1936
+ type: "button",
1937
+ role: "gridcell",
1938
+ "data-grid-key": dayKey(d),
1939
+ tabIndex: grid.tabIndexOf(d),
1940
+ "aria-disabled": dis || void 0,
1941
+ onClick: () => selectDay(d),
1942
+ onFocus: () => {
1943
+ if (!isSameDay(d, focusDate)) setFocusDate(d);
1944
+ },
1945
+ onMouseEnter: () => setHover(d),
1946
+ "aria-selected": isStart || isEnd || between,
1947
+ "aria-current": today !== null && isSameDay(d, today) ? "date" : void 0,
1948
+ "aria-label": formatDayLong(d),
1949
+ className: cn(
1950
+ "w-full aspect-square flex items-center justify-center text-xs",
1951
+ "transition-colors cursor-pointer",
1952
+ "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
1953
+ 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" : today !== null && 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)]",
1954
+ dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1955
+ ),
1956
+ children: d.getDate()
1957
+ },
1958
+ d.toISOString()
1959
+ );
1960
+ }) }, w))
1961
+ ] }, `${m.getFullYear()}-${m.getMonth()}`)) }),
1962
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center bg-[var(--ds-bg-muted)] border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-br-[var(--ds-radius-md)] px-ds-3 py-ds-2", children: [
1963
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--ds-text-muted)]", children: value.start && !value.end ? "Now pick an end date" : summary ?? "Pick a start date" }),
1964
+ (value.start || value.end) && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
1965
+ onChange({ start: null, end: null });
1966
+ setHover(null);
1967
+ }, children: "Clear" })
1968
+ ] })
1969
+ ] })
1970
+ ]
1971
+ }
1972
+ ),
1973
+ container ?? document.body
1974
+ )
1975
+ ] });
1976
+ }
1977
+
1978
+ // ../../showcase/src/lib/recurrence.ts
1979
+ var DAY_CODES = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
1980
+ var FREQS = { DAILY: "daily", WEEKLY: "weekly", MONTHLY: "monthly", YEARLY: "yearly" };
1981
+ var KNOWN_PARTS = /* @__PURE__ */ new Set(["FREQ", "INTERVAL", "WKST", "BYDAY", "BYSETPOS", "BYMONTHDAY", "BYMONTH", "UNTIL", "COUNT"]);
1982
+ var whole = (text) => text !== void 0 && /^[+-]?\d+$/.test(text) ? Number(text) : null;
1983
+ function plainDays(text) {
1984
+ const days = text.split(",").map((code) => DAY_CODES.indexOf(code));
1985
+ if (days.some((d) => d < 0) || new Set(days).size !== days.length) return null;
1986
+ return days.sort((a, b) => a - b);
1987
+ }
1988
+ function nthDay(byday, setpos) {
1989
+ const m = /^([+-]?\d)?(SU|MO|TU|WE|TH|FR|SA)$/.exec(byday);
1990
+ if (!m) return null;
1991
+ const week = m[1] !== void 0 ? setpos === void 0 ? Number(m[1]) : NaN : whole(setpos);
1992
+ if (week === null || ![1, 2, 3, 4, -1].includes(week)) return null;
1993
+ return { week, weekday: DAY_CODES.indexOf(m[2]) };
1994
+ }
1995
+ function parseUntil(text) {
1996
+ const m = /^(\d{4})(\d{2})(\d{2})(?:T(\d{2})(\d{2})(\d{2})(Z)?)?$/.exec(text);
1997
+ if (!m) return null;
1998
+ const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
1999
+ const date = m[4] === void 0 ? new Date(y, mo, d) : m[7] ? new Date(Date.UTC(y, mo, d, Number(m[4]), Number(m[5]), Number(m[6]))) : new Date(y, mo, d, Number(m[4]), Number(m[5]), Number(m[6]));
2000
+ return Number.isNaN(date.getTime()) || (m[7] ? date.getUTCMonth() : date.getMonth()) !== mo ? null : date;
2001
+ }
2002
+ function parseRRule(text) {
2003
+ if (typeof text !== "string") return null;
2004
+ const body = text.trim().replace(/^RRULE:/i, "");
2005
+ if (!body) return null;
2006
+ const parts = /* @__PURE__ */ new Map();
2007
+ for (const part of body.split(";")) {
2008
+ if (!part) continue;
2009
+ const at = part.indexOf("=");
2010
+ const key = part.slice(0, at).toUpperCase();
2011
+ if (at <= 0 || parts.has(key) || !KNOWN_PARTS.has(key)) return null;
2012
+ parts.set(key, part.slice(at + 1).toUpperCase());
2013
+ }
2014
+ const freq = FREQS[parts.get("FREQ") ?? ""];
2015
+ if (!freq) return null;
2016
+ const rule = { freq };
2017
+ if (parts.has("INTERVAL")) {
2018
+ const n = whole(parts.get("INTERVAL"));
2019
+ if (n === null || n < 1) return null;
2020
+ if (n > 1) rule.interval = n;
2021
+ }
2022
+ if (parts.has("WKST")) {
2023
+ const d = DAY_CODES.indexOf(parts.get("WKST"));
2024
+ if (d < 0) return null;
2025
+ if (d !== 1) rule.weekStart = d;
2026
+ }
2027
+ if (parts.has("UNTIL") && parts.has("COUNT")) return null;
2028
+ if (parts.has("COUNT")) {
2029
+ const n = whole(parts.get("COUNT"));
2030
+ if (n === null || n < 1) return null;
2031
+ rule.count = n;
2032
+ }
2033
+ if (parts.has("UNTIL")) {
2034
+ const until = parseUntil(parts.get("UNTIL"));
2035
+ if (!until) return null;
2036
+ rule.until = until;
2037
+ }
2038
+ const byday = parts.get("BYDAY"), setpos = parts.get("BYSETPOS"), monthday = parts.get("BYMONTHDAY"), month = parts.get("BYMONTH");
2039
+ if (freq === "daily" || freq === "weekly") {
2040
+ if (setpos !== void 0 || monthday !== void 0 || month !== void 0) return null;
2041
+ if (byday === void 0) return rule;
2042
+ if (freq === "daily" && rule.interval) return null;
2043
+ const days = plainDays(byday);
2044
+ if (!days) return null;
2045
+ return { ...rule, freq: "weekly", weekdays: days };
2046
+ }
2047
+ if (month !== void 0) {
2048
+ const m = whole(month);
2049
+ if (freq !== "yearly" || m === null || m < 1 || m > 12) return null;
2050
+ rule.month = m;
2051
+ }
2052
+ if (setpos !== void 0 && byday === void 0) return null;
2053
+ if (byday !== void 0) {
2054
+ const nth = nthDay(byday, setpos);
2055
+ if (!nth || monthday !== void 0) return null;
2056
+ rule.nth = nth;
2057
+ } else if (monthday !== void 0) {
2058
+ const d = whole(monthday);
2059
+ if (d === null || !(d === -1 || d >= 1 && d <= 31)) return null;
2060
+ rule.day = d;
2061
+ }
2062
+ if (freq === "yearly" && rule.month === void 0 !== (rule.day === void 0 && rule.nth === void 0)) return null;
2063
+ return rule;
2064
+ }
2065
+ var pad = (n, width = 2) => String(n).padStart(width, "0");
2066
+ function toRRule(rule, { allDay = false } = {}) {
2067
+ const out = [`FREQ=${rule.freq.toUpperCase()}`];
2068
+ if (rule.interval && rule.interval > 1) out.push(`INTERVAL=${rule.interval}`);
2069
+ if (rule.month !== void 0) out.push(`BYMONTH=${rule.month}`);
2070
+ if (rule.day !== void 0) out.push(`BYMONTHDAY=${rule.day}`);
2071
+ const weekStart = rule.weekStart ?? 1;
2072
+ if (rule.weekdays?.length) out.push(`BYDAY=${inWeekOrder(rule.weekdays, weekStart).map((d) => DAY_CODES[d]).join(",")}`);
2073
+ if (rule.nth) out.push(`BYDAY=${rule.nth.week}${DAY_CODES[rule.nth.weekday]}`);
2074
+ if (weekStart !== 1) out.push(`WKST=${DAY_CODES[weekStart]}`);
2075
+ if (rule.until) {
2076
+ const u = rule.until;
2077
+ out.push(allDay ? `UNTIL=${u.getFullYear()}${pad(u.getMonth() + 1)}${pad(u.getDate())}` : `UNTIL=${u.getUTCFullYear()}${pad(u.getUTCMonth() + 1)}${pad(u.getUTCDate())}T${pad(u.getUTCHours())}${pad(u.getUTCMinutes())}${pad(u.getUTCSeconds())}Z`);
2078
+ }
2079
+ if (rule.count !== void 0) out.push(`COUNT=${rule.count}`);
2080
+ return `RRULE:${out.join(";")}`;
2081
+ }
2082
+ var inWeekOrder = (days, weekStart) => [...days].sort((a, b) => (a - weekStart + 7) % 7 - (b - weekStart + 7) % 7);
2083
+ function readRecurrence(value) {
2084
+ if (!value) return null;
2085
+ if (typeof value === "string") return parseRRule(value);
2086
+ if (value.until === void 0 || value.until instanceof Date) return value;
2087
+ const until = new Date(value.until);
2088
+ return Number.isNaN(until.getTime()) ? null : { ...value, until };
2089
+ }
2090
+ var daysIn = (year, month) => new Date(year, month + 1, 0).getDate();
2091
+ var atDay = (year, month, day, time) => new Date(year, month, day, time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds());
2092
+ var dayIndex = (d) => Math.round(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / 864e5);
2093
+ var isMidnight = (d) => d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0 && d.getMilliseconds() === 0;
2094
+ function nthWeekdayOf(year, month, week, weekday2) {
2095
+ const last2 = daysIn(year, month);
2096
+ if (week === -1) return last2 - (new Date(year, month, last2).getDay() - weekday2 + 7) % 7;
2097
+ const day = 1 + (weekday2 - new Date(year, month, 1).getDay() + 7) % 7 + (week - 1) * 7;
2098
+ return day <= last2 ? day : null;
2099
+ }
2100
+ function nthOf(date) {
2101
+ const d = date.getDate();
2102
+ return { week: d + 7 > daysIn(date.getFullYear(), date.getMonth()) ? -1 : Math.ceil(d / 7), weekday: date.getDay() };
2103
+ }
2104
+ function nthChoices(date) {
2105
+ const d = date.getDate(), weekday2 = date.getDay();
2106
+ const out = [];
2107
+ if (d <= 28) out.push({ week: Math.ceil(d / 7), weekday: weekday2 });
2108
+ if (d + 7 > daysIn(date.getFullYear(), date.getMonth())) out.push({ week: -1, weekday: weekday2 });
2109
+ return out;
2110
+ }
2111
+ function weekOnMoved(week, to) {
2112
+ const d = to.getDate();
2113
+ if (week === -1 && d + 7 > daysIn(to.getFullYear(), to.getMonth())) return -1;
2114
+ return d <= 28 ? Math.ceil(d / 7) : -1;
2115
+ }
2116
+ function withinUntil(at, until) {
2117
+ return at <= until || isMidnight(until) && dayIndex(at) === dayIndex(until);
2118
+ }
2119
+ function* starts(rule, first) {
2120
+ const interval = Math.max(1, Math.floor(rule.interval ?? 1));
2121
+ const firstDay = dayIndex(first);
2122
+ let produced = 0;
2123
+ const accept = (d) => {
2124
+ if (rule.until && !withinUntil(d, rule.until)) return false;
2125
+ return rule.count === void 0 || produced < rule.count;
2126
+ };
2127
+ if (rule.count !== void 0 && rule.count < 1) return;
2128
+ produced++;
2129
+ yield first;
2130
+ const y0 = first.getFullYear(), m0 = first.getMonth();
2131
+ for (let k = 0, empty = 0; k < 146100 && empty < 400 * 12; k++) {
2132
+ const dates = [];
2133
+ if (rule.freq === "daily") dates.push(atDay(y0, m0, first.getDate() + k * interval, first));
2134
+ else if (rule.freq === "weekly") {
2135
+ const weekStart = rule.weekStart ?? 1;
2136
+ const base = first.getDate() - (first.getDay() - weekStart + 7) % 7 + k * interval * 7;
2137
+ for (const wd of inWeekOrder(rule.weekdays?.length ? rule.weekdays : [first.getDay()], weekStart)) dates.push(atDay(y0, m0, base + (wd - weekStart + 7) % 7, first));
2138
+ } else {
2139
+ const monthsFrom = rule.freq === "monthly" ? k * interval : k * interval * 12;
2140
+ const at = new Date(y0, m0 + monthsFrom, 1);
2141
+ const year = at.getFullYear(), month = rule.freq === "yearly" && rule.month !== void 0 ? rule.month - 1 : at.getMonth();
2142
+ let day;
2143
+ if (rule.nth) day = nthWeekdayOf(year, month, rule.nth.week, rule.nth.weekday);
2144
+ else if (rule.day === -1) day = daysIn(year, month);
2145
+ else day = rule.day ?? first.getDate();
2146
+ if (day !== null && day <= daysIn(year, month)) dates.push(atDay(year, month, day, first));
2147
+ }
2148
+ let any = false;
2149
+ for (const d of dates) {
2150
+ if (dayIndex(d) <= firstDay) continue;
2151
+ if (!accept(d)) return;
2152
+ produced++;
2153
+ any = true;
2154
+ yield d;
2155
+ }
2156
+ empty = any ? 0 : empty + 1;
2157
+ }
2158
+ }
2159
+ function lengthOf(event) {
2160
+ return event.end ? { days: dayIndex(event.end) - dayIndex(event.start), end: event.end } : null;
2161
+ }
2162
+ function occurrenceAt(event, start) {
2163
+ const { repeat: _repeat, exceptions: _exceptions, ...rest } = event;
2164
+ const length = lengthOf(event);
2165
+ const date = dayKey(start);
2166
+ const end = length ? atDay(start.getFullYear(), start.getMonth(), start.getDate() + length.days, length.end) : void 0;
2167
+ return { ...rest, id: `${event.id}::${date}`, start, ...end ? { end } : {}, series: { id: event.id, date } };
2168
+ }
2169
+ function stopOf(event) {
2170
+ if (event.allDay) {
2171
+ const last2 = event.end ?? event.start;
2172
+ return new Date(last2.getFullYear(), last2.getMonth(), last2.getDate() + 1);
2173
+ }
2174
+ return event.end ?? new Date(event.start.getTime() + 36e5);
2175
+ }
2176
+ function occurrences(event, from, to) {
2177
+ const rule = readRecurrence(event.repeat);
2178
+ if (!rule) return event.start < to && stopOf(event) > from ? [event] : [];
2179
+ const skip = new Set(event.exceptions ?? []);
2180
+ const reach = stopOf(event).getTime() - event.start.getTime();
2181
+ const out = [];
2182
+ for (const start of starts(rule, event.start)) {
2183
+ if (start >= to) break;
2184
+ if (start.getTime() + reach + 864e5 <= from.getTime()) continue;
2185
+ if (skip.has(dayKey(start))) continue;
2186
+ const occurrence = occurrenceAt(event, start);
2187
+ if (stopOf(occurrence) > from) out.push(occurrence);
2188
+ }
2189
+ return out;
2190
+ }
2191
+ function startsBefore(rule, first, date) {
2192
+ let n = 0;
2193
+ for (const start of starts(rule, first)) {
2194
+ if (dayKey(start) >= date) break;
2195
+ n++;
2196
+ }
2197
+ return n;
2198
+ }
2199
+ var asWritten = (event, rule) => typeof event.repeat === "string" ? toRRule(rule, { allDay: event.allDay }) : rule;
2200
+ var shiftKey = (key, days) => {
2201
+ const [y, m, d] = key.split("-").map(Number);
2202
+ return dayKey(new Date(y, m - 1, d + days));
2203
+ };
2204
+ var occurrenceOnDay = (series, date) => {
2205
+ const [y, m, d] = date.split("-").map(Number);
2206
+ return occurrenceAt(series, atDay(y, m - 1, d, series.start));
2207
+ };
2208
+ var editedOf = (events, seriesId) => events.filter((e) => e.series?.id === seriesId && e.id !== seriesId);
2209
+ function occurrenceOn(series, date, events) {
2210
+ const edited = events && editedOf(events, series.id).find((e) => e.series.date === date);
2211
+ if (edited) return edited;
2212
+ const [y, m, d] = date.split("-").map(Number);
2213
+ return occurrences(series, new Date(y, m - 1, d), new Date(y, m - 1, d + 1)).find((o) => "series" in o && o.series?.date === date);
2214
+ }
2215
+ var editOf = (changes) => {
2216
+ if (!changes) return {};
2217
+ const { id: _id, repeat: _repeat, exceptions: _exceptions, ...rest } = changes;
2218
+ delete rest.series;
2219
+ return rest;
2220
+ };
2221
+ var repeatChanged = (series, changes) => {
2222
+ if (!changes || !("repeat" in changes)) return false;
2223
+ const given = changes.repeat;
2224
+ if (given === series.repeat) return false;
2225
+ const a = readRecurrence(given), b = readRecurrence(series.repeat);
2226
+ return !(a && b && sameRecurrence(a, b));
2227
+ };
2228
+ function sameValue(a, b) {
2229
+ if (a instanceof Date || b instanceof Date) return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
2230
+ if (a && b && typeof a === "object" && typeof b === "object") return JSON.stringify(a) === JSON.stringify(b);
2231
+ return a === b;
2232
+ }
2233
+ function editFrom(opened, series, changes) {
2234
+ const was = opened;
2235
+ const edit = {};
2236
+ for (const [key, value] of Object.entries(editOf(changes))) if (!sameValue(value, was[key])) edit[key] = value;
2237
+ if (repeatChanged(series, changes)) edit.repeat = changes.repeat;
2238
+ return edit;
2239
+ }
2240
+ function moveRule(rule, days, to) {
2241
+ const next = { ...rule };
2242
+ if (rule.weekdays?.length) next.weekdays = rule.weekdays.map((d) => ((d + days) % 7 + 7) % 7).sort((a, b) => a - b);
2243
+ if (rule.freq === "monthly" || rule.freq === "yearly") {
2244
+ if (rule.nth) next.nth = { week: weekOnMoved(rule.nth.week, to), weekday: to.getDay() };
2245
+ else if (rule.day === -1) {
2246
+ if (to.getDate() !== daysIn(to.getFullYear(), to.getMonth())) next.day = to.getDate();
2247
+ } else if (rule.day !== void 0) next.day = to.getDate();
2248
+ if (rule.freq === "yearly" && rule.month !== void 0) next.month = to.getMonth() + 1;
2249
+ }
2250
+ return next;
2251
+ }
2252
+ function onMovedRule(rule, start, days) {
2253
+ const endless = { ...rule, until: void 0, count: void 0 };
2254
+ return (key) => {
2255
+ const shifted = shiftKey(key, days);
2256
+ const [y, m, d] = shifted.split("-").map(Number);
2257
+ const target = dayIndex(new Date(y, m - 1, d));
2258
+ let found = null;
2259
+ for (const at of starts(endless, start)) {
2260
+ const i = dayIndex(at);
2261
+ if (i > target + 7) break;
2262
+ if (i >= target - 7 && (!found || Math.abs(i - target) < Math.abs(dayIndex(found) - target))) found = at;
2263
+ }
2264
+ return found ? dayKey(found) : shifted;
2265
+ };
2266
+ }
2267
+ var sameClock = (a, b) => a.getHours() === b.getHours() && a.getMinutes() === b.getMinutes() && a.getSeconds() === b.getSeconds() && a.getMilliseconds() === b.getMilliseconds();
2268
+ var spanOf = (event) => event.end ? dayIndex(event.end) - dayIndex(event.start) : null;
2269
+ function changeSeries(series, occurrence, changes) {
2270
+ const at = typeof occurrence === "string" ? occurrenceOnDay(series, occurrence) : occurrence;
2271
+ const { start: newStart, end: newEnd, ...rest } = editOf(changes);
2272
+ const next = { ...series, ...rest };
2273
+ const moveBy = (from, to, base) => atDay(base.getFullYear(), base.getMonth(), base.getDate() + dayIndex(to) - dayIndex(from), sameClock(to, from) ? base : to);
2274
+ let days = 0;
2275
+ if (newStart) {
2276
+ days = dayIndex(newStart) - dayIndex(at.start);
2277
+ next.start = moveBy(at.start, newStart, series.start);
2278
+ }
2279
+ if (newEnd) {
2280
+ const from = at.end ?? at.start;
2281
+ next.end = moveBy(from, newEnd, series.end ?? series.start);
2282
+ }
2283
+ const rule = readRecurrence(series.repeat);
2284
+ let moveKey = (key) => shiftKey(key, days);
2285
+ const moveExceptions = () => {
2286
+ if (days !== 0 && series.exceptions?.length) next.exceptions = series.exceptions.map(moveKey);
2287
+ };
2288
+ if (repeatChanged(series, changes)) {
2289
+ if (changes.repeat) {
2290
+ next.repeat = changes.repeat;
2291
+ moveExceptions();
2292
+ } else {
2293
+ delete next.repeat;
2294
+ delete next.exceptions;
2295
+ }
2296
+ } else if (days !== 0 && rule && newStart) {
2297
+ const moved = moveRule(rule, days, newStart);
2298
+ next.repeat = asWritten(series, moved);
2299
+ if (moved.nth) {
2300
+ const from = next.start;
2301
+ const anchor = (k) => {
2302
+ const at2 = new Date(from.getFullYear() + (moved.freq === "yearly" ? k : 0), (moved.freq === "yearly" && moved.month !== void 0 ? moved.month - 1 : from.getMonth()) + (moved.freq === "monthly" ? k : 0), 1);
2303
+ const d = nthWeekdayOf(at2.getFullYear(), at2.getMonth(), moved.nth.week, moved.nth.weekday) ?? nthWeekdayOf(at2.getFullYear(), at2.getMonth(), -1, moved.nth.weekday);
2304
+ return atDay(at2.getFullYear(), at2.getMonth(), d, from);
2305
+ };
2306
+ let first = anchor(0);
2307
+ if (dayIndex(first) < dayIndex(from)) first = anchor(1);
2308
+ const extra = dayIndex(first) - dayIndex(from);
2309
+ if (extra) {
2310
+ next.start = first;
2311
+ if (next.end) next.end = atDay(next.end.getFullYear(), next.end.getMonth(), next.end.getDate() + extra, next.end);
2312
+ }
2313
+ }
2314
+ moveKey = onMovedRule(moved, next.start, days);
2315
+ moveExceptions();
2316
+ }
2317
+ return { next, moveKey };
2318
+ }
2319
+ function splitWith(series, date, id, at, changes) {
2320
+ const rule = readRecurrence(series.repeat);
2321
+ const firstDate = dayKey(series.start);
2322
+ if (!rule || date <= firstDate) {
2323
+ if (!changes) return { after: series, moveKey: (key) => key };
2324
+ const { next: next2, moveKey: moveKey2 } = changeSeries(series, typeof at === "string" ? at <= firstDate ? firstDate : at : at, changes);
2325
+ return { after: next2, moveKey: moveKey2 };
2326
+ }
2327
+ const [y, m, d] = date.split("-").map(Number);
2328
+ const first = occurrenceOnDay(series, date);
2329
+ const exceptions = series.exceptions ?? [];
2330
+ const counted = rule.count !== void 0 ? startsBefore(rule, series.start, date) : 0;
2331
+ const beforeRule = { ...rule };
2332
+ if (rule.count !== void 0) beforeRule.count = counted;
2333
+ else {
2334
+ delete beforeRule.until;
2335
+ beforeRule.until = series.allDay ? new Date(y, m - 1, d - 1) : new Date(y, m - 1, d - 1, 23, 59, 59);
2336
+ }
2337
+ const afterRule = { ...rule };
2338
+ if (rule.count !== void 0) afterRule.count = rule.count - counted;
2339
+ const before = { ...series, repeat: asWritten(series, beforeRule), exceptions: exceptions.filter((x) => x < date) };
2340
+ const after = { ...series, id, start: first.start, ...first.end ? { end: first.end } : {}, repeat: asWritten(series, afterRule), exceptions: exceptions.filter((x) => x >= date) };
2341
+ if (!changes) return { before, after, moveKey: (key) => key };
2342
+ const edit = repeatChanged(series, changes) ? changes : editOf(changes);
2343
+ const { next, moveKey } = changeSeries(after, typeof at === "string" ? date : at, edit);
2344
+ return { before, after: next, moveKey };
2345
+ }
2346
+ function detachOccurrence(series, date, id = `${series.id}-${date}`, changes) {
2347
+ return {
2348
+ series: { ...series, exceptions: [.../* @__PURE__ */ new Set([...series.exceptions ?? [], date])].sort() },
2349
+ event: { ...occurrenceOnDay(series, date), ...editOf(changes), id }
2350
+ };
2351
+ }
2352
+ var NOT_FOLLOWED = /* @__PURE__ */ new Set(["id", "series", "start", "end", "repeat", "exceptions"]);
2353
+ function follow(edited, was, now) {
2354
+ const out = { ...edited };
2355
+ const before = was, after = now;
2356
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) {
2357
+ if (NOT_FOLLOWED.has(key) || !sameValue(out[key], before[key])) continue;
2358
+ if (after[key] === void 0) delete out[key];
2359
+ else out[key] = after[key];
2360
+ }
2361
+ const ownDay = dayIndex(edited.start) !== dayIndex(was.start);
2362
+ const ownTimes = !sameClock(edited.start, was.start) || spanOf(edited) !== spanOf(was) || !!edited.end && !!was.end && !sameClock(edited.end, was.end);
2363
+ const day = ownDay ? edited.start : now.start;
2364
+ const times = ownTimes ? edited : now;
2365
+ const start = atDay(day.getFullYear(), day.getMonth(), day.getDate(), times.start);
2366
+ out.start = start;
2367
+ const span = spanOf(times);
2368
+ if (times.end && span !== null) out.end = atDay(start.getFullYear(), start.getMonth(), start.getDate() + span, times.end);
2369
+ else delete out.end;
2370
+ return out;
2371
+ }
2372
+ function carry(old, next, linked, moveKey, changed, opened, edit) {
2373
+ if (!linked.length) return { next, save: [], remove: [] };
2374
+ if (changed) {
2375
+ const freed = new Set(linked.map((e) => moveKey(e.series.date)));
2376
+ return { next: next.exceptions ? { ...next, exceptions: next.exceptions.filter((x) => !freed.has(x)) } : next, save: [], remove: linked.map((e) => e.id) };
2377
+ }
2378
+ const save = linked.map((e) => {
2379
+ const date = moveKey(e.series.date);
2380
+ const followed = follow(e, occurrenceOnDay(old, e.series.date), occurrenceOnDay(next, date));
2381
+ return { ...followed, ...e.id === opened?.id ? editOf(edit) : {}, series: { id: next.id, date } };
2382
+ });
2383
+ return { next, save, remove: [] };
2384
+ }
2385
+ function findSeries(events, occurrence) {
2386
+ const stored = events.find((e) => e.id === occurrence.id);
2387
+ const link = (stored ?? occurrence).series;
2388
+ const series = link ? events.find((e) => e.id === link.id && readRecurrence(e.repeat) !== null) : void 0;
2389
+ return { series, stored, date: link?.date };
2390
+ }
2391
+ function commit(events, save, remove) {
2392
+ const gone = new Set(remove);
2393
+ const saved = new Map(save.map((e) => [e.id, e]));
2394
+ const had = new Set(events.map((e) => e.id));
2395
+ return { events: [...events.filter((e) => !gone.has(e.id)).map((e) => saved.get(e.id) ?? e), ...save.filter((e) => !had.has(e.id))], save, remove };
2396
+ }
2397
+ function saveOccurrence(events, occurrence, scope, changes, newId) {
2398
+ const { series, stored, date } = findSeries(events, occurrence);
2399
+ if (!series || date === void 0) return commit(events, stored ? [{ ...stored, ...editOf(changes) }] : [], []);
2400
+ const opened = stored ?? occurrenceOnDay(series, date);
2401
+ const edit = editFrom(opened, series, changes);
2402
+ if (scope === "this") {
2403
+ if (stored) return commit(events, [{ ...stored, ...editOf(edit) }], []);
2404
+ const { series: kept, event } = detachOccurrence(series, date, newId, edit);
2405
+ return commit(events, [kept, event], []);
2406
+ }
2407
+ const linked = editedOf(events, series.id);
2408
+ const changed = repeatChanged(series, edit);
2409
+ if (scope === "following" && date > dayKey(series.start)) {
2410
+ const { before, after, moveKey: moveKey2 } = splitWith(series, date, newId, opened, edit);
2411
+ const carried2 = carry(series, after, linked.filter((e) => e.series.date >= date), moveKey2, changed, stored, edit);
2412
+ return commit(events, [...before ? [before] : [], carried2.next, ...carried2.save], carried2.remove);
2413
+ }
2414
+ const { next, moveKey } = changeSeries(series, opened, edit);
2415
+ const carried = carry(series, next, linked, moveKey, changed, stored, edit);
2416
+ return commit(events, [carried.next, ...carried.save], carried.remove);
2417
+ }
2418
+ function deleteOccurrence(events, occurrence, scope) {
2419
+ const { series, stored, date } = findSeries(events, occurrence);
2420
+ if (!series || date === void 0) return commit(events, [], stored ? [stored.id] : []);
2421
+ if (scope === "this") return stored ? commit(events, [], [stored.id]) : commit(events, [detachOccurrence(series, date).series], []);
2422
+ const linked = editedOf(events, series.id);
2423
+ if (scope === "following" && date > dayKey(series.start)) {
2424
+ const { before } = splitWith(series, date, series.id, date);
2425
+ return commit(events, before ? [before] : [], linked.filter((e) => e.series.date >= date).map((e) => e.id));
2426
+ }
2427
+ return commit(events, [], [series.id, ...linked.map((e) => e.id)]);
2428
+ }
2429
+ function countEdited(events, occurrence) {
2430
+ const { series, date } = findSeries(events, occurrence);
2431
+ if (!series || date === void 0) return { following: 0, all: 0 };
2432
+ const linked = editedOf(events, series.id);
2433
+ return { following: date > dayKey(series.start) ? linked.filter((e) => e.series.date >= date).length : linked.length, all: linked.length };
2434
+ }
2435
+ var formats = localeCache(128);
2436
+ function format(options, date, locale) {
2437
+ return formats(`${locale ?? ""}|${JSON.stringify(options)}`, () => new Intl.DateTimeFormat(locale, options)).format(date);
2438
+ }
2439
+ var weekdayName = (d, locale) => format({ weekday: "long" }, new Date(2026, 0, 4 + d), locale);
2440
+ var monthName = (m, locale) => format({ month: "long" }, new Date(2026, m - 1, 1), locale);
2441
+ var ORDINAL = { 1: "first", 2: "second", 3: "third", 4: "fourth", [-1]: "last" };
2442
+ var list = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
2443
+ var every = (n, one, many) => n > 1 ? `Every ${n} ${many}` : `Every ${one}`;
2444
+ function describeRecurrence(rule, start, locale = formatLocale()) {
2445
+ const n = Math.max(1, Math.floor(rule.interval ?? 1));
2446
+ const weekStart = rule.weekStart ?? 1;
2447
+ let text;
2448
+ if (rule.freq === "daily") text = every(n, "day", "days");
2449
+ else if (rule.freq === "weekly") {
2450
+ const days = inWeekOrder(rule.weekdays?.length ? rule.weekdays : [start.getDay()], weekStart);
2451
+ if (days.length === 7) text = every(n, "day", "weeks on every day");
2452
+ else if (n === 1 && days.join() === "1,2,3,4,5") text = "Every weekday (Monday to Friday)";
2453
+ else text = `${every(n, "week", "weeks")} on ${list(days.map((d) => weekdayName(d, locale)))}`;
2454
+ } else {
2455
+ const nth = rule.nth ? `the ${ORDINAL[rule.nth.week]} ${weekdayName(rule.nth.weekday, locale)}` : void 0;
2456
+ if (rule.freq === "monthly") {
2457
+ const on = nth ?? (rule.day === -1 ? "the last day" : `day ${rule.day ?? start.getDate()}`);
2458
+ text = `${every(n, "month", "months")} on ${on}`;
2459
+ } else {
2460
+ const month = rule.month ?? start.getMonth() + 1;
2461
+ const on = nth ? `${nth} of ${monthName(month, locale)}` : rule.day === -1 ? `the last day of ${monthName(month, locale)}` : (
2462
+ // a leap year, so 29 February reads as itself
2463
+ format({ day: "numeric", month: "long" }, new Date(2028, month - 1, rule.day ?? start.getDate()), locale)
2464
+ );
2465
+ text = `${every(n, "year", "years")} on ${on}`;
2466
+ }
2467
+ }
2468
+ if (rule.until) text += `, until ${format({ day: "numeric", month: "long", year: "numeric" }, rule.until, locale)}`;
2469
+ else if (rule.count !== void 0) text += rule.count === 1 ? ", once" : `, ${rule.count} times`;
2470
+ return text;
2471
+ }
2472
+ function recurrencePresets(start, locale = formatLocale()) {
2473
+ const rules = [{ freq: "daily" }];
2474
+ const weekday2 = start.getDay();
2475
+ if (weekday2 >= 1 && weekday2 <= 5) rules.push({ freq: "weekly", weekdays: [1, 2, 3, 4, 5] });
2476
+ rules.push({ freq: "weekly" }, ...nthChoices(start).map((nth) => ({ freq: "monthly", nth })), { freq: "monthly" });
2477
+ if (start.getDate() === daysIn(start.getFullYear(), start.getMonth())) rules.push({ freq: "monthly", day: -1 });
2478
+ rules.push({ freq: "yearly" });
2479
+ return rules.map((rule) => ({ rule, label: describeRecurrence(rule, start, locale) }));
2480
+ }
2481
+ function sameRecurrence(a, b) {
2482
+ if (!a || !b) return a === b;
2483
+ const norm = (r) => toRRule({ ...r, weekdays: r.weekdays?.length ? [...r.weekdays].sort() : void 0 });
2484
+ return norm(a) === norm(b);
2485
+ }
2486
+ var DAYS_MESSAGE = "Choose at least one day";
2487
+ var UNTIL_MESSAGE = "The end date must be after the event starts";
2488
+ var UNTIL_MISSING = "Enter the end date";
2489
+ var UNITS = { daily: ["day", "days"], weekly: ["week", "weeks"], monthly: ["month", "months"], yearly: ["year", "years"] };
2490
+ var ORDINAL2 = { 1: "first", 2: "second", 3: "third", 4: "fourth", [-1]: "last" };
2491
+ var weekdayName2 = (d, width, locale) => new Intl.DateTimeFormat(locale, { weekday: width }).format(new Date(2026, 0, 4 + d));
2492
+ var daysIn2 = (d) => new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
2493
+ var clean = (rule) => Object.fromEntries(Object.entries(rule).filter(([, v]) => v !== void 0));
2494
+ var kindOf = (r) => [r.freq, r.weekdays?.length ? "days" : "", r.nth ? r.nth.week === -1 ? "nth-last" : "nth" : "", r.day === -1 ? "last" : ""].join(":");
2495
+ var endOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59);
2496
+ var noDays = (r) => r?.freq === "weekly" && Array.isArray(r.weekdays) && r.weekdays.length === 0;
2497
+ var PENDING_OPTIONS = [{ id: "pending", label: "" }];
2498
+ function RecurrencePicker({ value, onChange: onChangeProp, start, label, weekStartsOn, className, ...wiring }) {
2499
+ const onChange = useReportedChange(onChangeProp, value);
2500
+ const field = useField();
2501
+ const weekStart = useWeekStart(weekStartsOn);
2502
+ const locale = useFormatLocale();
2503
+ const hydrated = useHydrated();
2504
+ const base = useId();
2505
+ const ids = { summary: `${base}-summary`, interval: `${base}-interval`, days: `${base}-days`, daysError: `${base}-days-error`, untilError: `${base}-until-error`, count: `${base}-count` };
2506
+ const startKey = dayKey(start);
2507
+ const presets = useMemo(() => recurrencePresets(start, locale), [startKey, locale]);
2508
+ const rule = readRecurrence(value);
2509
+ const unreadable = value !== null && !rule;
2510
+ const presetIndex = rule && !noDays(rule) ? presets.findIndex((p) => sameRecurrence(p.rule, rule)) : -1;
2511
+ const [customOpen, setCustomOpen] = useState(() => !!rule && presetIndex < 0);
2512
+ const openRule = rule && (customOpen || presetIndex < 0) ? rule : null;
2513
+ const choice = value === null ? "none" : unreadable ? "unreadable" : openRule ? "custom" : `preset-${presetIndex}`;
2514
+ const [untilPending, setUntilPending] = useState(false);
2515
+ const lastStart = useRef(start);
2516
+ useEffect(() => {
2517
+ const before = lastStart.current;
2518
+ lastStart.current = start;
2519
+ if (dayKey(before) === startKey || !rule || customOpen) return;
2520
+ const was = recurrencePresets(before, locale).find((p) => sameRecurrence(p.rule, rule));
2521
+ const now2 = was && (presets.find((p) => kindOf(p.rule) === kindOf(was.rule)) ?? (was.rule.nth ? presets.find((p) => !!p.rule.nth) : void 0));
2522
+ if (now2 && !sameRecurrence(now2.rule, rule)) onChangeProp(now2.rule);
2523
+ }, [startKey]);
2524
+ const explicit = (r) => {
2525
+ const week = weekStart !== 1 ? { weekStart } : {};
2526
+ if (!r || r.freq === "weekly" && r.weekdays === void 0) return clean({ ...r ?? {}, freq: "weekly", weekdays: [start.getDay()], ...week });
2527
+ if (r.freq === "monthly" && r.day === void 0 && !r.nth) return { ...r, day: start.getDate() };
2528
+ if (r.freq === "yearly" && r.month === void 0) return { ...r, month: start.getMonth() + 1, day: start.getDate() };
2529
+ return r;
2530
+ };
2531
+ const edit = (patch) => onChange(clean({ ...explicit(rule), ...patch }));
2532
+ const custom = openRule ? explicit(openRule) : null;
2533
+ const unit = (freq) => {
2534
+ const keep = { interval: rule?.interval, until: rule?.until, count: rule?.count };
2535
+ const week = weekStart !== 1 ? { weekStart } : {};
2536
+ const by = {
2537
+ daily: { freq },
2538
+ weekly: { freq, weekdays: [start.getDay()], ...week },
2539
+ monthly: { freq, day: start.getDate() },
2540
+ yearly: { freq, month: start.getMonth() + 1, day: start.getDate() }
2541
+ };
2542
+ onChange(clean({ ...by[freq], ...keep }));
2543
+ };
2544
+ const form = useOptionalFormContext();
2545
+ const check = (r, s, open, pending) => ({
2546
+ // every day taken off: an implied rule (no days written) repeats on the start's day, so it has one
2547
+ days: open && noDays(r) ? DAYS_MESSAGE : void 0,
2548
+ until: open && pending && !r?.until ? UNTIL_MISSING : r?.until && dayKey(r.until) <= s ? UNTIL_MESSAGE : void 0
2549
+ });
2550
+ const latest = useLatest({ rule, startKey, custom: !!custom, untilPending });
2551
+ const problems = () => check(latest.current.rule, latest.current.startKey, latest.current.custom, latest.current.untilPending);
2552
+ const register = form?.register;
2553
+ useEffect(() => register?.(ids.days, () => problems().days), [register, ids.days]);
2554
+ useEffect(() => register?.(ids.untilError, () => problems().until), [register, ids.untilError]);
2555
+ const now = check(rule, startKey, !!custom, untilPending);
2556
+ const setError = form?.setError;
2557
+ const shownDays = form ? form.errors[ids.days] : now.days;
2558
+ const shownUntil = form ? form.errors[ids.untilError] : now.until;
2559
+ useEffect(() => {
2560
+ if (shownDays !== void 0 && shownDays !== now.days) setError?.(ids.days, now.days);
2561
+ if (shownUntil !== void 0 && shownUntil !== now.until) setError?.(ids.untilError, now.until);
2562
+ });
2563
+ const options = [
2564
+ { id: "none", label: "Does not repeat" },
2565
+ ...presets.map((p, i) => ({ id: `preset-${i}`, label: p.label })),
2566
+ ...unreadable ? [{ id: "unreadable", label: "Custom repeat" }] : [],
2567
+ { id: "custom", label: "Custom\u2026" }
2568
+ ];
2569
+ const choose = (id) => {
2570
+ if (id === "unreadable") return;
2571
+ setUntilPending(false);
2572
+ if (id === "none") {
2573
+ setCustomOpen(false);
2574
+ onChange(null);
2575
+ return;
2576
+ }
2577
+ if (id === "custom") {
2578
+ setCustomOpen(true);
2579
+ onChange(explicit(rule));
2580
+ return;
2581
+ }
2582
+ setCustomOpen(false);
2583
+ onChange(presets[Number(id.slice("preset-".length))].rule);
2584
+ };
2585
+ const summary = rule && !noDays(rule) ? describeRecurrence(rule, start, locale) : unreadable ? "Repeats by a rule set in another app. Choose another to replace it." : null;
2586
+ const describedBy = [wiring["aria-describedby"], summary ? ids.summary : void 0].filter(Boolean).join(" ") || void 0;
2587
+ const order = Array.from({ length: 7 }, (_, i) => (weekStart + i) % 7);
2588
+ const interval = custom?.interval ?? 1;
2589
+ const monthDay = custom?.day !== void 0 && custom.day > 0 ? custom.day : start.getDate();
2590
+ const nths = nthChoices(start);
2591
+ if (custom?.nth && !nths.some((n) => n.week === custom.nth.week && n.weekday === custom.nth.weekday)) nths.unshift(custom.nth);
2592
+ const nthId = (n) => `nth:${n.week}:${n.weekday}`;
2593
+ const monthlyOn = custom?.nth ? nthId(custom.nth) : custom?.day === -1 ? "last" : "day";
2594
+ const ends = custom?.until ? "until" : custom?.count !== void 0 ? "count" : untilPending ? "until" : "never";
2595
+ if (!hydrated) {
2596
+ return /* @__PURE__ */ jsxs("div", { "data-gs": "recurrence-picker", "data-recurrence-pending": "", "aria-busy": "true", className: cn("space-y-ds-2", className), children: [
2597
+ /* @__PURE__ */ jsx(Dropdown, { ...wiring, label: field ? void 0 : label ?? "Repeat", options: PENDING_OPTIONS, value: "pending", onChange: () => {
2598
+ } }),
2599
+ (rule && !noDays(rule) || unreadable) && /* @__PURE__ */ jsx("p", { "aria-hidden": "true", className: "text-xs", children: "\xA0" })
2600
+ ] });
2601
+ }
2602
+ return /* @__PURE__ */ jsxs("div", { "data-gs": "recurrence-picker", className: cn("space-y-ds-2", className), children: [
2603
+ /* @__PURE__ */ jsx(
2604
+ Dropdown,
2605
+ {
2606
+ ...wiring,
2607
+ "aria-describedby": describedBy,
2608
+ label: field ? void 0 : label ?? "Repeat",
2609
+ options,
2610
+ value: choice,
2611
+ onChange: (v) => choose(v)
2612
+ }
2613
+ ),
2614
+ custom && /* @__PURE__ */ jsx(OutsideField, { children: /* @__PURE__ */ jsxs("div", { "data-gs": "recurrence-custom", className: "space-y-ds-4 pl-ds-4 border-l-[length:var(--ds-border-width)] border-[var(--ds-border-default)]", children: [
2615
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-ds-2", children: [
2616
+ /* @__PURE__ */ jsx("label", { htmlFor: ids.interval, className: "text-sm text-[var(--ds-text-primary)]", children: "Every" }),
2617
+ /* @__PURE__ */ jsx(NumberInput, { id: ids.interval, size: "sm", value: interval, min: 1, max: 99, onChange: (n) => edit({ interval: n > 1 ? n : void 0 }), className: "w-28" }),
2618
+ /* @__PURE__ */ jsx(
2619
+ Dropdown,
2620
+ {
2621
+ "aria-label": "Days, weeks, months or years",
2622
+ size: "sm",
2623
+ className: "w-36",
2624
+ options: Object.keys(UNITS).map((f) => ({ id: f, label: UNITS[f][interval > 1 ? 1 : 0] })),
2625
+ value: custom.freq,
2626
+ onChange: (v) => unit(v)
2627
+ }
2628
+ )
2629
+ ] }),
2630
+ custom.freq === "weekly" && /* @__PURE__ */ jsxs("div", { className: "space-y-ds-2", children: [
2631
+ /* @__PURE__ */ jsx("p", { "aria-hidden": "true", className: "text-sm font-medium text-[var(--ds-text-primary)]", children: "On" }),
2632
+ /* @__PURE__ */ jsx("div", { role: "group", id: ids.days, "aria-label": "Repeat on", "aria-invalid": !!shownDays || void 0, "aria-describedby": shownDays ? ids.daysError : void 0, className: "flex flex-wrap gap-ds-1.5", children: order.map((d) => {
2633
+ const on = !!custom.weekdays?.includes(d);
2634
+ return /* @__PURE__ */ jsx(
2635
+ "button",
2636
+ {
2637
+ type: "button",
2638
+ role: "checkbox",
2639
+ "aria-checked": on,
2640
+ "aria-label": weekdayName2(d, "long", locale),
2641
+ onClick: () => edit({ weekdays: (on ? (custom.weekdays ?? []).filter((x) => x !== d) : [...custom.weekdays ?? [], d]).sort((a, b) => a - b) }),
2642
+ className: cn(
2643
+ "inline-flex h-8 min-w-8 items-center justify-center px-ds-2 rounded-[var(--ds-radius-full)] text-xs font-medium",
2644
+ "border-[length:var(--ds-border-width)] cursor-pointer select-none outline-none transition-colors",
2645
+ "focus-visible:shadow-[var(--ds-shadow-focus)]",
2646
+ // chosen: the accent's readable pair, so its text reads 4.5:1 in every theme (the house's one way
2647
+ // to colour text on a colour, as the Calendar's accent events), and the checked border at 3:1
2648
+ // marking the state; the tick colour on the checked fill read 3.2:1 in three themes
2649
+ on ? "bg-[var(--ds-accent-bg,var(--ds-accent-subtle))] text-[var(--ds-accent-text,var(--ds-text-primary))] border-[var(--ds-check-border)]" : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)] text-[var(--ds-text-primary)] hover:border-[var(--ds-input-border-hover)]"
2650
+ ),
2651
+ children: weekdayName2(d, "short", locale)
2652
+ },
2653
+ d
2654
+ );
2655
+ }) }),
2656
+ shownDays && /* @__PURE__ */ jsx(InlineFieldError, { id: ids.daysError, message: shownDays, className: "mt-0" })
2657
+ ] }),
2658
+ custom.freq === "monthly" && /* @__PURE__ */ jsx(
2659
+ RadioGroup,
2660
+ {
2661
+ label: "On",
2662
+ value: monthlyOn,
2663
+ onChange: (v) => {
2664
+ if (v === "last") edit({ day: -1, nth: void 0 });
2665
+ else if (v === "day") edit({ day: monthDay, nth: void 0 });
2666
+ else {
2667
+ const [, week, weekday2] = v.split(":").map(Number);
2668
+ edit({ nth: { week, weekday: weekday2 }, day: void 0 });
2669
+ }
2670
+ },
2671
+ options: [
2672
+ { value: "day", label: `On day ${monthDay}` },
2673
+ ...nths.map((n) => ({ value: nthId(n), label: `On the ${ORDINAL2[n.week]} ${weekdayName2(n.weekday, "long", locale)}` })),
2674
+ ...start.getDate() === daysIn2(start) || custom.day === -1 ? [{ value: "last", label: "On the last day" }] : []
2675
+ ]
2676
+ }
2677
+ ),
2678
+ /* @__PURE__ */ jsx(
2679
+ RadioGroup,
2680
+ {
2681
+ label: "Ends",
2682
+ value: ends,
2683
+ onChange: (v) => {
2684
+ setUntilPending(false);
2685
+ if (v === "never") edit({ until: void 0, count: void 0 });
2686
+ else if (v === "until") edit({ count: void 0, until: endOfDay(new Date(start.getFullYear(), start.getMonth() + 1, start.getDate())) });
2687
+ else edit({ until: void 0, count: 10 });
2688
+ },
2689
+ options: [
2690
+ { value: "never", label: "Never" },
2691
+ {
2692
+ value: "until",
2693
+ label: "On a date",
2694
+ reveal: /* @__PURE__ */ jsxs("div", { children: [
2695
+ /* @__PURE__ */ jsx(
2696
+ DatePicker,
2697
+ {
2698
+ label: "End date",
2699
+ size: "sm",
2700
+ value: custom.until ?? null,
2701
+ min: new Date(start.getFullYear(), start.getMonth(), start.getDate() + 1),
2702
+ onChange: (d) => {
2703
+ setUntilPending(!d);
2704
+ edit({ until: d ? endOfDay(d) : void 0 });
2705
+ },
2706
+ error: !!shownUntil,
2707
+ "aria-describedby": shownUntil ? ids.untilError : void 0
2708
+ }
2709
+ ),
2710
+ shownUntil && /* @__PURE__ */ jsx(InlineFieldError, { id: ids.untilError, message: shownUntil })
2711
+ ] })
2712
+ },
2713
+ {
2714
+ value: "count",
2715
+ label: "After a number of times",
2716
+ reveal: /* @__PURE__ */ jsxs("div", { className: "space-y-ds-1", children: [
2717
+ /* @__PURE__ */ jsx("label", { htmlFor: ids.count, className: "block text-sm font-medium text-[var(--ds-text-primary)]", children: "Number of times" }),
2718
+ /* @__PURE__ */ jsx(NumberInput, { id: ids.count, size: "sm", value: custom.count ?? 10, min: 1, max: 999, onChange: (n) => edit({ count: Math.max(1, n) }), className: "w-28" })
2719
+ ] })
2720
+ }
2721
+ ]
2722
+ }
2723
+ )
2724
+ ] }) }),
2725
+ summary && /* @__PURE__ */ jsx("p", { id: ids.summary, className: "text-xs text-[var(--ds-text-secondary)]", children: summary })
2726
+ ] });
2727
+ }
2728
+ var DEFAULT_BUTTON_ATTR = "data-dialog-default-button";
2729
+ var sizeClasses = {
2730
+ // round 16 (the owner's item 5): the small dialog is 448 px, not 384 — a delete confirmation with a typed field was cramped
2731
+ sm: "max-w-md",
2732
+ md: "max-w-lg",
2733
+ lg: "max-w-2xl",
2734
+ xl: "max-w-4xl",
2735
+ full: "max-w-[calc(100vw-2rem)]"
2736
+ };
2737
+ function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
2738
+ const { marked, mark } = useOverlayMark(open);
2739
+ const guard = useDirtyGuard(dirty ?? marked, onCloseProp, onDiscard);
2740
+ const onClose = guard.requestClose;
2741
+ const titleId = useId();
2742
+ const descriptionId = useId();
2743
+ const isMobile = useBreakpoint("sm");
2744
+ const overlayRef = useRef(null);
2745
+ const dialogRef = useRef(null);
2746
+ const opener = useRef(null);
2747
+ const defaultButtonRef = useRef(null);
2748
+ const bodyRef = useScrollbarInset();
2749
+ const onCloseRef = useLatest(onClose);
2750
+ const onSubmitRef = useLatest(onSubmit);
2751
+ const isTopLayer = useOverlayLayer(open);
2752
+ useFocusTrap(dialogRef, open, isTopLayer);
2753
+ const before = useFocusBeforeOpen(open);
2754
+ useEffect(() => {
2755
+ if (!open) return;
2756
+ const active = before.current;
2757
+ opener.current = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
2758
+ }, [open, before]);
2759
+ useEffect(() => {
2760
+ if (!open) return;
2761
+ requestAnimationFrame(() => {
2762
+ focusInitial(dialogRef.current, { destructive: variant === "destructive" });
2763
+ });
2764
+ }, [open, variant]);
2765
+ useEffect(() => {
2766
+ if (!open) return;
2767
+ const handler = (e) => {
2768
+ if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
2769
+ e.preventDefault();
2770
+ onCloseRef.current();
2771
+ };
2772
+ document.addEventListener("keydown", handler);
2773
+ return () => {
2774
+ document.removeEventListener("keydown", handler);
2775
+ };
2776
+ }, [open, onCloseRef, isTopLayer]);
2777
+ useEffect(() => {
2778
+ if (!open) return;
2779
+ lockBodyScroll();
2780
+ return () => {
2781
+ unlockBodyScroll();
2782
+ const r = opener.current;
2783
+ opener.current = null;
2784
+ const active = document.activeElement;
2785
+ if (!active || active === document.body || !active.isConnected) r?.restore();
2786
+ };
2787
+ }, [open]);
2788
+ if (!open) return null;
2789
+ return createPortal(
2790
+ /* @__PURE__ */ jsxs(
2791
+ "div",
2792
+ {
2793
+ ref: overlayRef,
2794
+ className: cn("fixed inset-0 z-[var(--ds-z-modal)] flex items-center justify-center", !isMobile && "p-ds-4"),
2795
+ children: [
2796
+ /* @__PURE__ */ jsx(
2797
+ "div",
2798
+ {
2799
+ className: "fixed inset-0 bg-black/50 [backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))] [-webkit-backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))]",
2800
+ onClick: alert ? void 0 : onClose,
2801
+ "aria-hidden": "true"
2802
+ }
2803
+ ),
2804
+ /* @__PURE__ */ jsxs(
2805
+ "form",
2806
+ {
2807
+ onSubmit: (e) => {
2808
+ if (e.target !== e.currentTarget) return;
2809
+ e.preventDefault();
2810
+ e.stopPropagation();
2811
+ if (variant === "destructive") return;
2812
+ const submitter = e.nativeEvent.submitter;
2813
+ const implicit = !submitter || submitter === defaultButtonRef.current;
2814
+ if (!implicit && submitter.getAttribute("type") !== "submit") return;
2815
+ if (implicit) {
2816
+ const explicit = e.currentTarget.querySelector(`button[type="submit"]:not([${DEFAULT_BUTTON_ATTR}])`);
2817
+ if (explicit?.disabled) return;
2818
+ }
2819
+ onSubmitRef.current?.();
2820
+ },
2821
+ className: "contents",
2822
+ children: [
2823
+ /* @__PURE__ */ jsx(
2824
+ "button",
2825
+ {
2826
+ ref: defaultButtonRef,
2827
+ type: "submit",
2828
+ tabIndex: -1,
2829
+ "aria-hidden": "true",
2830
+ className: "sr-only",
2831
+ ...{ [DEFAULT_BUTTON_ATTR]: "" }
2832
+ }
2833
+ ),
2834
+ /* @__PURE__ */ jsxs(
2835
+ "div",
2836
+ {
2837
+ ref: dialogRef,
2838
+ role: alert ? "alertdialog" : "dialog",
2839
+ ...{ [OVERLAY_ROOT_ATTR]: "" },
2840
+ "aria-modal": "true",
2841
+ "data-gs": "dialog",
2842
+ "aria-labelledby": titleId,
2843
+ "aria-describedby": description ? descriptionId : void 0,
2844
+ style: { containerType: "inline-size", "--ds-form-max": "none" },
2845
+ className: cn(
2846
+ // a column: the title row, a body that scrolls when it is taller than the room, and the footer (Sprint 25.14,
2847
+ // with A36's full-screen phone dialog: its content ran past the screen with the footer cut off)
2848
+ "relative z-10 w-full flex flex-col max-h-full",
2849
+ isMobile ? "max-w-none h-full" : sizeClasses[size],
2850
+ OVERLAY_SURFACE,
2851
+ "text-[var(--ds-text-primary)]",
2852
+ GLASS_OVERLAY,
2853
+ // full screen on a phone: no edge to draw, so the content sits at the gutter from the screen's edge
2854
+ isMobile ? "rounded-none" : "rounded-[var(--ds-radius-xl)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
2855
+ "overflow-hidden",
2856
+ "animate-in fade-in-0 zoom-in-95"
2857
+ ),
2858
+ children: [
2859
+ /* @__PURE__ */ jsxs("div", { className: cn("shrink-0 flex items-start gap-ds-3 pt-ds-6 px-[var(--ds-page-gutter)]", children ? "pb-ds-4" : "pb-ds-6"), children: [
2860
+ variant === "destructive" && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center shrink-0 w-10 h-10 rounded-[var(--ds-radius-full)] bg-[var(--ds-status-error-bg)]", children: /* @__PURE__ */ jsx(Icon, { name: "warning", size: 20, className: "text-[var(--ds-status-error)]" }) }),
2861
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2862
+ /* @__PURE__ */ jsx(
2863
+ "h2",
2864
+ {
2865
+ id: titleId,
2866
+ className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] leading-none tracking-tight",
2867
+ children: title
2868
+ }
2869
+ ),
2870
+ description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description })
2871
+ ] }),
2872
+ /* @__PURE__ */ jsx(
2873
+ "button",
2874
+ {
2875
+ type: "button",
2876
+ onClick: onClose,
2877
+ ...{ [OVERLAY_CLOSE_ATTR]: "" },
2878
+ className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
2879
+ "aria-label": "Close",
2880
+ children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
2881
+ }
2882
+ )
2883
+ ] }),
2884
+ children && /* @__PURE__ */ jsx(
2885
+ "div",
2886
+ {
2887
+ ref: bodyRef,
2888
+ className: "min-h-0 flex-[0_1_auto] overflow-y-auto pb-ds-6",
2889
+ style: {
2890
+ paddingLeft: variant === "destructive" ? "calc(var(--ds-page-gutter) + 2.5rem + var(--ds-space-3))" : "var(--ds-page-gutter)",
2891
+ paddingRight: insetLessScrollbar("var(--ds-page-gutter)")
2892
+ },
2893
+ children: /* @__PURE__ */ jsx(SurfaceBoundary, { children: /* @__PURE__ */ jsx(DrawerFooterCtx, { value: null, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) }) }) })
2894
+ }
2895
+ ),
2896
+ isMobile && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "flex-1 min-h-0" }),
2897
+ /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
2898
+ footer && /* @__PURE__ */ jsx("div", { className: cn(
2899
+ "shrink-0 flex justify-end gap-ds-2 py-ds-4 px-[var(--ds-page-gutter)]",
2900
+ "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
2901
+ "bg-[var(--ds-bg-muted)]",
2902
+ !isMobile && "rounded-b-[var(--ds-radius-xl)]"
2903
+ ), children: footer })
2904
+ ]
2905
+ }
2906
+ )
2907
+ ]
2908
+ }
2909
+ )
1827
2910
  ]
1828
2911
  }
1829
2912
  ),
1830
- open && createPortal(
1831
- /* @__PURE__ */ jsxs(
1832
- "div",
1833
- {
1834
- ref: panelRef,
1835
- role: "dialog",
1836
- "aria-label": "Choose a date range",
1837
- tabIndex: -1,
1838
- className: cn(
1839
- "fixed z-[var(--ds-z-popover)] flex outline-none",
1840
- "bg-[var(--ds-bg-surface-elevated)]",
1841
- "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1842
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
1843
- "rounded-[var(--ds-radius-md)]",
1844
- "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]"
1845
- ),
1846
- style: { top: pos.top, left: pos.left },
1847
- children: [
1848
- presets.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-ds-0.5 p-ds-2 border-r-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] min-w-[140px]", children: presets.map((p) => /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "justify-start", onClick: () => {
1849
- const r = p.range();
1850
- returnFocus();
1851
- onChange(r);
1852
- setViewDate(r.start ?? /* @__PURE__ */ new Date());
1853
- setOpen(false);
1854
- }, children: p.label }, p.label)) }),
1855
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
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: [
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" }) }),
1858
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthRange(months[0], months[1]) }),
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 }) })
1860
- ] }),
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)) }),
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) => {
1864
- if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
1865
- const isStart = !!rangeStart && isSameDay(d, rangeStart);
1866
- const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
1867
- const between = inRange(d);
1868
- const dis = isDisabledDay(d);
1869
- return /* @__PURE__ */ jsx(
1870
- "button",
1871
- {
1872
- type: "button",
1873
- role: "gridcell",
1874
- "data-grid-key": dayKey(d),
1875
- tabIndex: grid.tabIndexOf(d),
1876
- "aria-disabled": dis || void 0,
1877
- onClick: () => selectDay(d),
1878
- onFocus: () => {
1879
- if (!isSameDay(d, focusDate)) setFocusDate(d);
1880
- },
1881
- onMouseEnter: () => setHover(d),
1882
- "aria-selected": isStart || isEnd || between,
1883
- "aria-current": isSameDay(d, today) ? "date" : void 0,
1884
- "aria-label": formatDayLong(d),
1885
- className: cn(
1886
- "w-full aspect-square flex items-center justify-center text-xs",
1887
- "transition-colors cursor-pointer",
1888
- "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
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)]",
1890
- dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1891
- ),
1892
- children: d.getDate()
1893
- },
1894
- d.toISOString()
1895
- );
1896
- }) }, w))
1897
- ] }, `${m.getFullYear()}-${m.getMonth()}`)) }),
1898
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center bg-[var(--ds-bg-muted)] border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-br-[var(--ds-radius-md)] px-ds-3 py-ds-2", children: [
1899
- /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--ds-text-muted)]", children: value.start && !value.end ? "Now pick an end date" : summary ?? "Pick a start date" }),
1900
- (value.start || value.end) && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
1901
- onChange({ start: null, end: null });
1902
- setHover(null);
1903
- }, children: "Clear" })
1904
- ] })
1905
- ] })
1906
- ]
1907
- }
1908
- ),
1909
- container ?? document.body
1910
- )
1911
- ] });
2913
+ container ?? document.body
2914
+ );
2915
+ }
2916
+ var SCOPES = [
2917
+ { value: "this", label: "This event" },
2918
+ { value: "following", label: "This and following events" },
2919
+ { value: "all", label: "All events" }
2920
+ ];
2921
+ function RecurrenceScopeDialog({ open, title, verb, destructive = false, repeatChanged: repeatChanged2 = false, edited, onChoose, onCancel }) {
2922
+ const first = repeatChanged2 ? "following" : "this";
2923
+ const [scope, setScope] = useState(first);
2924
+ const [wasOpen, setWasOpen] = useState(open);
2925
+ if (open !== wasOpen) {
2926
+ setWasOpen(open);
2927
+ if (open) setScope(first);
2928
+ }
2929
+ const chosen = repeatChanged2 && scope === "this" ? first : scope;
2930
+ const noteId = useId();
2931
+ const undone = repeatChanged2 && chosen !== "this" ? edited?.[chosen] ?? 0 : 0;
2932
+ return /* @__PURE__ */ jsxs(
2933
+ Dialog,
2934
+ {
2935
+ open,
2936
+ onClose: onCancel,
2937
+ title,
2938
+ size: "sm",
2939
+ variant: destructive ? "destructive" : "default",
2940
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
2941
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", onClick: onCancel, children: "Cancel" }),
2942
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: destructive ? "destructive" : "default", onClick: () => onChoose(chosen), children: verb })
2943
+ ] }),
2944
+ children: [
2945
+ /* @__PURE__ */ jsx(RadioGroup, { label: "Apply to", value: chosen, onChange: (v) => setScope(v), options: repeatChanged2 ? SCOPES.slice(1) : SCOPES, "aria-describedby": undone > 0 ? noteId : void 0 }),
2946
+ undone > 0 && /* @__PURE__ */ jsx("p", { id: noteId, className: "mt-ds-3 text-sm text-[var(--ds-text-secondary)]", children: undone === 1 ? "1 of these events was changed on its own. Changing how it repeats undoes its changes." : `${undone} of these events were changed on their own. Changing how it repeats undoes their changes.` })
2947
+ ]
2948
+ }
2949
+ );
1912
2950
  }
1913
2951
 
1914
2952
  // ../../showcase/src/lib/file-accept.ts
@@ -1952,12 +2990,12 @@ function fileFormat(file) {
1952
2990
  function matches(file, tokens) {
1953
2991
  if (tokens.length === 0 || tokens.some((t) => t.kind === "any")) return true;
1954
2992
  const name = file.name.toLowerCase();
1955
- const format2 = fileFormat(file);
2993
+ const format3 = fileFormat(file);
1956
2994
  const type = file.type.toLowerCase();
1957
2995
  return tokens.some((t) => {
1958
2996
  if (t.kind === "family") return type === "" || type.startsWith(`${t.family}/`);
1959
2997
  if (t.kind !== "format") return false;
1960
- return t.name === format2 || t.suffix != null && name.endsWith(t.suffix) || t.mime != null && t.mime === type;
2998
+ return t.name === format3 || t.suffix != null && name.endsWith(t.suffix) || t.mime != null && t.mime === type;
1961
2999
  });
1962
3000
  }
1963
3001
  var joinOr = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} or ${items[items.length - 1]}`;
@@ -1969,9 +3007,9 @@ function typeWords(accept) {
1969
3007
  const tokens = parseAccept(accept);
1970
3008
  if (tokens.length === 0 || tokens.some((t) => t.kind === "any" || t.kind === "family" && !FAMILIES[t.family])) return null;
1971
3009
  const families = [...new Set(tokens.flatMap((t) => t.kind === "family" ? [t.family] : []))].map((f) => FAMILIES[f]);
1972
- const formats2 = [...new Set(tokens.flatMap((t) => t.kind === "format" ? [t.name] : []))];
1973
- const many = `${joinOr([...families.map((f) => f.many), ...formats2])}${formats2.length ? " files" : ""}`;
1974
- const one = families.length === 0 ? `${article(formats2[0])} ${joinOr(formats2)}` : joinOr([...families.map((f) => f.one), ...formats2.map((f) => `${article(f)} ${f}`)]);
3010
+ const formats3 = [...new Set(tokens.flatMap((t) => t.kind === "format" ? [t.name] : []))];
3011
+ const many = `${joinOr([...families.map((f) => f.many), ...formats3])}${formats3.length ? " files" : ""}`;
3012
+ const one = families.length === 0 ? `${article(formats3[0])} ${joinOr(formats3)}` : joinOr([...families.map((f) => f.one), ...formats3.map((f) => `${article(f)} ${f}`)]);
1975
3013
  return { one, many };
1976
3014
  }
1977
3015
  function sizeWords(bytes) {
@@ -2198,10 +3236,10 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
2198
3236
  focusAfterRemoval(rows, gone.index, inputRef.current);
2199
3237
  return;
2200
3238
  }
2201
- const last = lastAction.current;
3239
+ const last2 = lastAction.current;
2202
3240
  const active = document.activeElement;
2203
- if (!last || last.el.isConnected || active && active !== document.body) return;
2204
- const row = Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).find((li) => li.dataset.uploadId === last.id);
3241
+ if (!last2 || last2.el.isConnected || active && active !== document.body) return;
3242
+ const row = Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).find((li) => li.dataset.uploadId === last2.id);
2205
3243
  row?.querySelector("button")?.focus();
2206
3244
  }, [shown]);
2207
3245
  const addFiles = useCallback(
@@ -2526,7 +3564,7 @@ var TEXT_SIZE4 = {
2526
3564
  md: "text-sm",
2527
3565
  lg: "text-base"
2528
3566
  };
2529
- function pad(n) {
3567
+ function pad3(n) {
2530
3568
  return n.toString().padStart(2, "0");
2531
3569
  }
2532
3570
  function generateHours(use24) {
@@ -2602,7 +3640,7 @@ function TimePicker({
2602
3640
  setPrevOpen(open);
2603
3641
  if (open) setColumnStop({});
2604
3642
  }
2605
- const stopOf = (column, chosen) => columnStop[column] ?? Math.max(0, chosen);
3643
+ const stopOf2 = (column, chosen) => columnStop[column] ?? Math.max(0, chosen);
2606
3644
  const holdStop = (column, index) => setColumnStop((s) => s[column] === index ? s : { ...s, [column]: index });
2607
3645
  const chosenHour = hours.findIndex((h) => use24Hour ? h === parseInt(value) : h === parsed.hour);
2608
3646
  const chosenMinute = minutes.findIndex((m) => m === parsed.minute);
@@ -2641,7 +3679,7 @@ function TimePicker({
2641
3679
  if (period === "AM" && hour === 12) h24 = 0;
2642
3680
  else if (period === "PM" && hour !== 12) h24 = hour + 12;
2643
3681
  }
2644
- onChange(`${pad(h24)}:${pad(minute)}`);
3682
+ onChange(`${pad3(h24)}:${pad3(minute)}`);
2645
3683
  }
2646
3684
  const specs = timeSegments(use24Hour);
2647
3685
  const [segs, setSegs] = useState(() => fromValue(value, use24Hour));
@@ -2688,10 +3726,10 @@ function TimePicker({
2688
3726
  lastCommitted.current = out;
2689
3727
  if (out !== value) onChange(out);
2690
3728
  };
2691
- const setSegment = (key, v, commit = true) => {
3729
+ const setSegment = (key, v, commit2 = true) => {
2692
3730
  setSegs((s) => {
2693
3731
  const next = { ...s, [key]: v };
2694
- if (commit) commitIfComplete(next);
3732
+ if (commit2) commitIfComplete(next);
2695
3733
  return next;
2696
3734
  });
2697
3735
  };
@@ -2893,7 +3931,7 @@ function TimePicker({
2893
3931
  "button",
2894
3932
  {
2895
3933
  type: "button",
2896
- tabIndex: i === stopOf("hour", chosenHour) ? 0 : -1,
3934
+ tabIndex: i === stopOf2("hour", chosenHour) ? 0 : -1,
2897
3935
  onFocus: () => holdStop("hour", i),
2898
3936
  "data-selected": use24Hour ? h === parseInt(value) : h === parsed.hour,
2899
3937
  onMouseDown: (e) => e.preventDefault(),
@@ -2903,7 +3941,7 @@ function TimePicker({
2903
3941
  "transition-colors focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
2904
3942
  (use24Hour ? h === parseInt(value) : h === parsed.hour) ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)]"
2905
3943
  ),
2906
- children: pad(h)
3944
+ children: pad3(h)
2907
3945
  },
2908
3946
  h
2909
3947
  )) }),
@@ -2912,7 +3950,7 @@ function TimePicker({
2912
3950
  "button",
2913
3951
  {
2914
3952
  type: "button",
2915
- tabIndex: i === stopOf("minute", chosenMinute) ? 0 : -1,
3953
+ tabIndex: i === stopOf2("minute", chosenMinute) ? 0 : -1,
2916
3954
  onFocus: () => holdStop("minute", i),
2917
3955
  "data-selected": m === parsed.minute,
2918
3956
  onMouseDown: (e) => e.preventDefault(),
@@ -2922,7 +3960,7 @@ function TimePicker({
2922
3960
  "transition-colors focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
2923
3961
  m === parsed.minute ? "bg-[var(--ds-accent-primary)] text-[var(--ds-text-on-primary)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)]"
2924
3962
  ),
2925
- children: pad(m)
3963
+ children: pad3(m)
2926
3964
  },
2927
3965
  m
2928
3966
  )) }),
@@ -2932,7 +3970,7 @@ function TimePicker({
2932
3970
  "button",
2933
3971
  {
2934
3972
  type: "button",
2935
- tabIndex: i === stopOf("period", chosenPeriod) ? 0 : -1,
3973
+ tabIndex: i === stopOf2("period", chosenPeriod) ? 0 : -1,
2936
3974
  onFocus: () => holdStop("period", i),
2937
3975
  "data-selected": p === parsed.period,
2938
3976
  onMouseDown: (e) => e.preventDefault(),
@@ -4069,14 +5107,16 @@ function AddAnother({ items, getId, itemName, columns, renderRow, onAdd, onRemov
4069
5107
  const body = /* @__PURE__ */ jsxs("div", { "data-add-another": "", className: cn("@container flex flex-col gap-ds-3", className), style: grid, children: [
4070
5108
  /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: "hidden gap-ds-3 text-sm font-medium text-[var(--ds-text-secondary)] @min-[30rem]:grid @min-[30rem]:grid-cols-[var(--gs-add-another-cols)]", children: [
4071
5109
  columns.map((column) => /* @__PURE__ */ jsx("span", { children: column }, column)),
4072
- canRemove && /* @__PURE__ */ jsx("span", { className: "invisible inline-flex items-center whitespace-nowrap px-ds-4 text-xs font-medium", children: "Remove" })
5110
+ canRemove && /* @__PURE__ */ jsx("span", { className: "invisible inline-flex items-center whitespace-nowrap text-xs font-medium", children: "Remove" })
4073
5111
  ] }),
4074
5112
  /* @__PURE__ */ jsx("ul", { ref: listRef, className: "flex flex-col gap-ds-4 @min-[30rem]:gap-ds-3", children: items.map((item, index) => {
4075
5113
  const id = getId(item);
4076
5114
  return /* @__PURE__ */ jsxs("li", { "data-add-another-row": "", className: "grid grid-cols-1 items-start gap-ds-3 @min-[30rem]:grid-cols-[var(--gs-add-another-cols)]", children: [
4077
5115
  /* @__PURE__ */ jsx(AddAnotherRowCtx.Provider, { value: true, children: renderRow(item, index, (column) => `${column}, ${itemName(index)}`) }),
4078
- canRemove && // the visible "Remove" starts its name, which says whose row it removes (WCAG 2.5.3 label in name)
4079
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": `Remove ${itemName(index)}`, className: "justify-self-start @min-[30rem]:mt-[calc((var(--ds-control-height-md)-var(--ds-control-height-sm))/2)]", onClick: () => {
5116
+ canRemove && // the visible "Remove" starts its name, which says whose row it removes (WCAG 2.5.3 label in name). A
5117
+ // link, alone under or beside its fields (Sprint 25.19 G4, page_composition#51): its word lines up with
5118
+ // the fields' edge, and beside them it takes a field's height, so it sits on their middle
5119
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", "aria-label": `Remove ${itemName(index)}`, className: "justify-self-start @min-[30rem]:h-[var(--ds-control-height-md)]", onClick: () => {
4080
5120
  pending.current = { kind: "remove", id, index };
4081
5121
  reportChange?.();
4082
5122
  onRemove(id);
@@ -4531,7 +5571,7 @@ function holdsHeader(children) {
4531
5571
  Children.forEach(children, (child) => {
4532
5572
  if (found || !isValidElement(child)) return;
4533
5573
  if (child.type === Fragment$1) found = holdsHeader(child.props.children);
4534
- else if (child.type === CardHeader) found = true;
5574
+ else if (elementType(child.type) === CardHeader) found = true;
4535
5575
  });
4536
5576
  return found;
4537
5577
  }
@@ -4847,7 +5887,7 @@ function partsOf(children) {
4847
5887
  }
4848
5888
  function splitTitle(children) {
4849
5889
  const parts = partsOf(children);
4850
- const at = parts.findIndex((p) => isValidElement(p) && p.type === CardTitle);
5890
+ const at = parts.findIndex((p) => isValidElement(p) && elementType(p.type) === CardTitle);
4851
5891
  if (at < 0) return { lead: [], title: parts[0] ?? null, rest: parts.slice(1) };
4852
5892
  return { lead: parts.slice(0, at), title: parts[at], rest: parts.slice(at + 1) };
4853
5893
  }
@@ -4896,7 +5936,7 @@ var CardHeader = forwardRef(function CardHeader2(allProps, ref) {
4896
5936
  const { lead, title, rest } = splitTitle(children);
4897
5937
  const leadBlock = lead.length > 0 ? /* @__PURE__ */ jsx("div", { "data-card-lead": "", className: "flex flex-col space-y-ds-1.5 min-w-0 grow", children: lead }) : null;
4898
5938
  const topLine = iconBlock ?? leadBlock;
4899
- const ownBlock = title !== null && !(isValidElement(title) && title.type === CardTitle);
5939
+ const ownBlock = title !== null && !(isValidElement(title) && elementType(title.type) === CardTitle);
4900
5940
  const actionsSlot = "actions" in allProps ? /* @__PURE__ */ jsx("div", { "data-header-actions": "", className: "contents", children: partsOf(actions).map((item, i) => /* @__PURE__ */ jsx("span", { className: cn("flex items-center shrink-0", TITLE_LINE), children: item }, isValidElement(item) && item.key !== null ? item.key : i)) }) : null;
4901
5941
  return /* @__PURE__ */ jsxs("div", { ref, "data-card-header": "", className: cn("flex flex-col px-ds-5 pt-ds-4 last:pb-ds-4 min-w-0", className), ...props, children: [
4902
5942
  topLine && /* @__PURE__ */ jsxs("div", { "data-card-icon-row": iconBlock ? "" : void 0, "data-card-lead-row": iconBlock ? void 0 : "", className: cn("flex items-start gap-ds-2", iconBlock ? "mb-ds-2.5" : "mb-ds-1.5"), children: [
@@ -5489,7 +6529,7 @@ function useTitleFloorFits(rowRef, active) {
5489
6529
  }
5490
6530
  function actionsAreBadges(actions) {
5491
6531
  const items = Children.toArray(actions);
5492
- return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : item.type === Badge));
6532
+ return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : elementType(item.type) === Badge));
5493
6533
  }
5494
6534
  function TruncCell({ children, lines }) {
5495
6535
  const { ref, isTruncated } = useTextOverflow({ lines });
@@ -5536,19 +6576,13 @@ function sortableValue(v) {
5536
6576
  if (typeof v === "boolean") return v ? 1 : 0;
5537
6577
  return isValidElement(v) || typeof v === "object" ? null : String(v);
5538
6578
  }
5539
- var COLLATORS = /* @__PURE__ */ new Map();
5540
- function collator() {
5541
- const locale = formatLocale() ?? "";
5542
- let c = COLLATORS.get(locale);
5543
- if (!c) {
5544
- c = new Intl.Collator(locale || void 0, { numeric: true });
5545
- COLLATORS.set(locale, c);
5546
- }
5547
- return c;
6579
+ var COLLATORS = localeCache(32);
6580
+ function collator(given) {
6581
+ return COLLATORS(given ?? "", () => new Intl.Collator(given || void 0, { numeric: true }));
5548
6582
  }
5549
- function compareValues(a, b) {
6583
+ function compareValues(a, b, locale) {
5550
6584
  if (typeof a === "number" && typeof b === "number") return a - b;
5551
- return collator().compare(String(a), String(b));
6585
+ return collator(locale).compare(String(a), String(b));
5552
6586
  }
5553
6587
  var UNIT_SCALE = {
5554
6588
  "": 1,
@@ -5710,12 +6744,13 @@ function Table({
5710
6744
  );
5711
6745
  const columnKeys = useMemo(() => columns.map((c) => c.key), [columns]);
5712
6746
  const inferredKinds = useMemo(() => inferColumnKinds(data, columnKeys), [data, columnKeys]);
5713
- const kindOf = useCallback(
6747
+ const kindOf2 = useCallback(
5714
6748
  (col) => col.kind ?? inferredKinds.get(col.key),
5715
6749
  [inferredKinds]
5716
6750
  );
5717
6751
  const sortColumn = sortKey ? columns.find((c) => c.key === sortKey) : void 0;
5718
- const sortKind = sortColumn ? kindOf(sortColumn) : void 0;
6752
+ const sortKind = sortColumn ? kindOf2(sortColumn) : void 0;
6753
+ const locale = useFormatLocale();
5719
6754
  const { sortedAll, unsortable } = useMemo(() => {
5720
6755
  const indexed = data.map((row, i) => ({ row, originalIdx: i }));
5721
6756
  if (!sortKey || !sortDir) return { sortedAll: indexed, unsortable: null };
@@ -5729,11 +6764,11 @@ function Table({
5729
6764
  const elements = valued.some((v) => v.value === null);
5730
6765
  const sorted = elements ? indexed : valued.sort((a, b) => {
5731
6766
  if (a.value === void 0 || b.value === void 0) return a.value === b.value ? 0 : a.value === void 0 ? 1 : -1;
5732
- const cmp = compareValues(a.value, b.value);
6767
+ const cmp = compareValues(a.value, b.value, locale);
5733
6768
  return sortDir === "asc" ? cmp : -cmp;
5734
6769
  });
5735
6770
  return { sortedAll: sorted, unsortable: elements ? sortKey : null };
5736
- }, [data, sortKey, sortDir, sortColumn, sortKind]);
6771
+ }, [data, sortKey, sortDir, sortColumn, sortKind, locale]);
5737
6772
  const canSort = useMemo(() => {
5738
6773
  const can = /* @__PURE__ */ new Set();
5739
6774
  for (const col of columns) if (col.sortValue || data.every((row) => sortableValue(row[col.key]) !== null)) can.add(col.key);
@@ -5828,8 +6863,8 @@ function Table({
5828
6863
  const frame = scroller.clientWidth;
5829
6864
  const nameFits = nameIndex >= 0 && frame > 0 && at + nameWidth + fromRight <= frame / 2;
5830
6865
  if (nameFits) start.push(at);
5831
- const pad3 = (cell, side) => cell ? parseFloat(getComputedStyle(cell)[side]) || 0 : 0;
5832
- const panel = cells.length > lead ? [at + pad3(cells[lead], "paddingLeft"), pad3(cells[cells.length - 1], "paddingRight")] : null;
6866
+ const pad4 = (cell, side) => cell ? parseFloat(getComputedStyle(cell)[side]) || 0 : 0;
6867
+ const panel = cells.length > lead ? [at + pad4(cells[lead], "paddingLeft"), pad4(cells[cells.length - 1], "paddingRight")] : null;
5833
6868
  const next = { start, end, nameFits, frame, panel };
5834
6869
  setPins((prev) => samePins(prev, next) ? prev : next);
5835
6870
  };
@@ -5860,13 +6895,13 @@ function Table({
5860
6895
  if (!table2 || isMobile) return;
5861
6896
  const next = /* @__PURE__ */ new Map();
5862
6897
  for (const col of columns) {
5863
- if (kindOf(col)) continue;
6898
+ if (kindOf2(col)) continue;
5864
6899
  const texts = Array.from(table2.querySelectorAll(`:scope > tbody > tr > td[data-col="${CSS.escape(col.key)}"]`)).slice(0, 50).map((td) => td.textContent ?? "");
5865
6900
  const kind = texts.length ? kindOfRenderedText(texts) : void 0;
5866
6901
  if (kind) next.set(col.key, kind);
5867
6902
  }
5868
6903
  setRenderedKinds((prev) => prev.size === next.size && [...next].every(([k, v]) => prev.get(k) === v) ? prev : next);
5869
- }, [columns, kindOf, drawn, isMobile]);
6904
+ }, [columns, kindOf2, drawn, isMobile]);
5870
6905
  const expandedKey = expanded.map(String).join("");
5871
6906
  const shownBefore = useRef(/* @__PURE__ */ new Set());
5872
6907
  const pendingScroll = useRef(null);
@@ -5913,10 +6948,10 @@ function Table({
5913
6948
  const control = pager && !loading && data.length > 0 ? /* @__PURE__ */ jsx(Pagination, { ...pager.props }) : null;
5914
6949
  const listRoot = control ? void 0 : setRoot;
5915
6950
  const listClass = control ? void 0 : className;
5916
- const withControl = (list) => control ? /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
5917
- list,
6951
+ const withControl = (list2) => control ? /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
6952
+ list2,
5918
6953
  control
5919
- ] }) : list;
6954
+ ] }) : list2;
5920
6955
  const focusWas = useRef(null);
5921
6956
  const noteFocus = (e) => {
5922
6957
  const target = e.target;
@@ -6055,7 +7090,7 @@ function Table({
6055
7090
  columns.map((col, c) => {
6056
7091
  const isSortable = col.header.trim() !== "" && (col.sortable ?? (!!sortable && canSort.has(col.key)));
6057
7092
  const isActiveSort = sortKey === col.key && !!sortDir && unsortable !== col.key;
6058
- const headerKind = kindOf(col);
7093
+ const headerKind = kindOf2(col);
6059
7094
  const headerAlign = col.align ?? (headerKind === "number" ? "right" : "left");
6060
7095
  const shrink = (headerKind === "number" || headerKind === "date") && !col.maxWidth;
6061
7096
  const style = { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth, ...pinStyle(lead + c, cellCount) };
@@ -6177,11 +7212,11 @@ function Table({
6177
7212
  ) }),
6178
7213
  columns.map((col, c) => {
6179
7214
  const truncLines = col.truncate === true ? 1 : col.truncate ? col.truncate.lines : 0;
6180
- const kind = kindOf(col);
7215
+ const kind = kindOf2(col);
6181
7216
  const alignClass = col.align ? col.align === "right" ? "text-right" : col.align === "center" ? "text-center" : "text-left" : kind === "number" ? "text-right" : "text-left";
6182
7217
  const cellContent = row[col.key];
6183
7218
  const shrink = (kind === "number" || kind === "date") && !col.maxWidth;
6184
- const whole = !kind && renderedKinds.has(col.key);
7219
+ const whole2 = !kind && renderedKinds.has(col.key);
6185
7220
  let inner;
6186
7221
  if (truncLines > 0) {
6187
7222
  inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
@@ -6204,7 +7239,7 @@ function Table({
6204
7239
  "text-[var(--ds-text-primary)]",
6205
7240
  alignClass,
6206
7241
  shrink && "w-px whitespace-nowrap",
6207
- whole && "whitespace-nowrap",
7242
+ whole2 && "whitespace-nowrap",
6208
7243
  kind === "identifier" && "[overflow-wrap:anywhere]"
6209
7244
  ),
6210
7245
  style: col.maxWidth || col.width || col.minWidth || c + lead < pinnedStart || c >= columns.length - endCount ? { maxWidth: col.maxWidth, width: col.width, minWidth: col.minWidth, ...pinStyle(lead + c, cellCount) } : void 0,
@@ -6444,7 +7479,7 @@ function SegmentedControl({
6444
7479
  // theme's radius; the segments inside sit concentric with it (SEGMENT_RADIUS)
6445
7480
  "rounded-[var(--ds-radius-md)]",
6446
7481
  // The frame's edge is a button's: each segment is a labelled button, so the frame isn't what identifies the
6447
- // control, and WCAG 1.4.11 asks no 3:1 of it. The selected segment carries the 3:1 edge instead (below)
7482
+ // control, and WCAG 1.4.11 asks no 3:1 of it
6448
7483
  variant === "default" && "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6449
7484
  control.invalid && "border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]",
6450
7485
  className
@@ -6457,9 +7492,11 @@ function SegmentedControl({
6457
7492
  className: cn(
6458
7493
  "absolute rounded-[var(--ds-segment-radius)]",
6459
7494
  "bg-[var(--ds-bg-surface)]",
6460
- // The selected state is what the control shows, so its edge is the one at 3:1 (WCAG 1.4.11), against the
6461
- // frame's fill and its own: the surface on the muted fill alone measures 1.05–1.46:1 across the presets
6462
- "border-[length:var(--ds-border-width)] border-[var(--ds-input-border)]",
7495
+ // The selected segment is a kind of button, so it takes a secondary button's surface and edge, as the frame
7496
+ // takes a button's edge (the owner, 3 October 2026: the 3:1 strong edge it kept from Sprint 25.14 drew the
7497
+ // darker outline 25.19 took off the fields). It shows its state as the raised surface on the muted track, with
7498
+ // the default variant's shadow and its label's stronger colour
7499
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
6463
7500
  variant === "default" && "shadow-[var(--ds-shadow-sm)]",
6464
7501
  "transition-all motion-reduce:transition-none"
6465
7502
  ),
@@ -6533,12 +7570,13 @@ var ACCENT = {
6533
7570
  var NEUTRAL = {
6534
7571
  fill: "var(--ds-bg-muted)",
6535
7572
  text: "var(--ds-text-primary)",
6536
- edge: "var(--ds-input-border)"
7573
+ edge: "var(--ds-control-edge-strong)"
6537
7574
  };
6538
7575
  function EventBar({ event, category, compact, onOpen, inert, showTime = true, continuesBefore, continuesAfter, className, style }) {
6539
7576
  const ref = useRef(null);
6540
7577
  const colours = eventColours(category);
6541
- const name = eventName(event, category);
7578
+ const words = useDateWords();
7579
+ const name = eventName(event, category, words);
6542
7580
  const look = cn(
6543
7581
  "border-[length:var(--ds-border-width)] border-solid rounded-[var(--ds-radius-sm)]",
6544
7582
  continuesBefore && "rounded-s-none border-s-0",
@@ -6591,17 +7629,18 @@ function EventBar({ event, category, compact, onOpen, inert, showTime = true, co
6591
7629
  },
6592
7630
  "aria-label": name,
6593
7631
  children: [
6594
- showTime && !event.allDay && /* @__PURE__ */ jsx("span", { className: "font-medium mr-ds-1", children: formatTime(event.start) }),
7632
+ showTime && !event.allDay && /* @__PURE__ */ jsx("span", { className: "font-medium mr-ds-1", children: words.formatTime(event.start) }),
6595
7633
  event.title
6596
7634
  ]
6597
7635
  }
6598
7636
  );
6599
7637
  }
6600
- function eventName(event, category) {
6601
- return [event.title, formatEventWhen(event, true), category?.label].filter(Boolean).join(", ");
7638
+ function eventName(event, category, words) {
7639
+ return [event.title, words.formatEventWhen(event, true), category?.label].filter(Boolean).join(", ");
6602
7640
  }
6603
7641
  var CALENDAR_LAYER = "data-calendar-layer";
6604
- function EventCard({ event, category, anchor, actions, onClose, fallbackFocus }) {
7642
+ function EventCard({ event, category, anchor, actions, onClose, fallbackFocus, repeatSummary }) {
7643
+ const words = useDateWords();
6605
7644
  const anchorRef = useMemo(() => ({ current: anchor }), [anchor]);
6606
7645
  const panelRef = useRef(null);
6607
7646
  const titleId = useId();
@@ -6645,14 +7684,14 @@ function EventCard({ event, category, anchor, actions, onClose, fallbackFocus })
6645
7684
  };
6646
7685
  }, [anchor, isTop, returnFocus, closeRef]);
6647
7686
  const run = (action) => {
6648
- const list = anchor.closest(`[${CALENDAR_LAYER}]`);
7687
+ const list2 = anchor.closest(`[${CALENDAR_LAYER}]`);
6649
7688
  returnFocus();
6650
7689
  onClose();
6651
7690
  action.onClick(event);
6652
7691
  requestAnimationFrame(() => {
6653
7692
  const now = document.activeElement;
6654
7693
  if (now && now !== document.body && now.isConnected) return;
6655
- if (list?.isConnected && focusInitial(list)) return;
7694
+ if (list2?.isConnected && focusInitial(list2)) return;
6656
7695
  fallbackFocus?.()?.focus();
6657
7696
  });
6658
7697
  };
@@ -6700,15 +7739,19 @@ function EventCard({ event, category, anchor, actions, onClose, fallbackFocus })
6700
7739
  category.label
6701
7740
  ] }),
6702
7741
  /* @__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) })
7742
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: words.formatEventWhen(event) }),
7743
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: words.formatEventWhen(event, true) })
7744
+ ] }),
7745
+ repeatSummary && /* @__PURE__ */ jsxs("p", { "data-card-repeat": "", className: "text-xs text-[var(--ds-text-secondary)] flex items-start gap-ds-1", children: [
7746
+ /* @__PURE__ */ jsx(Icon, { name: "refresh", size: 14, className: "shrink-0 mt-px" }),
7747
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: repeatSummary })
6705
7748
  ] }),
6706
7749
  event.location && /* @__PURE__ */ jsxs("p", { className: "text-xs text-[var(--ds-text-secondary)] flex items-start gap-ds-1", children: [
6707
7750
  /* @__PURE__ */ jsx(Icon, { name: "map-pin", size: 14, className: "shrink-0 mt-px" }),
6708
7751
  /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: event.location })
6709
7752
  ] }),
6710
7753
  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: [
7754
+ event.attendees && event.attendees.length > 0 && /* @__PURE__ */ jsx("div", { ref: setAttendeeScroll, className: cn("max-h-32 overflow-y-auto -mx-[2px] p-[2px]", FOCUS_INSET), 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
7755
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "shrink-0", children: /* @__PURE__ */ jsx(Avatar, { size: "sm", initials: person.initials ?? person.name }) }),
6713
7756
  /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: person.name })
6714
7757
  ] }, `${person.name}-${i}`)) }) }),
@@ -6720,6 +7763,7 @@ function EventCard({ event, category, anchor, actions, onClose, fallbackFocus })
6720
7763
  );
6721
7764
  }
6722
7765
  function DayList({ day, events, categories, anchor, onOpenEvent, onNewEvent, onClose }) {
7766
+ const { formatDayLong, formatDayShort } = useDateWords();
6723
7767
  const anchorRef = useMemo(() => ({ current: anchor }), [anchor]);
6724
7768
  const panelRef = useRef(null);
6725
7769
  const pos = usePosition(anchorRef, panelRef, true, { side: "bottom", align: "start", offset: 4, flip: true });
@@ -6803,16 +7847,18 @@ var LANE_GAP = 4;
6803
7847
  var NUMBER_ROW = 30;
6804
7848
  var INSET = "var(--ds-space-1)";
6805
7849
  function MonthView({ date, events, categories, selectedDate, onDayClick, canStartEvent, onOpenEvent, onNavigate, weekStartsOn }) {
7850
+ const { weekdayNames, formatDayLong, formatMonthYear } = useDateWords();
7851
+ const now = useToday();
6806
7852
  const weeks = useMemo(() => getMonthGrid(date.getFullYear(), date.getMonth(), weekStartsOn), [date, weekStartsOn]);
6807
7853
  const phone = useBreakpoint("sm");
6808
7854
  const names = { short: weekdayNames(weekStartsOn, "short"), narrow: weekdayNames(weekStartsOn, "narrow"), long: weekdayNames(weekStartsOn, "long") };
6809
7855
  const layouts = useMemo(() => weeks.map((week) => layoutWeek(events, week, LANES)), [weeks, events]);
6810
7856
  const currentMonth = date.getMonth();
6811
7857
  const gridRef = useRef(null);
6812
- const [list, setList] = useState(null);
7858
+ const [list2, setList] = useState(null);
6813
7859
  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);
7860
+ const fallback = inMonth(selectedDate) ? selectedDate : inMonth(now) ? now : new Date(date.getFullYear(), currentMonth, 1);
7861
+ const [activeDay, setActiveDay] = useState(null);
6816
7862
  const active = inMonth(activeDay) ? activeDay : fallback;
6817
7863
  const openList = (day, anchor) => setList({ day, anchor });
6818
7864
  const choose = (day, anchor) => phone ? openList(day, anchor) : onDayClick(day);
@@ -6836,7 +7882,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
6836
7882
  return /* @__PURE__ */ jsx("div", { role: "row", className: "relative grid grid-cols-7 flex-1", children: week.map((day, di) => {
6837
7883
  const dayEvents = getEventsForDay(events, day);
6838
7884
  const isCurrentMonth = day.getMonth() === currentMonth;
6839
- const today = isToday(day);
7885
+ const today = now !== null && isSameDay(day, now);
6840
7886
  const selected = !!selectedDate && isSameDay(day, selectedDate);
6841
7887
  const weekend = isWeekend(day);
6842
7888
  const more = layout.more[di];
@@ -6851,7 +7897,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
6851
7897
  "aria-selected": selected,
6852
7898
  "aria-current": today ? "date" : void 0,
6853
7899
  onFocus: () => {
6854
- if (!isSameDay(day, active)) setActiveDay(day);
7900
+ if (!activeDay || !isSameDay(day, activeDay)) setActiveDay(day);
6855
7901
  },
6856
7902
  onClick: (e) => choose(day, e.currentTarget),
6857
7903
  className: cn(
@@ -6900,7 +7946,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
6900
7946
  {
6901
7947
  type: "button",
6902
7948
  "aria-haspopup": "dialog",
6903
- "aria-expanded": !!list && isSameDay(list.day, day),
7949
+ "aria-expanded": !!list2 && isSameDay(list2.day, day),
6904
7950
  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
7951
  style: { top: NUMBER_ROW + (LANES - 1) * (LANE_H + LANE_GAP), left: INSET, height: LANE_H },
6906
7952
  onClick: (e) => {
@@ -6922,13 +7968,13 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
6922
7968
  }) }, dayKey(week[0]));
6923
7969
  }) })
6924
7970
  ] }),
6925
- list && /* @__PURE__ */ jsx(
7971
+ list2 && /* @__PURE__ */ jsx(
6926
7972
  DayList,
6927
7973
  {
6928
- day: list.day,
6929
- events: getEventsForDay(events, list.day),
7974
+ day: list2.day,
7975
+ events: getEventsForDay(events, list2.day),
6930
7976
  categories,
6931
- anchor: list.anchor,
7977
+ anchor: list2.anchor,
6932
7978
  onOpenEvent,
6933
7979
  onNewEvent: phone && canStartEvent ? onDayClick : void 0,
6934
7980
  onClose: () => setList(null)
@@ -6964,6 +8010,7 @@ function NowLine() {
6964
8010
  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
8011
  }
6966
8012
  function TimedEvent({ placed, category, onOpen, onFocusEvent }) {
8013
+ const words = useDateWords();
6967
8014
  const { piece, column, columns } = placed;
6968
8015
  const { event } = piece;
6969
8016
  const top = timeToFraction(piece.start) * GRID_HEIGHT;
@@ -6979,7 +8026,7 @@ function TimedEvent({ placed, category, onOpen, onFocusEvent }) {
6979
8026
  {
6980
8027
  type: "button",
6981
8028
  "data-calendar-event": event.id,
6982
- "aria-label": eventName(event, category),
8029
+ "aria-label": eventName(event, category, words),
6983
8030
  className: cn(
6984
8031
  // a short event keeps its title whole: no padding above and below it
6985
8032
  "absolute pointer-events-auto text-left px-ds-1.5 text-xs leading-tight",
@@ -7007,13 +8054,16 @@ function TimedEvent({ placed, category, onOpen, onFocusEvent }) {
7007
8054
  onFocus: onFocusEvent,
7008
8055
  children: [
7009
8056
  /* @__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) })
8057
+ height > 30 && /* @__PURE__ */ jsx("span", { className: "text-2xs truncate w-full tabular-nums", children: words.formatTimeRange(event.start, end) })
7011
8058
  ]
7012
8059
  }
7013
8060
  );
7014
8061
  }
7015
8062
  function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEvent, onNavigate, weekStartsOn }) {
7016
8063
  const days = useMemo(() => getWeekDays(date, weekStartsOn), [date, weekStartsOn]);
8064
+ const { weekdayNames, formatHourLabel, formatTime: formatTime2, formatDayLong, formatWeekRange } = useDateWords();
8065
+ const now = useToday();
8066
+ const isToday = (d) => now !== null && isSameDay(d, now);
7017
8067
  const names = weekdayNames(weekStartsOn, "short");
7018
8068
  const barEvents = useMemo(() => events.filter(isBarEvent), [events]);
7019
8069
  const allDay = useMemo(() => layoutWeek(barEvents, days, ALL_DAY_LANES), [barEvents, days]);
@@ -7023,7 +8073,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7023
8073
  const scrollerRef = useRef(null);
7024
8074
  const syncingScroll = useRef(false);
7025
8075
  const { ref: edgesRef, measure: measureEdges, fadeProps: edgeFade } = useScrollEdges();
7026
- const [list, setList] = useState(null);
8076
+ const [list2, setList] = useState(null);
7027
8077
  const handleScroll = useCallback((e) => {
7028
8078
  if (syncingScroll.current) return;
7029
8079
  syncingScroll.current = true;
@@ -7038,8 +8088,8 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7038
8088
  panels?.forEach((panel) => panel.addEventListener("scroll", handleScroll));
7039
8089
  return () => panels?.forEach((panel) => panel.removeEventListener("scroll", handleScroll));
7040
8090
  }, [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));
8091
+ const starts2 = pieces.flat().filter((p) => p.piece.start.getTime() === p.piece.event.start.getTime()).map((p) => p.piece.start.getHours() + p.piece.start.getMinutes() / 60);
8092
+ const earliest = Math.floor(Math.min(WORKING_DAY, ...starts2));
7043
8093
  const topOf = (hour) => Math.max(0, hour * HOUR_HEIGHT - HOUR_LABEL_ROOM);
7044
8094
  const openingHourRef = useLatest(earliest);
7045
8095
  const opened = useRef(null);
@@ -7079,8 +8129,8 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7079
8129
  }, [earliest, week]);
7080
8130
  const inWeek = (d) => days.some((day) => isSameDay(day, d));
7081
8131
  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);
8132
+ const [activeKey, setActiveKey] = useState(null);
8133
+ const active = activeKey !== null && inWeek(parseKey(activeKey).day) ? activeKey : cellKey(anchorDay, activeKey !== null ? parseKey(activeKey).hour : openingHour);
7084
8134
  const firstVisibleHour = () => Math.min(23, Math.ceil((scrollerRef.current?.scrollTop ?? 0) / HOUR_HEIGHT));
7085
8135
  const placeInView = (_key, el) => {
7086
8136
  const scroller = scrollerRef.current;
@@ -7165,7 +8215,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7165
8215
  tabIndex: grid.tabIndexOf(key),
7166
8216
  "aria-label": `${formatDayLong(day)}, all day`,
7167
8217
  onFocus: () => {
7168
- if (active !== key) setActiveKey(key);
8218
+ if (activeKey !== key) setActiveKey(key);
7169
8219
  },
7170
8220
  onClick: () => onDayClick(day),
7171
8221
  className: cn("relative cursor-pointer", FOCUS_INSET, i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"),
@@ -7193,7 +8243,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7193
8243
  {
7194
8244
  type: "button",
7195
8245
  "aria-haspopup": "dialog",
7196
- "aria-expanded": !!list && isSameDay(list.day, day),
8246
+ "aria-expanded": !!list2 && isSameDay(list2.day, day),
7197
8247
  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
8248
  style: { top: 2 + (ALL_DAY_LANES - 1) * (LANE_H2 + LANE_GAP2), left: 2, height: LANE_H2 },
7199
8249
  onClick: (e) => {
@@ -7224,9 +8274,9 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7224
8274
  role: "gridcell",
7225
8275
  "data-grid-key": key,
7226
8276
  tabIndex: grid.tabIndexOf(key),
7227
- "aria-label": `${formatDayLong(day)}, ${formatTime(new Date(day.getFullYear(), day.getMonth(), day.getDate(), hour))}`,
8277
+ "aria-label": `${formatDayLong(day)}, ${formatTime2(new Date(day.getFullYear(), day.getMonth(), day.getDate(), hour))}`,
7228
8278
  onFocus: () => {
7229
- if (active !== key) setActiveKey(key);
8279
+ if (activeKey !== key) setActiveKey(key);
7230
8280
  },
7231
8281
  onClick: chooseHour(day, hour),
7232
8282
  className: cn(
@@ -7253,18 +8303,21 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
7253
8303
  ] }, i)) })
7254
8304
  ] }) })
7255
8305
  ] }),
7256
- list && /* @__PURE__ */ jsx(DayList, { day: list.day, events: getEventsForDay(barEvents, list.day), categories, anchor: list.anchor, onOpenEvent, onClose: () => setList(null) })
8306
+ list2 && /* @__PURE__ */ jsx(DayList, { day: list2.day, events: getEventsForDay(barEvents, list2.day), categories, anchor: list2.anchor, onOpenEvent, onClose: () => setList(null) })
7257
8307
  ] });
7258
8308
  }
7259
8309
  var WEEKS_SHOWN = 6;
7260
8310
  var COLS = WEEKS_SHOWN * 7;
8311
+ var NONE = [];
7261
8312
  function DayCellWithPopover({
7262
8313
  day,
7263
8314
  month,
7264
8315
  events,
7265
8316
  categories,
7266
8317
  selectedDate,
7267
- isEvenRow
8318
+ isEvenRow,
8319
+ today: now,
8320
+ words
7268
8321
  }) {
7269
8322
  const [open, setOpen] = useState(false);
7270
8323
  const cellRef = useRef(null);
@@ -7277,8 +8330,8 @@ function DayCellWithPopover({
7277
8330
  });
7278
8331
  const hoverTimeout = useRef(void 0);
7279
8332
  const inMonth = day.getMonth() === month;
7280
- const dayEvents = inMonth ? getEventsForDay(events, day) : [];
7281
- const today = inMonth && isToday(day);
8333
+ const dayEvents = inMonth ? events : NONE;
8334
+ const today = inMonth && now !== null && isSameDay(day, now);
7282
8335
  const selected = inMonth && selectedDate && isSameDay(day, selectedDate);
7283
8336
  const hasEvents = dayEvents.length > 0;
7284
8337
  const handleMouseEnter = () => {
@@ -7334,7 +8387,7 @@ function DayCellWithPopover({
7334
8387
  style: { top: pos.top, left: pos.left },
7335
8388
  role: "tooltip",
7336
8389
  children: [
7337
- /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-0.5", children: formatDayShort(day) }),
8390
+ /* @__PURE__ */ jsx("p", { className: "text-2xs font-medium text-[var(--ds-text-muted)] px-ds-1 mb-ds-0.5", children: words.formatDayShort(day) }),
7338
8391
  dayEvents.map((event) => /* @__PURE__ */ jsx(EventBar, { event, category: categories[event.category ?? ""], inert: true }, event.id))
7339
8392
  ]
7340
8393
  }
@@ -7348,11 +8401,13 @@ function DayCellWithPopover({
7348
8401
  function MonthRow({
7349
8402
  year,
7350
8403
  month,
7351
- events,
8404
+ byDay,
7352
8405
  categories,
7353
8406
  selectedDate,
7354
8407
  onClick,
7355
- weekStartsOn
8408
+ weekStartsOn,
8409
+ today,
8410
+ words
7356
8411
  }) {
7357
8412
  const grid = useMemo(
7358
8413
  () => getMonthGrid(year, month, weekStartsOn),
@@ -7361,15 +8416,18 @@ function MonthRow({
7361
8416
  const flat = grid.flat();
7362
8417
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7363
8418
  /* @__PURE__ */ jsx(
7364
- "div",
8419
+ "button",
7365
8420
  {
8421
+ type: "button",
8422
+ "aria-label": `${words.monthName(month, "short")} ${year}`,
7366
8423
  className: cn(
7367
8424
  "sticky left-0 z-10 flex items-center justify-center px-ds-2 text-xs font-semibold text-[var(--ds-text-primary)]",
7368
8425
  "bg-[var(--ds-bg-muted)] cursor-pointer hover:text-[var(--ds-accent-primary)] transition-colors",
7369
- "border-r-[length:var(--ds-divider-width)] border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"
8426
+ "border-r-[length:var(--ds-divider-width)] border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]",
8427
+ FOCUS_INSET
7370
8428
  ),
7371
8429
  onClick,
7372
- children: monthName(month, "short")
8430
+ children: words.monthName(month, "short")
7373
8431
  }
7374
8432
  ),
7375
8433
  flat.map((day, i) => /* @__PURE__ */ jsx(
@@ -7377,10 +8435,12 @@ function MonthRow({
7377
8435
  {
7378
8436
  day,
7379
8437
  month,
7380
- events,
8438
+ events: byDay.get(dayKey(day)) ?? NONE,
7381
8439
  categories,
7382
8440
  selectedDate,
7383
- isEvenRow: month % 2 === 0
8441
+ isEvenRow: month % 2 === 0,
8442
+ today,
8443
+ words
7384
8444
  },
7385
8445
  i
7386
8446
  ))
@@ -7395,7 +8455,10 @@ function YearView({
7395
8455
  weekStartsOn
7396
8456
  }) {
7397
8457
  const year = date.getFullYear();
7398
- const dayNames = weekdayNames(weekStartsOn, "narrow");
8458
+ const words = useDateWords();
8459
+ const today = useToday();
8460
+ const dayNames = words.weekdayNames(weekStartsOn, "narrow");
8461
+ const byDay = useMemo(() => groupEventsByDay(events, new Date(year, 0, 1), new Date(year + 1, 0, 1)), [events, year]);
7399
8462
  const repeatedHeaders = useMemo(() => {
7400
8463
  const headers = [];
7401
8464
  for (let w = 0; w < WEEKS_SHOWN; w++) {
@@ -7432,11 +8495,13 @@ function YearView({
7432
8495
  {
7433
8496
  year,
7434
8497
  month,
7435
- events,
8498
+ byDay,
7436
8499
  categories,
7437
8500
  selectedDate,
7438
8501
  onClick: () => onMonthClick(month),
7439
- weekStartsOn
8502
+ weekStartsOn,
8503
+ today,
8504
+ words
7440
8505
  },
7441
8506
  month
7442
8507
  ))
@@ -7445,14 +8510,16 @@ function YearView({
7445
8510
  ) });
7446
8511
  }
7447
8512
  function CompactMonthView({ date, events, categories, selectedDate, onDayClick, onOpenEvent, onNavigate, weekStartsOn }) {
8513
+ const { weekdayNames, formatDayLong, formatMonthYear } = useDateWords();
8514
+ const now = useToday();
7448
8515
  const weeks = useMemo(() => getMonthGrid(date.getFullYear(), date.getMonth(), weekStartsOn), [date, weekStartsOn]);
7449
8516
  const names = { narrow: weekdayNames(weekStartsOn, "narrow"), long: weekdayNames(weekStartsOn, "long") };
7450
8517
  const currentMonth = date.getMonth();
7451
8518
  const gridRef = useRef(null);
7452
- const [list, setList] = useState(null);
8519
+ const [list2, setList] = useState(null);
7453
8520
  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);
8521
+ const fallback = inMonth(selectedDate) ? selectedDate : inMonth(now) ? now : new Date(date.getFullYear(), currentMonth, 1);
8522
+ const [activeDay, setActiveDay] = useState(null);
7456
8523
  const active = inMonth(activeDay) ? activeDay : fallback;
7457
8524
  const grid = useGridKeyboard({
7458
8525
  container: gridRef,
@@ -7470,7 +8537,7 @@ function CompactMonthView({ date, events, categories, selectedDate, onDayClick,
7470
8537
  weeks.map((week) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7", children: week.map((day) => {
7471
8538
  const isCurrentMonth = day.getMonth() === currentMonth;
7472
8539
  const dayEvents = isCurrentMonth ? getEventsForDay(events, day) : [];
7473
- const today = isCurrentMonth && isToday(day);
8540
+ const today = isCurrentMonth && now !== null && isSameDay(day, now);
7474
8541
  const selected = isCurrentMonth && !!selectedDate && isSameDay(day, selectedDate);
7475
8542
  return /* @__PURE__ */ jsxs(
7476
8543
  "button",
@@ -7483,7 +8550,7 @@ function CompactMonthView({ date, events, categories, selectedDate, onDayClick,
7483
8550
  "aria-selected": selected,
7484
8551
  "aria-current": today ? "date" : void 0,
7485
8552
  onFocus: () => {
7486
- if (!isSameDay(day, active)) setActiveDay(day);
8553
+ if (!activeDay || !isSameDay(day, activeDay)) setActiveDay(day);
7487
8554
  },
7488
8555
  onClick: (e) => {
7489
8556
  onDayClick(day);
@@ -7506,7 +8573,7 @@ function CompactMonthView({ date, events, categories, selectedDate, onDayClick,
7506
8573
  );
7507
8574
  }) }, dayKey(week[0])))
7508
8575
  ] }),
7509
- list && /* @__PURE__ */ jsx(DayList, { day: list.day, events: getEventsForDay(events, list.day), categories, anchor: list.anchor, onOpenEvent, onClose: () => setList(null) })
8576
+ list2 && /* @__PURE__ */ jsx(DayList, { day: list2.day, events: getEventsForDay(events, list2.day), categories, anchor: list2.anchor, onOpenEvent, onClose: () => setList(null) })
7510
8577
  ] });
7511
8578
  }
7512
8579
 
@@ -7539,10 +8606,19 @@ var viewLabels = {
7539
8606
  year: "Year"
7540
8607
  };
7541
8608
  var VIEWS = ["week", "month", "year"];
8609
+ var PENDING_HEAD = { default: "h-[97px] @min-[500px]:h-[49px]", compact: "h-[41px]" };
8610
+ var PENDING_BODY = {
8611
+ month: "h-[513px] sm:h-[633px]",
8612
+ week: "h-[1056px]",
8613
+ year: "h-[455px]",
8614
+ compact: "h-[332px]"
8615
+ };
8616
+ var NO_EVENTS = [];
7542
8617
  function MonthDropdown({
7543
8618
  currentDate,
7544
8619
  onSelect
7545
8620
  }) {
8621
+ const { formatMonthYear, monthName: monthName2 } = useDateWords();
7546
8622
  const [open, setOpen] = useState(false);
7547
8623
  const [browseYear, setBrowseYear] = useState(currentDate.getFullYear());
7548
8624
  const triggerRef = useRef(null);
@@ -7642,7 +8718,7 @@ function MonthDropdown({
7642
8718
  }
7643
8719
  )
7644
8720
  ] }),
7645
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-ds-1", children: Array.from({ length: 12 }, (_, i) => monthName(i, "short")).map((name, i) => {
8721
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-ds-1", children: Array.from({ length: 12 }, (_, i) => monthName2(i, "short")).map((name, i) => {
7646
8722
  const isActive2 = i === currentMonth && browseYear === currentYear;
7647
8723
  return /* @__PURE__ */ jsx(
7648
8724
  "button",
@@ -7815,10 +8891,14 @@ function Calendar({
7815
8891
  onDayClick,
7816
8892
  eventActions,
7817
8893
  categories = DEFAULT_CATEGORIES,
7818
- weekStartsOn = 1,
8894
+ weekStartsOn: weekStartsOnProp,
7819
8895
  size = "default",
7820
8896
  className
7821
8897
  }) {
8898
+ const weekStartsOn = useWeekStart(weekStartsOnProp);
8899
+ const { formatWeekRange, formatMonthYear } = useDateWords();
8900
+ const locale = useFormatLocale();
8901
+ const hydrated = useHydrated();
7822
8902
  const [currentView, setCurrentView] = useControllableState(
7823
8903
  controlledView,
7824
8904
  defaultView,
@@ -7885,6 +8965,32 @@ function Calendar({
7885
8965
  useEffect(() => {
7886
8966
  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
8967
  }, [unreadable]);
8968
+ const drawnFrom = useMemo(() => {
8969
+ if (currentView === "week") return startOfWeek(currentDate, weekStartsOn);
8970
+ if (currentView === "year") return new Date(currentDate.getFullYear(), 0, 1);
8971
+ return getMonthGrid(currentDate.getFullYear(), currentDate.getMonth(), weekStartsOn)[0][0];
8972
+ }, [currentView, currentDate, weekStartsOn]);
8973
+ const drawnTo = useMemo(() => {
8974
+ if (currentView === "week") return addDays2(drawnFrom, 7);
8975
+ if (currentView === "year") return new Date(drawnFrom.getFullYear() + 1, 0, 1);
8976
+ return addDays2(drawnFrom, 42);
8977
+ }, [currentView, drawnFrom]);
8978
+ const { drawnEvents, unreadableRepeats } = useMemo(() => {
8979
+ const out = [];
8980
+ const odd = [];
8981
+ for (const e of stableEvents) {
8982
+ if (!e.repeat) {
8983
+ out.push(e);
8984
+ continue;
8985
+ }
8986
+ if (!readRecurrence(e.repeat)) odd.push(String(e.id));
8987
+ out.push(...occurrences(e, drawnFrom, drawnTo));
8988
+ }
8989
+ return { drawnEvents: out, unreadableRepeats: odd.join(", ") };
8990
+ }, [stableEvents, drawnFrom, drawnTo]);
8991
+ useEffect(() => {
8992
+ if (unreadableRepeats) warnOnce("Calendar", `events whose repeat it can't read are drawn once (${unreadableRepeats}): pass RecurrencePicker's rule, or an RRULE in the subset parseRRule reads.`);
8993
+ }, [unreadableRepeats]);
7888
8994
  useEffect(() => {
7889
8995
  if (!onDateRangeChange) return;
7890
8996
  let start, end;
@@ -7924,35 +9030,133 @@ function Calendar({
7924
9030
  },
7925
9031
  [onDayClick]
7926
9032
  );
9033
+ const viewRef = useRef(null);
9034
+ const focusGridNext = useRef(null);
7927
9035
  const handleMonthClick = useCallback(
7928
9036
  (month) => {
9037
+ focusGridNext.current = { year: currentDate.getFullYear(), month, until: Date.now() + 2e3 };
7929
9038
  setCurrentDate(new Date(currentDate.getFullYear(), month, 1));
7930
9039
  setCurrentView("month");
7931
9040
  },
7932
9041
  [currentDate, setCurrentDate, setCurrentView]
7933
9042
  );
9043
+ useEffect(() => {
9044
+ const wish = focusGridNext.current;
9045
+ if (!wish) return;
9046
+ if (Date.now() > wish.until || currentView !== "month" && currentView !== "year") {
9047
+ focusGridNext.current = null;
9048
+ return;
9049
+ }
9050
+ if (currentView !== "month" || currentDate.getFullYear() !== wish.year || currentDate.getMonth() !== wish.month) return;
9051
+ focusGridNext.current = null;
9052
+ viewRef.current?.querySelector('[role="grid"] [role="gridcell"][tabindex="0"]')?.focus();
9053
+ }, [currentView, currentDate]);
7934
9054
  const headerLabel = useMemo(() => {
7935
9055
  if (currentView === "week") return formatWeekRange(currentDate, weekStartsOn);
7936
9056
  return void 0;
7937
- }, [currentView, currentDate, weekStartsOn]);
7938
- const live = openEvent && stableEvents.find((e) => e.id === openEvent.event.id);
9057
+ }, [currentView, currentDate, weekStartsOn, formatWeekRange]);
9058
+ const lastEvent = useRef(null);
9059
+ const drawnRef = useLatest(drawnEvents);
9060
+ const rememberEvent = useCallback((e) => {
9061
+ const id = e.target.closest?.("[data-calendar-event]")?.getAttribute("data-calendar-event");
9062
+ const event = id ? drawnRef.current.find((x) => x.id === id) : void 0;
9063
+ if (event) lastEvent.current = event;
9064
+ }, [drawnRef]);
9065
+ const forgetEvent = useCallback((e) => {
9066
+ const to = e.relatedTarget;
9067
+ if (to && (rootRef.current?.contains(to) || to.closest('[role="dialog"], [role="alertdialog"]'))) return;
9068
+ lastEvent.current = null;
9069
+ }, []);
9070
+ useEffect(() => {
9071
+ const gone = lastEvent.current;
9072
+ if (!gone || drawnEvents.some((e) => e.id === gone.id)) return;
9073
+ const key = currentView === "week" && !gone.allDay ? `${dayKey(gone.start)}T${String(gone.start.getHours()).padStart(2, "0")}` : dayKey(gone.start);
9074
+ const fallen = () => {
9075
+ const active = document.activeElement;
9076
+ return !active || active === document.body || !active.isConnected;
9077
+ };
9078
+ const inOverlay = () => !!document.activeElement?.closest?.('[role="dialog"], [role="alertdialog"]');
9079
+ const SETTLE_MS = 120;
9080
+ let check;
9081
+ const settle = () => {
9082
+ if (fallen()) {
9083
+ rootRef.current?.querySelector(`[role="gridcell"][data-grid-key="${key}"]`)?.focus();
9084
+ stop();
9085
+ return;
9086
+ }
9087
+ if (!inOverlay()) {
9088
+ stop();
9089
+ return;
9090
+ }
9091
+ check = setTimeout(settle, SETTLE_MS);
9092
+ };
9093
+ const onOut = () => {
9094
+ clearTimeout(check);
9095
+ check = setTimeout(settle, SETTLE_MS);
9096
+ };
9097
+ check = setTimeout(settle, SETTLE_MS);
9098
+ const timer = setTimeout(() => stop(), 1e4);
9099
+ function stop() {
9100
+ document.removeEventListener("focusout", onOut, true);
9101
+ clearTimeout(timer);
9102
+ clearTimeout(check);
9103
+ lastEvent.current = null;
9104
+ }
9105
+ document.addEventListener("focusout", onOut, true);
9106
+ return () => {
9107
+ document.removeEventListener("focusout", onOut, true);
9108
+ clearTimeout(timer);
9109
+ clearTimeout(check);
9110
+ };
9111
+ }, [drawnEvents, currentView]);
9112
+ const live = openEvent && drawnEvents.find((e) => e.id === openEvent.event.id);
9113
+ const series = live?.series ? stableEvents.find((e) => e.id === live.series.id) : live?.repeat ? live : void 0;
9114
+ const seriesRule = series ? readRecurrence(series.repeat) : null;
9115
+ const repeatSummary = series?.repeat ? seriesRule ? describeRecurrence(seriesRule, series.start, locale) : "Repeats" : void 0;
7939
9116
  const card = openEvent && live ? /* @__PURE__ */ jsx(
7940
9117
  EventCard,
7941
9118
  {
7942
9119
  event: live,
7943
9120
  category: categories[live.category ?? ""],
7944
9121
  anchor: openEvent.anchor,
9122
+ repeatSummary,
7945
9123
  actions: typeof eventActions === "function" ? eventActions(live) : eventActions ?? [],
7946
9124
  onClose: () => setOpenEvent(null),
7947
9125
  fallbackFocus: () => rootRef.current?.querySelector('[role="gridcell"][tabindex="0"]')
7948
9126
  },
7949
9127
  openEvent.event.id
7950
9128
  ) : null;
9129
+ const shownEvents = hydrated ? drawnEvents : NO_EVENTS;
9130
+ if (!hydrated) {
9131
+ return /* @__PURE__ */ jsxs(
9132
+ "div",
9133
+ {
9134
+ ref: containerRef,
9135
+ "aria-busy": "true",
9136
+ "data-calendar-pending": "",
9137
+ className: cn(
9138
+ "flex flex-col @container",
9139
+ "bg-[var(--ds-bg-surface)]",
9140
+ "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
9141
+ size === "compact" ? "rounded-[var(--ds-radius-md)]" : "rounded-[var(--ds-radius-lg)]",
9142
+ "shadow-[var(--ds-shadow-sm)]",
9143
+ "overflow-hidden",
9144
+ className
9145
+ ),
9146
+ children: [
9147
+ /* @__PURE__ */ jsx("div", { className: cn("bg-[var(--ds-bg-muted)] border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", size === "compact" ? PENDING_HEAD.compact : PENDING_HEAD.default) }),
9148
+ /* @__PURE__ */ jsx("div", { className: size === "compact" ? PENDING_BODY.compact : PENDING_BODY[currentView] })
9149
+ ]
9150
+ }
9151
+ );
9152
+ }
7951
9153
  if (size === "compact") {
7952
9154
  return /* @__PURE__ */ jsxs(
7953
9155
  "div",
7954
9156
  {
7955
9157
  ref: containerRef,
9158
+ onFocus: rememberEvent,
9159
+ onBlur: forgetEvent,
7956
9160
  className: cn(
7957
9161
  "flex flex-col",
7958
9162
  "bg-[var(--ds-bg-surface)]",
@@ -8006,7 +9210,7 @@ function Calendar({
8006
9210
  CompactMonthView,
8007
9211
  {
8008
9212
  date: currentDate,
8009
- events: stableEvents,
9213
+ events: shownEvents,
8010
9214
  categories,
8011
9215
  selectedDate,
8012
9216
  onDayClick: handleDayClick,
@@ -8024,6 +9228,8 @@ function Calendar({
8024
9228
  "div",
8025
9229
  {
8026
9230
  ref: containerRef,
9231
+ onFocus: rememberEvent,
9232
+ onBlur: forgetEvent,
8027
9233
  "data-fill": fill2 != null ? "" : void 0,
8028
9234
  className: cn(
8029
9235
  "flex flex-col",
@@ -8094,12 +9300,12 @@ function Calendar({
8094
9300
  ]
8095
9301
  }
8096
9302
  ),
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: [
9303
+ /* @__PURE__ */ jsxs("div", { ref: viewRef, className: "flex-1 min-h-0", style: { minHeight: currentView === "week" ? fill2 != null || inPane ? void 0 : 420 : currentView === "month" ? isNarrow ? 360 : 500 : void 0 }, children: [
8098
9304
  currentView === "month" && /* @__PURE__ */ jsx(
8099
9305
  MonthView,
8100
9306
  {
8101
9307
  date: currentDate,
8102
- events: stableEvents,
9308
+ events: shownEvents,
8103
9309
  categories,
8104
9310
  selectedDate,
8105
9311
  onDayClick: handleDayClick,
@@ -8113,7 +9319,7 @@ function Calendar({
8113
9319
  WeekView,
8114
9320
  {
8115
9321
  date: currentDate,
8116
- events: stableEvents,
9322
+ events: shownEvents,
8117
9323
  categories,
8118
9324
  selectedDate,
8119
9325
  onDayClick: handleDayClick,
@@ -8126,7 +9332,7 @@ function Calendar({
8126
9332
  YearView,
8127
9333
  {
8128
9334
  date: currentDate,
8129
- events: stableEvents,
9335
+ events: shownEvents,
8130
9336
  categories,
8131
9337
  selectedDate,
8132
9338
  onMonthClick: handleMonthClick,
@@ -8201,7 +9407,7 @@ var defaultIcons = {
8201
9407
  var FRAME_ICON_SIZE = 28;
8202
9408
  function renderIcon(icon) {
8203
9409
  if (typeof icon === "string") return /* @__PURE__ */ jsx(Icon, { name: icon, size: FRAME_ICON_SIZE, className: "text-[var(--ds-text-muted)]", from: "EmptyState's icon" });
8204
- if (isValidElement(icon) && icon.type === Icon) return cloneElement(icon, { size: FRAME_ICON_SIZE });
9410
+ if (isValidElement(icon) && elementType(icon.type) === Icon) return cloneElement(icon, { size: FRAME_ICON_SIZE });
8205
9411
  return icon;
8206
9412
  }
8207
9413
  function EmptyState({ icon, iconFrame = "circle", title, description, action, variant = "empty", bordered = false, style: styleProp, className }) {
@@ -8459,30 +9665,32 @@ function HorizontalLayout({ items, minItemWidth, className }) {
8459
9665
  ] }, i);
8460
9666
  }) });
8461
9667
  }
8462
- var EXACT = { format: (d) => new Intl.DateTimeFormat(formatLocale(), { dateStyle: "medium", timeStyle: "short" }).format(d) };
8463
- var EXACT_DAY = { format: (d) => new Intl.DateTimeFormat(formatLocale(), { dateStyle: "medium" }).format(d) };
9668
+ var exactFormat = (locale, d) => new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(d);
9669
+ var exactDayFormat = (locale, d) => new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(d);
8464
9670
  var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
8465
- function exactTime(value) {
9671
+ function exactTime(value, locale) {
8466
9672
  if (typeof value === "string") {
8467
9673
  const day = DATE_ONLY.exec(value.trim());
8468
9674
  if (day) {
8469
9675
  const [y, m, d] = [Number(day[1]), Number(day[2]) - 1, Number(day[3])];
8470
9676
  const date2 = new Date(y, m, d);
8471
- return date2.getFullYear() === y && date2.getMonth() === m && date2.getDate() === d ? { iso: value.trim(), text: EXACT_DAY.format(date2) } : null;
9677
+ return date2.getFullYear() === y && date2.getMonth() === m && date2.getDate() === d ? { iso: value.trim(), text: exactDayFormat(locale, date2) } : null;
8472
9678
  }
8473
9679
  }
8474
9680
  const date = value instanceof Date ? value : new Date(value);
8475
- return Number.isNaN(date.getTime()) ? null : { iso: date.toISOString(), text: EXACT.format(date) };
9681
+ return Number.isNaN(date.getTime()) ? null : { iso: date.toISOString(), text: exactFormat(locale, date) };
8476
9682
  }
8477
9683
  function ItemTime({ item }) {
9684
+ const locale = useFormatLocale();
9685
+ const hydrated = useHydrated();
8478
9686
  if (item.time !== void 0) {
8479
9687
  return /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 tabular-nums", children: /* @__PURE__ */ jsx(RelativeTime, { time: item.time }) });
8480
9688
  }
8481
- const exact = item.dateTime ? exactTime(item.dateTime) : null;
9689
+ const exact = item.dateTime ? exactTime(item.dateTime, locale) : null;
8482
9690
  if (!item.timestamp && !exact) return null;
8483
- return /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 tabular-nums", children: exact ? /* @__PURE__ */ jsxs("time", { dateTime: exact.iso, title: exact.text, children: [
8484
- item.timestamp ?? exact.text,
8485
- item.timestamp && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
9691
+ return /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 tabular-nums", children: exact ? /* @__PURE__ */ jsxs("time", { dateTime: exact.iso, title: hydrated ? exact.text : void 0, children: [
9692
+ item.timestamp ?? (hydrated ? exact.text : "\xA0"),
9693
+ item.timestamp && hydrated && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
8486
9694
  " (",
8487
9695
  exact.text,
8488
9696
  ")"
@@ -8515,10 +9723,10 @@ function Timeline({ items: allItems, orientation = "vertical", minItemWidth = 20
8515
9723
  }, [ref]);
8516
9724
  const fits = width === 0 || width >= items.length * minItemWidth;
8517
9725
  const horizontal = orientation === "horizontal" || orientation === "responsive" && fits;
8518
- const list = /* @__PURE__ */ jsx("div", { ref: setRefs, "data-ds-list": "", "data-timeline-orientation": horizontal ? "horizontal" : "vertical", className: pager ? void 0 : className, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: horizontal ? /* @__PURE__ */ jsx(HorizontalLayout, { items, minItemWidth }) : /* @__PURE__ */ jsx(VerticalLayout, { items }) }) });
8519
- if (!pager) return list;
9726
+ const list2 = /* @__PURE__ */ jsx("div", { ref: setRefs, "data-ds-list": "", "data-timeline-orientation": horizontal ? "horizontal" : "vertical", className: pager ? void 0 : className, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: horizontal ? /* @__PURE__ */ jsx(HorizontalLayout, { items, minItemWidth }) : /* @__PURE__ */ jsx(VerticalLayout, { items }) }) });
9727
+ if (!pager) return list2;
8520
9728
  return /* @__PURE__ */ jsxs("div", { "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
8521
- list,
9729
+ list2,
8522
9730
  allItems.length > 0 && /* @__PURE__ */ jsx(Pagination, { ...pager.props })
8523
9731
  ] });
8524
9732
  }
@@ -8733,8 +9941,8 @@ function TreeView({
8733
9941
  );
8734
9942
  const flatNodes = useCallback(() => {
8735
9943
  const result = [];
8736
- const walk = (list, depth) => {
8737
- for (const node of list) {
9944
+ const walk = (list2, depth) => {
9945
+ for (const node of list2) {
8738
9946
  result.push({ node, depth });
8739
9947
  if (node.children && expanded.has(node.id)) walk(node.children, depth + 1);
8740
9948
  }
@@ -9083,8 +10291,8 @@ function Carousel({
9083
10291
  }
9084
10292
  );
9085
10293
  }
9086
- function formatTime2(d) {
9087
- return d.toLocaleTimeString(formatLocale(), { hour: "2-digit", minute: "2-digit" });
10294
+ function formatTime(d, locale) {
10295
+ return d.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" });
9088
10296
  }
9089
10297
  function isSameMinute(a, b) {
9090
10298
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate() && a.getHours() === b.getHours() && a.getMinutes() === b.getMinutes();
@@ -9164,6 +10372,9 @@ function SourcesRow({ sources }) {
9164
10372
  }
9165
10373
  var StreamingCaret = () => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-ds-0.5 inline-block animate-pulse text-[var(--ds-accent-primary)]", children: "\u258D" });
9166
10374
  var ChatMessageItem = memo(function ChatMessageItem2({ message: msg, grouped, spaced, layout, onAction, renderContent }) {
10375
+ const locale = useFormatLocale();
10376
+ const hydrated = useHydrated();
10377
+ const time = hydrated ? formatTime(msg.timestamp, locale) : "\xA0";
9167
10378
  const streaming = msg.status === "streaming";
9168
10379
  const content = renderContent ? renderContent(msg) : msg.content;
9169
10380
  if (layout === "block") {
@@ -9178,7 +10389,7 @@ var ChatMessageItem = memo(function ChatMessageItem2({ message: msg, grouped, sp
9178
10389
  !grouped && /* @__PURE__ */ jsxs("div", { className: "mb-ds-2 flex items-center gap-ds-2", children: [
9179
10390
  !msg.isOwn && /* @__PURE__ */ jsx(SenderVisual, { sender: msg.sender }),
9180
10391
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-[var(--ds-text-secondary)]", children: msg.isOwn ? "You" : msg.sender.name }),
9181
- /* @__PURE__ */ jsx("span", { className: "text-2xs text-[var(--ds-text-muted)]", children: formatTime2(msg.timestamp) })
10392
+ /* @__PURE__ */ jsx("span", { className: "text-2xs text-[var(--ds-text-muted)]", children: time })
9182
10393
  ] }),
9183
10394
  msg.status === "error" ? /* @__PURE__ */ jsx(Alert, { variant: "error", title: "Reply failed", children: typeof content === "string" && content ? content : void 0 }) : /* @__PURE__ */ jsx("div", { className: "space-y-ds-4 text-sm leading-relaxed text-[var(--ds-text-primary)]", children: typeof content === "string" ? /* @__PURE__ */ jsxs("p", { className: "whitespace-pre-wrap", children: [
9184
10395
  content,
@@ -9218,7 +10429,7 @@ var ChatMessageItem = memo(function ChatMessageItem2({ message: msg, grouped, sp
9218
10429
  ]
9219
10430
  }
9220
10431
  ),
9221
- !grouped && /* @__PURE__ */ jsx("span", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 mx-ds-1", children: formatTime2(msg.timestamp) }),
10432
+ !grouped && /* @__PURE__ */ jsx("span", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-0.5 mx-ds-1", children: time }),
9222
10433
  msg.sources && msg.sources.length > 0 && /* @__PURE__ */ jsx(SourcesRow, { sources: msg.sources }),
9223
10434
  /* @__PURE__ */ jsx(ActionsRow, { message: msg, onAction })
9224
10435
  ] })
@@ -9248,7 +10459,7 @@ function ChatInputBar({ onSend, busy, onStop, onAttach, onVoice, placeholder })
9248
10459
  className: cn(
9249
10460
  "flex flex-col",
9250
10461
  "rounded-[var(--ds-radius-lg)]",
9251
- // Sprint 25.14 A20: the composer is a field, its edge at 3:1 like every other control's
10462
+ // the composer is a field: its edge is every other control's resting edge (Sprint 25.14 A20, 25.19 K1)
9252
10463
  "border-[length:var(--ds-border-width)] border-[var(--ds-input-border)]",
9253
10464
  "bg-[var(--ds-input-bg)]",
9254
10465
  "shadow-[var(--ds-input-shadow)]",
@@ -9526,12 +10737,12 @@ function apply(target, corner) {
9526
10737
  }
9527
10738
  function claimCornerInset(corner, inset, priority, target) {
9528
10739
  const claim = { inset, priority, seq: ++seq };
9529
- const list = claimsFor(target)[corner];
9530
- list.push(claim);
10740
+ const list2 = claimsFor(target)[corner];
10741
+ list2.push(claim);
9531
10742
  apply(target, corner);
9532
10743
  return () => {
9533
- const i = list.indexOf(claim);
9534
- if (i >= 0) list.splice(i, 1);
10744
+ const i = list2.indexOf(claim);
10745
+ if (i >= 0) list2.splice(i, 1);
9535
10746
  apply(target, corner);
9536
10747
  };
9537
10748
  }
@@ -9865,7 +11076,7 @@ var StackedList = forwardRef(function StackedList2({
9865
11076
  const stackMeta = isPhone && articles;
9866
11077
  const selectable = selectedId !== void 0 || items.some((i) => i.selected !== void 0);
9867
11078
  const inset = articles ? inCard ? "px-ds-5" : "px-0" : inCard ? "px-ds-5" : inPane ? "pl-ds-3 pr-ds-4" : selectable ? "pl-ds-3 pr-0" : "px-0";
9868
- const list = /* @__PURE__ */ jsx(
11079
+ const list2 = /* @__PURE__ */ jsx(
9869
11080
  "ul",
9870
11081
  {
9871
11082
  ref,
@@ -9967,12 +11178,14 @@ var StackedList = forwardRef(function StackedList2({
9967
11178
  }) })
9968
11179
  }
9969
11180
  );
9970
- if (!pager) return list;
11181
+ if (!pager) return list2;
9971
11182
  return /* @__PURE__ */ jsxs("div", { "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
9972
- list,
11183
+ list2,
9973
11184
  items.length > 0 && /* @__PURE__ */ jsx(Pagination, { ...pager.props })
9974
11185
  ] });
9975
11186
  });
11187
+
11188
+ // ../../showcase/src/lib/time-days.ts
9976
11189
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
9977
11190
  var DAY_MS2 = 864e5;
9978
11191
  var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
@@ -9999,9 +11212,15 @@ function dayOf(value) {
9999
11212
  return back.getUTCFullYear() === y && back.getUTCMonth() === mo && back.getUTCDate() === d ? day : NaN;
10000
11213
  }
10001
11214
  function dateOfDay(day) {
10002
- const whole = Math.floor(day);
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));
11215
+ const whole2 = Math.floor(day);
11216
+ const u = new Date(whole2 * DAY_MS2);
11217
+ return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole2) * DAY_MS2));
11218
+ }
11219
+ function isZoned(value) {
11220
+ if (value instanceof Date) return !Number.isNaN(value.getTime());
11221
+ if (typeof value !== "string") return false;
11222
+ const text = value.trim();
11223
+ return !DATE_ONLY2.test(text) && !Number.isNaN(new Date(text).getTime());
10005
11224
  }
10006
11225
  function isWholeDay(value) {
10007
11226
  return typeof value === "string" ? DATE_ONLY2.test(value.trim()) : Number.isInteger(dayOf(value));
@@ -10013,6 +11232,60 @@ function endDayOf(value) {
10013
11232
  function rangeEnd(start, end) {
10014
11233
  return end instanceof Date && !isWholeDay(start) ? dayOf(end) : endDayOf(end);
10015
11234
  }
11235
+ var formats2 = localeCache(128);
11236
+ function dayFormatter(given, options) {
11237
+ const locale = given ?? formatLocale();
11238
+ return formats2(`${locale ?? ""}|${JSON.stringify(options)}`, () => new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" }));
11239
+ }
11240
+ function timeExtent(ranges) {
11241
+ let start = Infinity;
11242
+ let end = -Infinity;
11243
+ for (const r of ranges) {
11244
+ if (r == null) continue;
11245
+ const s = dayOf(r.start);
11246
+ const e = r.end == null ? endDayOf(r.start) : rangeEnd(r.start, r.end);
11247
+ if (Number.isFinite(s)) start = Math.min(start, s);
11248
+ if (Number.isFinite(e)) end = Math.max(end, e);
11249
+ }
11250
+ return Number.isFinite(start) && Number.isFinite(end) ? { start, end: Math.max(start, end) } : { start: NaN, end: NaN };
11251
+ }
11252
+ var DAY_WORDS = { day: "numeric", month: "long" };
11253
+ var TIME_WORDS = { hour: "2-digit", minute: "2-digit" };
11254
+ var SEPARATOR = /\s*[\u2010-\u2015\u2212~\uff5e\u301c-]\s*/;
11255
+ var english = (f) => new Intl.Locale(f.resolvedOptions().locale).language === "en";
11256
+ function range(f, a, b, to) {
11257
+ if (typeof f.formatRangeToParts !== "function") return to === void 0 ? f.formatRange(a, b) : `${f.format(a)}${to}${f.format(b)}`;
11258
+ const parts = f.formatRangeToParts(a, b);
11259
+ const lastOfStart = parts.map((p) => p.source).lastIndexOf("startRange");
11260
+ const firstOfEnd = parts.findIndex((p) => p.source === "endRange");
11261
+ if (lastOfStart < 0 || firstOfEnd < 0) return f.format(a);
11262
+ if (to === void 0) return parts.map((p) => p.value).join("");
11263
+ let joined = false;
11264
+ const out = parts.map((p, i) => {
11265
+ if (joined || i <= lastOfStart || i >= firstOfEnd || !SEPARATOR.test(p.value)) return p.value;
11266
+ joined = true;
11267
+ return p.value.replace(SEPARATOR, to);
11268
+ });
11269
+ if (!joined) out.splice(lastOfStart + 1, 0, to);
11270
+ return out.join("");
11271
+ }
11272
+ function formatDateRange(start, end, words) {
11273
+ const { locale, to, day = DAY_WORDS, time = TIME_WORDS } = words ?? {};
11274
+ const until = end ?? start;
11275
+ const from = dayOf(start);
11276
+ const stop = rangeEnd(start, until);
11277
+ if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
11278
+ const timed = !isWholeDay(start) || !isWholeDay(until);
11279
+ const first = timed ? from : Math.floor(from);
11280
+ const last2 = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
11281
+ const a = new Date(first * DAY_MS2);
11282
+ const b = new Date(last2 * DAY_MS2);
11283
+ const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
11284
+ const f = dayFormatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
11285
+ return first === last2 ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
11286
+ }
11287
+
11288
+ // ../../showcase/src/lib/time-scale.ts
10016
11289
  var MINOR = { day: "day", week: "week", month: "month", quarter: "quarter" };
10017
11290
  var MAJOR = { day: "month", week: "month", month: "year", quarter: "year" };
10018
11291
  var weekday = (day) => (day % 7 + 7 + 4) % 7;
@@ -10037,19 +11310,8 @@ function periodAfter(unit, start) {
10037
11310
  if (unit === "quarter") return utcDay(y, m + 3, 1);
10038
11311
  return utcDay(y + 1, 0, 1);
10039
11312
  }
10040
- var formats = /* @__PURE__ */ new Map();
10041
- function formatter(given, options) {
10042
- const locale = given ?? formatLocale();
10043
- const key = `${locale ?? ""}|${JSON.stringify(options)}`;
10044
- let f = formats.get(key);
10045
- if (!f) {
10046
- f = new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" });
10047
- formats.set(key, f);
10048
- }
10049
- return f;
10050
- }
10051
- function format(day, locale, options) {
10052
- return formatter(locale, options).format(new Date(day * DAY_MS2));
11313
+ function format2(day, locale, options) {
11314
+ return dayFormatter(locale, options).format(new Date(day * DAY_MS2));
10053
11315
  }
10054
11316
  var pad22 = (n) => String(n).padStart(2, "0");
10055
11317
  function keyOf(unit, day) {
@@ -10062,7 +11324,7 @@ function keyOf(unit, day) {
10062
11324
  return `${y}-${pad22(m + 1)}-${pad22(u.getUTCDate())}`;
10063
11325
  }
10064
11326
  function labelsOf(unit, role, day, locale) {
10065
- const f = (options) => format(day, locale, options);
11327
+ const f = (options) => format2(day, locale, options);
10066
11328
  let forms;
10067
11329
  if (unit === "day") forms = [f({ weekday: "short", day: "numeric" }), f({ day: "numeric" })];
10068
11330
  else if (unit === "week") forms = [f({ day: "numeric", month: "short" }), f({ day: "numeric" })];
@@ -10122,7 +11384,7 @@ function build(s) {
10122
11384
  return scale;
10123
11385
  }
10124
11386
  var toMinute = (day) => Math.floor(day * 1440 + 1e-6) / 1440;
10125
- function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, pad3) {
11387
+ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, pad4) {
10126
11388
  const fallback = Number.isFinite(todayValue) ? Math.floor(todayValue) : 0;
10127
11389
  const a = Number.isFinite(from) ? from : fallback;
10128
11390
  const b = Number.isFinite(to) ? to : Math.floor(a) + 1;
@@ -10131,7 +11393,7 @@ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, p
10131
11393
  const unit = MINOR[zoom];
10132
11394
  let startDay = periodStart(unit, lo, weekStartsOn);
10133
11395
  let endDay = periodAfter(unit, periodStart(unit, hi - 1, weekStartsOn));
10134
- for (let i = 0; i < pad3; i++) {
11396
+ for (let i = 0; i < pad4; i++) {
10135
11397
  startDay = periodStart(unit, startDay - 1, weekStartsOn);
10136
11398
  endDay = periodAfter(unit, endDay);
10137
11399
  }
@@ -10140,8 +11402,8 @@ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, p
10140
11402
  function extendedEnd(scale, width) {
10141
11403
  if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale.endDay;
10142
11404
  const unit = MINOR[scale.zoom];
10143
- const last = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
10144
- return periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
11405
+ const last2 = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
11406
+ return periodAfter(unit, periodStart(unit, last2, scale.weekStartsOn));
10145
11407
  }
10146
11408
  function extendTimeScaleTo(scale, endDay) {
10147
11409
  if (!(endDay > scale.endDay)) return scale;
@@ -10154,7 +11416,10 @@ var noSubscription = () => () => {
10154
11416
  };
10155
11417
  var inBrowser2 = () => true;
10156
11418
  var onServer2 = () => false;
10157
- function useTimeScale({ start, end, zoom, today, weekStartsOn = 1, locale, pxPerDay, pad: pad3 = 0 }) {
11419
+ function useTimeScale({ start, end, zoom, today, weekStartsOn: own, locale: ownLocale, pxPerDay, pad: pad4 = 0 }) {
11420
+ const weekStartsOn = useWeekStart(own, ownLocale);
11421
+ const pageLocale = useFormatLocale();
11422
+ const locale = ownLocale ?? pageLocale;
10158
11423
  const readable = useSyncExternalStore(noSubscription, inBrowser2, onServer2);
10159
11424
  const [mounted] = useState(() => toMinute(dayOf(/* @__PURE__ */ new Date())));
10160
11425
  const pending = today === void 0 && !readable;
@@ -10163,57 +11428,10 @@ function useTimeScale({ start, end, zoom, today, weekStartsOn = 1, locale, pxPer
10163
11428
  const todayValue = today !== void 0 ? toMinute(dayOf(today)) : pending ? NaN : mounted;
10164
11429
  const density = pxPerDay?.[zoom] ?? DENSITY[zoom];
10165
11430
  return useMemo(() => {
10166
- const scale = scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, density, pad3);
11431
+ const scale = scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, density, pad4);
10167
11432
  if (pending) awaiting.add(scale);
10168
11433
  return scale;
10169
- }, [from, to, zoom, todayValue, weekStartsOn, locale, density, pad3, pending]);
10170
- }
10171
- function timeExtent(ranges) {
10172
- let start = Infinity;
10173
- let end = -Infinity;
10174
- for (const r of ranges) {
10175
- if (r == null) continue;
10176
- const s = dayOf(r.start);
10177
- const e = r.end == null ? endDayOf(r.start) : rangeEnd(r.start, r.end);
10178
- if (Number.isFinite(s)) start = Math.min(start, s);
10179
- if (Number.isFinite(e)) end = Math.max(end, e);
10180
- }
10181
- return Number.isFinite(start) && Number.isFinite(end) ? { start, end: Math.max(start, end) } : { start: NaN, end: NaN };
10182
- }
10183
- var DAY_WORDS = { day: "numeric", month: "long" };
10184
- var TIME_WORDS = { hour: "2-digit", minute: "2-digit" };
10185
- var SEPARATOR = /\s*[\u2010-\u2015\u2212~\uff5e\u301c-]\s*/;
10186
- var english = (f) => new Intl.Locale(f.resolvedOptions().locale).language === "en";
10187
- function range(f, a, b, to) {
10188
- if (typeof f.formatRangeToParts !== "function") return to === void 0 ? f.formatRange(a, b) : `${f.format(a)}${to}${f.format(b)}`;
10189
- const parts = f.formatRangeToParts(a, b);
10190
- const lastOfStart = parts.map((p) => p.source).lastIndexOf("startRange");
10191
- const firstOfEnd = parts.findIndex((p) => p.source === "endRange");
10192
- if (lastOfStart < 0 || firstOfEnd < 0) return f.format(a);
10193
- if (to === void 0) return parts.map((p) => p.value).join("");
10194
- let joined = false;
10195
- const out = parts.map((p, i) => {
10196
- if (joined || i <= lastOfStart || i >= firstOfEnd || !SEPARATOR.test(p.value)) return p.value;
10197
- joined = true;
10198
- return p.value.replace(SEPARATOR, to);
10199
- });
10200
- if (!joined) out.splice(lastOfStart + 1, 0, to);
10201
- return out.join("");
10202
- }
10203
- function formatDateRange(start, end, words) {
10204
- const { locale, to, day = DAY_WORDS, time = TIME_WORDS } = words ?? {};
10205
- const until = end ?? start;
10206
- const from = dayOf(start);
10207
- const stop = rangeEnd(start, until);
10208
- if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
10209
- const timed = !isWholeDay(start) || !isWholeDay(until);
10210
- const first = timed ? from : Math.floor(from);
10211
- const last = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
10212
- const a = new Date(first * DAY_MS2);
10213
- const b = new Date(last * DAY_MS2);
10214
- const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
10215
- const f = formatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
10216
- return first === last ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
11434
+ }, [from, to, zoom, todayValue, weekStartsOn, locale, density, pad4, pending]);
10217
11435
  }
10218
11436
  function createScrollStore() {
10219
11437
  let current = { scrollLeft: 0, visibleWidth: 0 };
@@ -10334,11 +11552,11 @@ function measurer(info) {
10334
11552
  return v;
10335
11553
  };
10336
11554
  }
10337
- function useTextMeasure(ref) {
11555
+ function useTextMeasure(ref, active = true) {
10338
11556
  const [info, setInfo] = useState(null);
10339
11557
  useLayoutEffect(() => {
10340
11558
  const el = ref.current;
10341
- if (!el) return;
11559
+ if (!active || !el) return;
10342
11560
  let live = true;
10343
11561
  const read = (fresh) => {
10344
11562
  if (!live) return;
@@ -10358,7 +11576,7 @@ function useTextMeasure(ref) {
10358
11576
  ro?.disconnect();
10359
11577
  fonts?.removeEventListener?.("loadingdone", loaded);
10360
11578
  };
10361
- }, [ref]);
11579
+ }, [ref, active]);
10362
11580
  return useMemo(() => measurer(info), [info]);
10363
11581
  }
10364
11582
  var fontGeneration = 0;
@@ -10449,6 +11667,7 @@ function TimeScaleHeader({ todayLabel = "Today", className }) {
10449
11667
  const todayRef = useRef(null);
10450
11668
  const measured = visibleWidth > 0;
10451
11669
  const view = { start: scrollLeft, end: scrollLeft + visibleWidth };
11670
+ const hydrated = useHydrated();
10452
11671
  const major = useFittedLabels(scale.major, majorRef, measured ? { visible: view } : {});
10453
11672
  const minor = useFittedLabels(scale.minor, minorRef, measured ? { align: "center", visible: view } : { align: "center" });
10454
11673
  const measureToday = useTextMeasure(todayRef);
@@ -10461,12 +11680,12 @@ function TimeScaleHeader({ todayLabel = "Today", className }) {
10461
11680
  /* @__PURE__ */ jsxs("div", { "data-scale-row": "major", className: "relative h-7 font-medium text-[var(--ds-text-primary)]", children: [
10462
11681
  /* @__PURE__ */ jsx("span", { ref: majorRef, className: "invisible absolute whitespace-nowrap leading-none", children: "0" }),
10463
11682
  scale.major.slice(1).map((t) => /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 border-s-[length:1px] border-[var(--ds-border-subtle)]", style: { insetInlineStart: t.x } }, `line-${t.key}`)),
10464
- major.map((l) => l.text && /* @__PURE__ */ jsx("span", { "data-scale-label": "", className: "absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none", style: { insetInlineStart: l.x }, children: l.text }, l.key))
11683
+ hydrated && major.map((l) => l.text && /* @__PURE__ */ jsx("span", { "data-scale-label": "", className: "absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none", style: { insetInlineStart: l.x }, children: l.text }, l.key))
10465
11684
  ] }),
10466
11685
  /* @__PURE__ */ jsxs("div", { "data-scale-row": "minor", className: "relative h-7 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] text-[var(--ds-text-secondary)]", children: [
10467
11686
  /* @__PURE__ */ jsx("span", { ref: minorRef, className: "invisible absolute whitespace-nowrap leading-none tabular-nums", children: "0" }),
10468
11687
  scale.minor.slice(1).map((t) => /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 border-s-[length:1px] border-[var(--ds-border-subtle)]", style: { insetInlineStart: t.x } }, `line-${t.key}`)),
10469
- minor.map((l) => l.text && !underToday(l.x, l.width) && /* @__PURE__ */ jsx("span", { "data-scale-label": "", className: "absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none tabular-nums", style: { insetInlineStart: l.x }, children: l.text }, l.key)),
11688
+ hydrated && minor.map((l) => l.text && !underToday(l.x, l.width) && /* @__PURE__ */ jsx("span", { "data-scale-label": "", className: "absolute top-1/2 -translate-y-1/2 whitespace-nowrap leading-none tabular-nums", style: { insetInlineStart: l.x }, children: l.text }, l.key)),
10470
11689
  /* @__PURE__ */ jsx(
10471
11690
  "span",
10472
11691
  {
@@ -10643,8 +11862,8 @@ var TimeViewport = forwardRef(function TimeViewport2({
10643
11862
  if (shadeDays && drawn.pxPerDay >= SHADE_MIN) {
10644
11863
  for (let d = drawn.startDay; d < drawn.endDay; d++) {
10645
11864
  if (!shadeDays(calendarDay(d))) continue;
10646
- const last = shaded[shaded.length - 1];
10647
- if (last && Math.abs(last.x + last.width - drawn.x(d)) < 0.5) last.width += drawn.pxPerDay;
11865
+ const last2 = shaded[shaded.length - 1];
11866
+ if (last2 && Math.abs(last2.x + last2.width - drawn.x(d)) < 0.5) last2.width += drawn.pxPerDay;
10648
11867
  else shaded.push({ key: String(d), x: drawn.x(d), width: drawn.pxPerDay });
10649
11868
  }
10650
11869
  }
@@ -10771,6 +11990,7 @@ var TimeViewport = forwardRef(function TimeViewport2({
10771
11990
  ref: scrollerRef,
10772
11991
  role: "region",
10773
11992
  "aria-label": ariaLabel,
11993
+ suppressHydrationWarning: true,
10774
11994
  tabIndex: tabStop ? 0 : void 0,
10775
11995
  onScroll,
10776
11996
  className: "peer h-full overflow-auto isolate outline-none",
@@ -10875,6 +12095,10 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
10875
12095
  }
10876
12096
  );
10877
12097
  }
12098
+ function LocaleProvider({ lang, acceptLanguage, children }) {
12099
+ const value = useMemo(() => localeChoice(lang, acceptLanguage), [lang, acceptLanguage]);
12100
+ return /* @__PURE__ */ jsx(LocaleContext.Provider, { value, children });
12101
+ }
10878
12102
 
10879
12103
  // ../../showcase/src/lib/gantt-layout.ts
10880
12104
  var ROW_HEIGHT2 = 40;
@@ -10895,18 +12119,18 @@ function datesOf(item) {
10895
12119
  const end = asDate(item.end);
10896
12120
  return end !== void 0 && Number.isFinite(endDayOf(end)) ? { start, end } : null;
10897
12121
  }
10898
- function uniqueById(list) {
12122
+ function uniqueById(list2) {
10899
12123
  const seen = /* @__PURE__ */ new Set();
10900
12124
  const repeated = /* @__PURE__ */ new Set();
10901
12125
  const unique = [];
10902
- for (const entry of list) {
12126
+ for (const entry of list2) {
10903
12127
  if (seen.has(entry.id)) repeated.add(entry.id);
10904
12128
  else {
10905
12129
  seen.add(entry.id);
10906
12130
  unique.push(entry);
10907
12131
  }
10908
12132
  }
10909
- return repeated.size ? { unique, repeated: [...repeated] } : { unique: list, repeated: [] };
12133
+ return repeated.size ? { unique, repeated: [...repeated] } : { unique: list2, repeated: [] };
10910
12134
  }
10911
12135
  function ganttRows(items, groups, collapsed) {
10912
12136
  const known = new Set(groups.map((g) => g.id));
@@ -10917,8 +12141,8 @@ function ganttRows(items, groups, collapsed) {
10917
12141
  loose.push(item);
10918
12142
  continue;
10919
12143
  }
10920
- const list = byGroup.get(item.group);
10921
- if (list) list.push(item);
12144
+ const list2 = byGroup.get(item.group);
12145
+ if (list2) list2.push(item);
10922
12146
  else byGroup.set(item.group, [item]);
10923
12147
  }
10924
12148
  const shown = groups.filter((g) => byGroup.has(g.id));
@@ -10988,8 +12212,8 @@ function ganttArrows(items, rows, shapeOf, collapsed) {
10988
12212
  }
10989
12213
  return arrows;
10990
12214
  }
10991
- function labelPlace(shape, textWidth, scaleWidth, { pad: pad3 = 8, gap = BESIDE } = {}) {
10992
- if (shape.kind === "bar" && textWidth + 2 * pad3 <= shape.x1 - shape.x0) return "inside";
12215
+ function labelPlace(shape, textWidth, scaleWidth, { pad: pad4 = 8, gap = BESIDE } = {}) {
12216
+ if (shape.kind === "bar" && textWidth + 2 * pad4 <= shape.x1 - shape.x0) return "inside";
10993
12217
  if (shape.x1 + gap + textWidth <= scaleWidth) return "after";
10994
12218
  return shape.x0 - gap - textWidth >= 0 ? "before" : "after";
10995
12219
  }
@@ -11121,6 +12345,11 @@ function Gantt({
11121
12345
  if (phases.repeated.length) warnOnce2(`more than one group has the id ${quoted(phases.repeated)}; only the first of each is shown.`);
11122
12346
  }, [plan, phases]);
11123
12347
  const tasks = plan.unique;
12348
+ const pageLocale = useFormatLocale();
12349
+ const writeLocale = locale ?? pageLocale;
12350
+ const hydrated = useHydrated();
12351
+ const zoned = useMemo(() => isZoned(today) || tasks.some((t) => isZoned(t.start) || isZoned(t.end)), [tasks, today]);
12352
+ const pending = zoned && !hydrated;
11124
12353
  const extent = useMemo(() => itemsExtent(tasks), [tasks]);
11125
12354
  const scale = useTimeScale({ start: extent.start, end: extent.end, zoom, today, weekStartsOn, locale, pad: 1 });
11126
12355
  const rows = useMemo(() => ganttRows(tasks, phases.unique, collapsed), [tasks, phases, collapsed]);
@@ -11130,7 +12359,7 @@ function Gantt({
11130
12359
  const byKey = useMemo(() => new Map(rows.map((r) => [r.key, r])), [rows]);
11131
12360
  const names = useMemo(() => {
11132
12361
  const byId = new Map(tasks.map((i) => [i.id, i]));
11133
- const list = new Intl.ListFormat(locale ?? formatLocale(), { type: "conjunction" });
12362
+ const list2 = new Intl.ListFormat(writeLocale, { type: "conjunction" });
11134
12363
  const out = /* @__PURE__ */ new Map();
11135
12364
  for (const item of tasks) {
11136
12365
  const dates = datesOf(item);
@@ -11139,29 +12368,29 @@ function Gantt({
11139
12368
  out.set(itemKey(item.id), [
11140
12369
  item.title,
11141
12370
  item.milestone ? "milestone" : null,
11142
- dates ? formatDateRange(dates.start, dates.end, { locale }) : NO_DATES,
12371
+ dates ? formatDateRange(dates.start, dates.end, { locale: writeLocale }) : NO_DATES,
11143
12372
  status?.label ?? null,
11144
- after.length ? `after ${list.format(after)}` : null
12373
+ after.length ? `after ${list2.format(after)}` : null
11145
12374
  ].filter((part) => part).join(", "));
11146
12375
  }
11147
12376
  for (const row of rows) {
11148
12377
  if (!row.group || !row.children) continue;
11149
12378
  const e = extents.get(row.group.id);
11150
12379
  const count = row.children.length === 1 ? "1 item" : `${row.children.length} items`;
11151
- const span = Number.isFinite(e.start) ? formatDateRange(e.start, e.end, { locale }) : NO_DATES;
12380
+ const span = Number.isFinite(e.start) ? formatDateRange(e.start, e.end, { locale: writeLocale }) : NO_DATES;
11152
12381
  out.set(row.key, [row.group.title, span, count].filter((part) => part).join(", "));
11153
12382
  }
11154
12383
  return out;
11155
- }, [tasks, rows, extents, statuses, locale]);
12384
+ }, [tasks, rows, extents, statuses, writeLocale]);
11156
12385
  const nameOf = (row) => {
11157
12386
  const name = names.get(row.key) ?? "";
11158
12387
  const words = row.item && describe ? describe(row.item) : "";
11159
12388
  return words ? `${name}, ${words}` : name;
11160
12389
  };
11161
12390
  const regionLabel = useMemo(() => {
11162
- const span = Number.isFinite(extent.start) ? formatDateRange(extent.start, extent.end, { locale }) : "";
12391
+ const span = Number.isFinite(extent.start) ? formatDateRange(extent.start, extent.end, { locale: writeLocale }) : "";
11163
12392
  return `${ariaLabel}, ${span || NO_DATES}`;
11164
- }, [ariaLabel, extent, locale]);
12393
+ }, [ariaLabel, extent, writeLocale]);
11165
12394
  const hasStatuses = useMemo(() => Object.keys(statuses).length > 0, [statuses]);
11166
12395
  const toneOf = (item) => {
11167
12396
  const status = item.status === void 0 ? void 0 : statuses[item.status];
@@ -11169,7 +12398,7 @@ function Gantt({
11169
12398
  return { tone, cue: hasStatuses ? status?.icon ?? tone.icon : void 0 };
11170
12399
  };
11171
12400
  const probeRef = useRef(null);
11172
- const measure = useTextMeasure(probeRef);
12401
+ const measure = useTextMeasure(probeRef, !pending);
11173
12402
  const treeRef = useRef(null);
11174
12403
  const viewportRef = useRef(null);
11175
12404
  const [activeState, setActive] = useState(null);
@@ -11280,6 +12509,9 @@ function Gantt({
11280
12509
  "aria-setsize": row.setSize,
11281
12510
  "aria-posinset": row.posInSet,
11282
12511
  "aria-label": nameOf(row),
12512
+ // a plan of days is named on the server too: the same dates, which the server's date data and the browser's can write
12513
+ // a space apart ("5–9 Oct", "5 – 9 Oct"), so hydrating keeps the server's name (Sprint 25.20's review)
12514
+ suppressHydrationWarning: true,
11283
12515
  "data-grid-key": row.key,
11284
12516
  tabIndex: grid.tabIndexOf(row.key),
11285
12517
  onFocus: () => {
@@ -11381,7 +12613,8 @@ function Gantt({
11381
12613
  const legend = /* @__PURE__ */ jsx(Legend, { statuses });
11382
12614
  const legendApart = toolbar === false && hasStatuses;
11383
12615
  const apartRef = useRef(null);
11384
- const apartFill = useFillHeight(apartRef, legendApart);
12616
+ const apartFill = useFillHeight(apartRef, legendApart && !pending);
12617
+ if (pending) return /* @__PURE__ */ jsx("div", { "aria-busy": "true", "data-gantt-pending": "", className: cn(className), style: { minHeight: rows.length * ROW_HEIGHT2 + 96 } });
11385
12618
  const chart = /* @__PURE__ */ jsx(
11386
12619
  TimeViewport,
11387
12620
  {
@@ -12174,6 +13407,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12174
13407
  const tabRow = useHeaderTabRow();
12175
13408
  const narrow = useNarrowRow(tabRow?.row ?? null);
12176
13409
  const inHeader = isHorizontal && tabRow != null;
13410
+ const kindOf2 = (step) => !onStepChange ? "progress" : step.disabled ? "locked" : "open";
12177
13411
  if (inHeader && narrow) {
12178
13412
  const current = steps[currentStep];
12179
13413
  const where = `Step ${Math.min(currentStep + 1, steps.length)} of ${steps.length}`;
@@ -12198,12 +13432,12 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12198
13432
  ] });
12199
13433
  }
12200
13434
  if (inHeader) {
12201
- return /* @__PURE__ */ jsx("nav", { "data-gs": "stepper", "aria-label": "Progress", className: cn("flex items-start mx-ds-1", className), children: steps.map((step, i) => {
13435
+ return /* @__PURE__ */ jsx("nav", { "data-gs": "stepper", "aria-label": "Progress", className: cn("mx-ds-1", className), children: /* @__PURE__ */ jsx("ol", { role: "list", className: STEP_LIST, children: steps.map((step, i) => {
12202
13436
  const status = step.status ?? (i < currentStep ? "complete" : i === currentStep ? "current" : "upcoming");
12203
13437
  const isLast = i === steps.length - 1;
12204
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-start min-w-0", isLast ? "flex-none" : "flex-1"), children: [
13438
+ return /* @__PURE__ */ jsxs("li", { className: cn("flex flex-col items-start min-w-0", isLast ? "flex-none" : "flex-1"), children: [
12205
13439
  /* @__PURE__ */ jsxs("div", { className: "flex items-center w-full", children: [
12206
- /* @__PURE__ */ jsx(StepCircle, { status, index: i, label: step.label, onClick: onStepChange && !step.disabled ? () => onStepChange(i) : void 0, clickable: !!onStepChange && !step.disabled }),
13440
+ /* @__PURE__ */ jsx(StepCircle, { status, index: i, label: step.label, kind: kindOf2(step), onClick: () => onStepChange?.(i) }),
12207
13441
  !isLast && /* @__PURE__ */ jsx(
12208
13442
  "div",
12209
13443
  {
@@ -12220,7 +13454,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12220
13454
  {
12221
13455
  className: cn(
12222
13456
  "text-xs font-medium",
12223
- status === "current" ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-primary)]"
13457
+ status === "current" ? "text-[var(--ds-accent-primary)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
12224
13458
  ),
12225
13459
  children: step.label
12226
13460
  }
@@ -12228,15 +13462,15 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12228
13462
  step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: step.description })
12229
13463
  ] })
12230
13464
  ] }, i);
12231
- }) });
13465
+ }) }) });
12232
13466
  }
12233
13467
  if (!isHorizontal) {
12234
- return /* @__PURE__ */ jsx("nav", { "data-gs": "stepper", "aria-label": "Progress", className: cn("flex flex-col", className), children: steps.map((step, i) => {
13468
+ return /* @__PURE__ */ jsx("nav", { "data-gs": "stepper", "aria-label": "Progress", className, children: /* @__PURE__ */ jsx("ol", { role: "list", className: cn(STEP_LIST, "flex-col items-stretch"), children: steps.map((step, i) => {
12235
13469
  const status = step.status ?? (i < currentStep ? "complete" : i === currentStep ? "current" : "upcoming");
12236
13470
  const isLast = i === steps.length - 1;
12237
- return /* @__PURE__ */ jsxs("div", { className: "flex gap-ds-3", children: [
13471
+ return /* @__PURE__ */ jsxs("li", { className: "flex gap-ds-3", children: [
12238
13472
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
12239
- /* @__PURE__ */ jsx(StepCircle, { status, index: i, label: step.label, onClick: onStepChange && !step.disabled ? () => onStepChange(i) : void 0, clickable: !!onStepChange && !step.disabled }),
13473
+ /* @__PURE__ */ jsx(StepCircle, { status, index: i, label: step.label, kind: kindOf2(step), onClick: () => onStepChange?.(i) }),
12240
13474
  !isLast && /* @__PURE__ */ jsx(
12241
13475
  "div",
12242
13476
  {
@@ -12253,7 +13487,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12253
13487
  {
12254
13488
  className: cn(
12255
13489
  "text-xs font-medium leading-8",
12256
- status === "current" ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-primary)]"
13490
+ status === "current" ? "text-[var(--ds-accent-primary)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
12257
13491
  ),
12258
13492
  children: step.label
12259
13493
  }
@@ -12261,18 +13495,18 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12261
13495
  step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)] -mt-1", children: step.description })
12262
13496
  ] })
12263
13497
  ] }, i);
12264
- }) });
13498
+ }) }) });
12265
13499
  }
12266
13500
  return /* @__PURE__ */ jsx(
12267
13501
  "nav",
12268
13502
  {
12269
13503
  "data-gs": "stepper",
12270
13504
  "aria-label": "Progress",
12271
- className: cn("flex items-start", className),
12272
- children: steps.map((step, i) => {
13505
+ className,
13506
+ children: /* @__PURE__ */ jsx("ol", { role: "list", className: STEP_LIST, children: steps.map((step, i) => {
12273
13507
  const status = step.status ?? (i < currentStep ? "complete" : i === currentStep ? "current" : "upcoming");
12274
13508
  const isLast = i === steps.length - 1;
12275
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center", children: [
13509
+ return /* @__PURE__ */ jsxs("li", { className: "flex flex-1 flex-col items-center", children: [
12276
13510
  /* @__PURE__ */ jsxs("div", { className: "flex items-center w-full", children: [
12277
13511
  /* @__PURE__ */ jsx(
12278
13512
  "div",
@@ -12283,7 +13517,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12283
13517
  )
12284
13518
  }
12285
13519
  ),
12286
- /* @__PURE__ */ jsx(StepCircle, { status, index: i, label: step.label, onClick: onStepChange && !step.disabled ? () => onStepChange(i) : void 0, clickable: !!onStepChange && !step.disabled }),
13520
+ /* @__PURE__ */ jsx(StepCircle, { status, index: i, label: step.label, kind: kindOf2(step), onClick: () => onStepChange?.(i) }),
12287
13521
  /* @__PURE__ */ jsx(
12288
13522
  "div",
12289
13523
  {
@@ -12300,7 +13534,7 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12300
13534
  {
12301
13535
  className: cn(
12302
13536
  "text-xs font-medium",
12303
- status === "current" ? "text-[var(--ds-accent-primary)]" : "text-[var(--ds-text-primary)]"
13537
+ status === "current" ? "text-[var(--ds-accent-primary)]" : kindOf2(step) === "locked" ? "text-[var(--ds-text-muted)]" : "text-[var(--ds-text-primary)]"
12304
13538
  ),
12305
13539
  children: step.label
12306
13540
  }
@@ -12308,33 +13542,41 @@ function Stepper({ steps, currentStep, onStepChange, orientation = "horizontal",
12308
13542
  step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: step.description })
12309
13543
  ] })
12310
13544
  ] }, i);
12311
- })
13545
+ }) })
12312
13546
  }
12313
13547
  );
12314
13548
  }
12315
- function StepCircle({ status, index, label, onClick, clickable }) {
13549
+ var STEP_LIST = "flex items-start list-none m-0 p-0";
13550
+ function StepCircle({ status, index, label, kind, onClick }) {
13551
+ const name = `Step ${index + 1}: ${label}${status === "complete" ? ", completed" : ""}`;
13552
+ const look = cn(
13553
+ "flex items-center justify-center shrink-0 w-8 h-8 rounded-[var(--ds-radius-full)] text-xs font-bold transition-colors",
13554
+ status === "complete" && "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)]",
13555
+ status === "current" && `border-2 ${ACTIVE_RING} text-[var(--ds-accent-primary)] bg-[var(--ds-bg-surface)]`,
13556
+ status === "error" && "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)]",
13557
+ status === "loading" && `border-2 ${ACTIVE_RING} text-[var(--ds-accent-primary)] bg-[var(--ds-bg-surface)] animate-pulse`,
13558
+ status === "upcoming" && "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)] text-[var(--ds-text-muted)] bg-[var(--ds-bg-surface)]"
13559
+ );
13560
+ const glyph = status === "complete" ? /* @__PURE__ */ jsx(Icon, { name: "check", size: 14 }) : status === "error" ? /* @__PURE__ */ jsx(Icon, { name: "warning", size: 14 }) : index + 1;
13561
+ if (kind === "progress") {
13562
+ return /* @__PURE__ */ jsx("span", { role: "img", "aria-label": name, "aria-current": status === "current" ? "step" : void 0, className: look, children: glyph });
13563
+ }
12316
13564
  return /* @__PURE__ */ jsx(
12317
13565
  "button",
12318
13566
  {
12319
13567
  type: "button",
12320
- onClick,
12321
- disabled: !clickable,
12322
- "aria-label": `Step ${index + 1}: ${label}${status === "complete" ? ", completed" : ""}`,
13568
+ onClick: kind === "open" ? onClick : void 0,
13569
+ disabled: kind === "locked",
13570
+ "aria-label": kind === "locked" ? `${name}, cannot start yet` : name,
12323
13571
  "aria-current": status === "current" ? "step" : void 0,
12324
13572
  className: cn(
12325
- "flex items-center justify-center shrink-0 w-8 h-8 rounded-[var(--ds-radius-full)] text-xs font-bold transition-colors",
13573
+ look,
12326
13574
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
12327
- clickable && "cursor-pointer",
12328
- !clickable && "cursor-default",
12329
- status === "complete" && "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)]",
12330
- status === "current" && `border-2 ${ACTIVE_RING} text-[var(--ds-accent-primary)] bg-[var(--ds-bg-surface)]`,
12331
- status === "error" && "bg-[var(--ds-status-error)] text-[var(--ds-text-on-error)]",
12332
- status === "loading" && `border-2 ${ACTIVE_RING} text-[var(--ds-accent-primary)] bg-[var(--ds-bg-surface)] animate-pulse`,
12333
- status === "upcoming" && "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)] text-[var(--ds-text-muted)] bg-[var(--ds-bg-surface)]",
13575
+ kind === "open" ? "cursor-pointer" : "cursor-not-allowed opacity-disabled",
12334
13576
  // a step reached but not done (the user went back) can be gone to: it answers the pointer as the done ones do
12335
- status === "upcoming" && clickable && "hover:border-[var(--ds-accent-primary)] hover:text-[var(--ds-accent-primary)]"
13577
+ status === "upcoming" && kind === "open" && "hover:border-[var(--ds-accent-primary)] hover:text-[var(--ds-accent-primary)]"
12336
13578
  ),
12337
- children: status === "complete" ? /* @__PURE__ */ jsx(Icon, { name: "check", size: 14 }) : status === "error" ? /* @__PURE__ */ jsx(Icon, { name: "warning", size: 14 }) : index + 1
13579
+ children: glyph
12338
13580
  }
12339
13581
  );
12340
13582
  }
@@ -12546,6 +13788,7 @@ var Sidebar = forwardRef(function Sidebar2({
12546
13788
  effectiveDarkMode = false;
12547
13789
  }
12548
13790
  const isDark = effectiveDarkMode ?? variant === "dark";
13791
+ const hydrated = useHydrated();
12549
13792
  const showCollapse = type === "traditional" && !mini && !fill2;
12550
13793
  const isMinimal = type === "minimal";
12551
13794
  const isCompact = type === "compact";
@@ -12620,7 +13863,7 @@ var Sidebar = forwardRef(function Sidebar2({
12620
13863
  children: footer
12621
13864
  }
12622
13865
  ),
12623
- isDark && (() => {
13866
+ isDark && hydrated && (() => {
12624
13867
  const root = document.documentElement;
12625
13868
  const pageIsDark = root.getAttribute("data-mode") === "dark";
12626
13869
  const shadow = root.getAttribute("data-shadow") || "flat";
@@ -13406,7 +14649,7 @@ function fill(entries, slots) {
13406
14649
  }
13407
14650
  function ToastProvider({ children, maxVisible = 3, position = "bottom-right", duration = 5e3, mode, pauseOnHover = true, coalesce = true }) {
13408
14651
  const slots = Math.max(1, Math.floor(Number.isFinite(maxVisible) ? maxVisible : 1));
13409
- const [list, setList] = useState([]);
14652
+ const [list2, setList] = useState([]);
13410
14653
  const entries = useRef([]);
13411
14654
  const isMobile = useBreakpoint("sm");
13412
14655
  const shownDuringRuns = useRef([]);
@@ -13435,7 +14678,7 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13435
14678
  setBottomNavs((n) => n - 1);
13436
14679
  };
13437
14680
  }, []);
13438
- const commit = useCallback((next) => {
14681
+ const commit2 = useCallback((next) => {
13439
14682
  entries.current = fill(next, slots);
13440
14683
  setList(entries.current);
13441
14684
  }, [slots]);
@@ -13444,9 +14687,9 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13444
14687
  const reslotted = entries.current.map((e) => e.shown && ++kept > slots ? { ...e, shown: false } : e);
13445
14688
  const next = fill(reslotted, slots);
13446
14689
  if (next.length === entries.current.length && next.every((e, i) => e === entries.current[i])) return;
13447
- commit(next);
13448
- }, [slots, commit]);
13449
- if (list.every((t) => !t.shown) && (hovered || focusInside)) {
14690
+ commit2(next);
14691
+ }, [slots, commit2]);
14692
+ if (list2.every((t) => !t.shown) && (hovered || focusInside)) {
13450
14693
  setHovered(false);
13451
14694
  setFocusInside(false);
13452
14695
  }
@@ -13463,11 +14706,11 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13463
14706
  }).filter((h) => h.text);
13464
14707
  pendingHeard.current.clear();
13465
14708
  if (!batch.length) return;
13466
- setHeard((list2) => [...list2, ...batch].slice(-5));
14709
+ setHeard((list3) => [...list3, ...batch].slice(-5));
13467
14710
  const ids = new Set(batch.map((h) => h.id));
13468
14711
  const timer = setTimeout(() => {
13469
14712
  clearTimers.current.delete(timer);
13470
- setHeard((list2) => list2.filter((h) => !ids.has(h.id)));
14713
+ setHeard((list3) => list3.filter((h) => !ids.has(h.id)));
13471
14714
  }, HEARD_MS);
13472
14715
  clearTimers.current.add(timer);
13473
14716
  }, 0);
@@ -13487,60 +14730,60 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13487
14730
  }, []);
13488
14731
  const remove = useCallback((id) => {
13489
14732
  leaving(id);
13490
- commit(entries.current.filter((t) => t.id !== id));
13491
- }, [commit, leaving]);
14733
+ commit2(entries.current.filter((t) => t.id !== id));
14734
+ }, [commit2, leaving]);
13492
14735
  const dismissAll = useCallback(() => {
13493
14736
  leaving();
13494
- commit([]);
13495
- }, [commit, leaving]);
14737
+ commit2([]);
14738
+ }, [commit2, leaving]);
13496
14739
  const noteShown = useCallback((id, variant, action, fresh) => {
13497
14740
  if (runs.current.size) shownDuringRuns.current.push({ seq: ++showSeq.current, at: Date.now(), id, variant, action, fresh });
13498
14741
  }, []);
13499
14742
  const update2 = useCallback((id, message, options = {}) => {
13500
- const list2 = entries.current;
13501
- const at = list2.findIndex((t2) => t2.id === id);
14743
+ const list3 = entries.current;
14744
+ const at = list3.findIndex((t2) => t2.id === id);
13502
14745
  if (at === -1) return;
13503
14746
  pressed.current.delete(id);
13504
- const t = list2[at];
13505
- const next = [...list2];
14747
+ const t = list3[at];
14748
+ const next = [...list3];
13506
14749
  next[at] = { ...t, message, variant: options.variant ?? t.variant, duration: options.duration ?? t.duration, action: options.action ?? t.action, actionWindow: options.actionWindow ?? t.actionWindow, key: options.key ?? t.key, version: t.version + 1 };
13507
- commit(next);
14750
+ commit2(next);
13508
14751
  announce(next[at]);
13509
14752
  noteShown(id, next[at].variant, !!next[at].action, false);
13510
- }, [commit, announce, noteShown]);
14753
+ }, [commit2, announce, noteShown]);
13511
14754
  const toast = useCallback((message, options = {}) => {
13512
- const list2 = entries.current;
14755
+ const list3 = entries.current;
13513
14756
  const base = { message, variant: options.variant ?? "default", duration: options.duration ?? duration, action: options.action, actionWindow: options.actionWindow, key: options.key };
13514
14757
  if (options.id) {
13515
- const at = list2.findIndex((t) => t.id === options.id);
14758
+ const at = list3.findIndex((t) => t.id === options.id);
13516
14759
  if (at !== -1) {
13517
- const next = [...list2];
13518
- next[at] = { ...list2[at], ...base, version: list2[at].version + 1 };
14760
+ const next = [...list3];
14761
+ next[at] = { ...list3[at], ...base, version: list3[at].version + 1 };
13519
14762
  pressed.current.delete(options.id);
13520
- commit(next);
14763
+ commit2(next);
13521
14764
  announce(next[at]);
13522
14765
  noteShown(options.id, base.variant, !!base.action, false);
13523
14766
  return options.id;
13524
14767
  }
13525
14768
  }
13526
14769
  if (coalesce && base.key && mergeable(base)) {
13527
- const at = list2.findIndex((t) => t.key === base.key && mergeable(t));
14770
+ const at = list3.findIndex((t) => t.key === base.key && mergeable(t));
13528
14771
  if (at !== -1) {
13529
- const next = [...list2];
13530
- next[at] = { ...list2[at], ...base, version: list2[at].version + 1 };
13531
- commit(next);
14772
+ const next = [...list3];
14773
+ next[at] = { ...list3[at], ...base, version: list3[at].version + 1 };
14774
+ commit2(next);
13532
14775
  announce(next[at]);
13533
- noteShown(list2[at].id, base.variant, !!base.action, false);
13534
- return list2[at].id;
14776
+ noteShown(list3[at].id, base.variant, !!base.action, false);
14777
+ return list3[at].id;
13535
14778
  }
13536
14779
  }
13537
14780
  const id = options.id ?? `toast-${++toastSeq}`;
13538
14781
  const entry = { id, ...base, shown: false, version: 0 };
13539
- commit([...list2, entry]);
14782
+ commit2([...list3, entry]);
13540
14783
  announce(entry);
13541
14784
  noteShown(id, base.variant, !!base.action, true);
13542
14785
  return id;
13543
- }, [duration, coalesce, commit, announce, noteShown]);
14786
+ }, [duration, coalesce, commit2, announce, noteShown]);
13544
14787
  const api = useMemo(() => ({
13545
14788
  toast,
13546
14789
  success: (m, o) => toast(m, { ...o, variant: "success" }),
@@ -13592,7 +14835,7 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13592
14835
  if (!entry?.action) return;
13593
14836
  const run = entry.action.onClick;
13594
14837
  leaving(id);
13595
- commit(entries.current.map((t) => t.id === id ? { ...t, action: void 0, actionWindow: void 0, version: t.version + 1 } : t));
14838
+ commit2(entries.current.map((t) => t.id === id ? { ...t, action: void 0, actionWindow: void 0, version: t.version + 1 } : t));
13596
14839
  pressed.current.add(id);
13597
14840
  const done = () => {
13598
14841
  if (pressed.current.delete(id)) remove(id);
@@ -13610,15 +14853,15 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13610
14853
  }
13611
14854
  if (isThenable2(result)) result.then(done, failed);
13612
14855
  else done();
13613
- }, [commit, leaving, remove]);
14856
+ }, [commit2, leaving, remove]);
13614
14857
  const lapse = useCallback((id) => {
13615
- const list2 = entries.current;
13616
- const at = list2.findIndex((t) => t.id === id);
14858
+ const list3 = entries.current;
14859
+ const at = list3.findIndex((t) => t.id === id);
13617
14860
  if (at === -1) return;
13618
- const next = [...list2];
13619
- next[at] = { ...list2[at], action: void 0, actionWindow: void 0, version: list2[at].version + 1 };
13620
- commit(next);
13621
- }, [commit]);
14861
+ const next = [...list3];
14862
+ next[at] = { ...list3[at], action: void 0, actionWindow: void 0, version: list3[at].version + 1 };
14863
+ commit2(next);
14864
+ }, [commit2]);
13622
14865
  useEffect(() => {
13623
14866
  const onKey = (e) => {
13624
14867
  if (e.key !== "F8" || e.defaultPrevented) return;
@@ -13654,9 +14897,9 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
13654
14897
  }
13655
14898
  main.focus();
13656
14899
  }
13657
- }, [list]);
13658
- const shown = list.filter((t) => t.shown);
13659
- const waiting = list.length - shown.length;
14900
+ }, [list2]);
14901
+ const shown = list2.filter((t) => t.shown);
14902
+ const waiting = list2.length - shown.length;
13660
14903
  const isTop = position.startsWith("top");
13661
14904
  const hasBottomNav = isMobile && bottomNavs > 0;
13662
14905
  return /* @__PURE__ */ jsx(ToastContext.Provider, { value: api, children: /* @__PURE__ */ jsxs(ToastLedgerCtx.Provider, { value: ledger, children: [
@@ -13937,8 +15180,8 @@ function usePageActionsSlot() {
13937
15180
  const [asking, setAsking] = useState(0);
13938
15181
  const [slots, setSlots] = useState([]);
13939
15182
  const register = useCallback((el, size) => {
13940
- setSlots((list) => [...list.filter((s) => s.el !== el), { el, size }]);
13941
- return () => setSlots((list) => list.filter((s) => s.el !== el));
15183
+ setSlots((list2) => [...list2.filter((s) => s.el !== el), { el, size }]);
15184
+ return () => setSlots((list2) => list2.filter((s) => s.el !== el));
13942
15185
  }, []);
13943
15186
  const outerAdd = outer?.add;
13944
15187
  const add = useCallback(() => {
@@ -13961,7 +15204,7 @@ function usePageContainer() {
13961
15204
  }
13962
15205
  function isPageHeaderElement(node) {
13963
15206
  if (!isValidElement(node)) return false;
13964
- const type = node.type;
15207
+ const type = elementType(node.type);
13965
15208
  return !!type && typeof type !== "string" && type[PAGE_HEADER_MARK] === true;
13966
15209
  }
13967
15210
  var OVERLAP_MIN = 24;
@@ -14106,7 +15349,7 @@ var PageContainer = forwardRef(function PageContainer2({
14106
15349
  const placement = block ? "block" : r.outside ? "outside" : "inside";
14107
15350
  const breadcrumbsInsideHeader = snapping && floatingHeader && contained;
14108
15351
  const strip = contained && !breadcrumbsInsideHeader && crumbs ? /* @__PURE__ */ jsx(Breadcrumbs, { variant: "contained", itemVariant: r.breadcrumbStyle, bleed: true, contentMaxWidth: maxWidthValue, separator: r.breadcrumbSeparator, items: headerEl.props.title ? [...crumbs, { label: headerEl.props.title }] : crumbs }) : null;
14109
- const columnClass = (pad3) => cn("w-full", paddingClasses[pad3], centered && "mx-auto");
15352
+ const columnClass = (pad4) => cn("w-full", paddingClasses[pad4], centered && "mx-auto");
14110
15353
  const columnStyle = { maxWidth: maxWidthValue, containerType: "inline-size" };
14111
15354
  const header = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement, containedBreadcrumbs: contained, breadcrumbsInsideHeader, noBottomMargin: block, overlap: overlapActive, setPinnedHeight, reportHeader, requestContentWidth }, children: renderHeader ? renderHeader(headerEl) : headerEl });
14112
15355
  const contentPadding = floatingHeader && shell?.isMobile ? "none" : padding;
@@ -14156,7 +15399,7 @@ function outerHeight(el) {
14156
15399
  return el.getBoundingClientRect().height + (parseFloat(s.marginTop) || 0) + (parseFloat(s.marginBottom) || 0);
14157
15400
  }
14158
15401
  function Breadcrumbs2({ items, onLight, onTint, pill, separator = "chevron" }) {
14159
- const follow = useCrumbFollow();
15402
+ const follow2 = useCrumbFollow();
14160
15403
  if (!items.length) return null;
14161
15404
  const mutedCls = onLight ? "text-[var(--ds-text-on-primary)]/40" : onTint ? "text-[var(--ds-text-muted-on-tint)]" : "text-[var(--ds-text-secondary)]";
14162
15405
  const sepEl = (key) => {
@@ -14179,7 +15422,7 @@ function Breadcrumbs2({ items, onLight, onTint, pill, separator = "chevron" }) {
14179
15422
  "a",
14180
15423
  {
14181
15424
  href: item.href ?? "#",
14182
- onClick: (e) => follow(e, item),
15425
+ onClick: (e) => follow2(e, item),
14183
15426
  className: cn(
14184
15427
  "transition-colors duration-[var(--ds-transition-speed)]",
14185
15428
  onLight ? "text-[var(--ds-text-on-primary)]/60 hover:text-[var(--ds-text-on-primary)]/90" : cn(mutedCls, "hover:text-[var(--ds-text-primary)]"),
@@ -14343,7 +15586,7 @@ var PageHeader = forwardRef(function PageHeader2({
14343
15586
  const contentMaxWidth = contentMaxWidthProp ?? container?.contentMaxWidth;
14344
15587
  const insidePaddedColumn = !!container?.insidePaddedColumn;
14345
15588
  const gutterWidth = insidePaddedColumn ? void 0 : contentMaxWidth;
14346
- const floating = isFloating && !insidePaddedColumn;
15589
+ const floating = isFloating && !insidePaddedColumn && container?.placement !== "block";
14347
15590
  const stickyTop = shell ? "top-0" : "top-[var(--ds-navbar-height)]";
14348
15591
  const snapping = behavior === "snapping";
14349
15592
  const inlineBreadcrumbs = container?.containedBreadcrumbs && !snapping ? void 0 : breadcrumbs;
@@ -14392,7 +15635,7 @@ var PageHeader = forwardRef(function PageHeader2({
14392
15635
  }
14393
15636
  let header;
14394
15637
  if (variant === "minimal") {
14395
- header = /* @__PURE__ */ jsx(MinimalHeader, { title, description, caption, focusBack, badge, breadcrumbs: inlineBreadcrumbs, actions: shownActions, tabs, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
15638
+ header = /* @__PURE__ */ jsx(MinimalHeader, { title, caption, focusBack, badge, breadcrumbs: inlineBreadcrumbs, actions: shownActions, tabs, breadcrumbPill: bcPill, breadcrumbSeparator: bcSep, as: Heading, className, headerRef: floating ? void 0 : ref, isFloating: floating, contentMaxWidth: gutterWidth });
14396
15639
  } else if (variant === "tabs-integrated") {
14397
15640
  header = /* @__PURE__ */ jsx(
14398
15641
  TabsIntegratedHeader,
@@ -14567,7 +15810,6 @@ function DefaultHeader({
14567
15810
  }
14568
15811
  function MinimalHeader({
14569
15812
  title,
14570
- description,
14571
15813
  caption,
14572
15814
  focusBack,
14573
15815
  badge,
@@ -14595,7 +15837,6 @@ function MinimalHeader({
14595
15837
  ] }),
14596
15838
  actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
14597
15839
  ] }),
14598
- description && /* @__PURE__ */ jsx(ClampedText, { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description }),
14599
15840
  tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
14600
15841
  ] }) });
14601
15842
  }
@@ -14887,7 +16128,6 @@ function SnappingHeader({
14887
16128
  ] }),
14888
16129
  actions && /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: "sm", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: actions }) })
14889
16130
  ] }),
14890
- description && /* @__PURE__ */ jsx(ClampedText, { className: "mt-ds-1 text-sm text-[var(--ds-text-secondary)]", children: description }),
14891
16131
  filters && /* @__PURE__ */ jsx(FilterRow, { className: "mt-ds-3", children: filters }),
14892
16132
  expandedContent && /* @__PURE__ */ jsx("div", { className: "mt-ds-3", children: expandedContent }),
14893
16133
  tabs && /* @__PURE__ */ jsx(TabRow, { hasCaption: !!caption, className: "mt-ds-3 -mx-ds-1", children: tabs })
@@ -15022,7 +16262,7 @@ function TabsIntegratedHeader({
15022
16262
  }) {
15023
16263
  const hasBreadcrumbs = !!(breadcrumbs && breadcrumbs.length > 0);
15024
16264
  const inner = floatingContentProps(isFloating, contentMaxWidth);
15025
- return /* @__PURE__ */ jsx("header", { "data-gs": "page-header", ref: headerRef, className: cn(hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6", "pb-ds-4", isFloating && cn(FLOATING_HEADER, "overflow-hidden"), className), children: /* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
16265
+ return /* @__PURE__ */ jsx("header", { "data-gs": "page-header", ref: headerRef, className: cn(hasBreadcrumbs ? "pt-ds-3" : "pt-ds-6", "pb-ds-6", isFloating && cn(FLOATING_HEADER, "overflow-hidden"), className), children: /* @__PURE__ */ jsxs("div", { className: inner.className, style: inner.style, children: [
15026
16266
  focusBack && /* @__PURE__ */ jsx(BackLink, { onClick: focusBack }),
15027
16267
  hasBreadcrumbs && /* @__PURE__ */ jsx(Breadcrumbs2, { items: breadcrumbs, pill: breadcrumbPill, separator: breadcrumbSeparator }),
15028
16268
  /* @__PURE__ */ jsxs("div", { className: cn(
@@ -15457,194 +16697,6 @@ function NavMenuGroup({
15457
16697
  )) })
15458
16698
  ] });
15459
16699
  }
15460
- var DEFAULT_BUTTON_ATTR = "data-dialog-default-button";
15461
- var sizeClasses = {
15462
- // round 16 (the owner's item 5): the small dialog is 448 px, not 384 — a delete confirmation with a typed field was cramped
15463
- sm: "max-w-md",
15464
- md: "max-w-lg",
15465
- lg: "max-w-2xl",
15466
- xl: "max-w-4xl",
15467
- full: "max-w-[calc(100vw-2rem)]"
15468
- };
15469
- function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
15470
- const { marked, mark } = useOverlayMark(open);
15471
- const guard = useDirtyGuard(dirty ?? marked, onCloseProp, onDiscard);
15472
- const onClose = guard.requestClose;
15473
- const titleId = useId();
15474
- const descriptionId = useId();
15475
- const isMobile = useBreakpoint("sm");
15476
- const overlayRef = useRef(null);
15477
- const dialogRef = useRef(null);
15478
- const opener = useRef(null);
15479
- const defaultButtonRef = useRef(null);
15480
- const bodyRef = useScrollbarInset();
15481
- const onCloseRef = useLatest(onClose);
15482
- const onSubmitRef = useLatest(onSubmit);
15483
- const isTopLayer = useOverlayLayer(open);
15484
- useFocusTrap(dialogRef, open, isTopLayer);
15485
- const before = useFocusBeforeOpen(open);
15486
- useEffect(() => {
15487
- if (!open) return;
15488
- const active = before.current;
15489
- opener.current = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
15490
- }, [open, before]);
15491
- useEffect(() => {
15492
- if (!open) return;
15493
- requestAnimationFrame(() => {
15494
- focusInitial(dialogRef.current, { destructive: variant === "destructive" });
15495
- });
15496
- }, [open, variant]);
15497
- useEffect(() => {
15498
- if (!open) return;
15499
- const handler = (e) => {
15500
- if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
15501
- e.preventDefault();
15502
- onCloseRef.current();
15503
- };
15504
- document.addEventListener("keydown", handler);
15505
- return () => {
15506
- document.removeEventListener("keydown", handler);
15507
- };
15508
- }, [open, onCloseRef, isTopLayer]);
15509
- useEffect(() => {
15510
- if (!open) return;
15511
- lockBodyScroll();
15512
- return () => {
15513
- unlockBodyScroll();
15514
- const r = opener.current;
15515
- opener.current = null;
15516
- const active = document.activeElement;
15517
- if (!active || active === document.body || !active.isConnected) r?.restore();
15518
- };
15519
- }, [open]);
15520
- if (!open) return null;
15521
- return createPortal(
15522
- /* @__PURE__ */ jsxs(
15523
- "div",
15524
- {
15525
- ref: overlayRef,
15526
- className: cn("fixed inset-0 z-[var(--ds-z-modal)] flex items-center justify-center", !isMobile && "p-ds-4"),
15527
- children: [
15528
- /* @__PURE__ */ jsx(
15529
- "div",
15530
- {
15531
- className: "fixed inset-0 bg-black/50 [backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))] [-webkit-backdrop-filter:blur(calc(var(--ds-glass-blur)*0.3))]",
15532
- onClick: alert ? void 0 : onClose,
15533
- "aria-hidden": "true"
15534
- }
15535
- ),
15536
- /* @__PURE__ */ jsxs(
15537
- "form",
15538
- {
15539
- onSubmit: (e) => {
15540
- if (e.target !== e.currentTarget) return;
15541
- e.preventDefault();
15542
- e.stopPropagation();
15543
- if (variant === "destructive") return;
15544
- const submitter = e.nativeEvent.submitter;
15545
- const implicit = !submitter || submitter === defaultButtonRef.current;
15546
- if (!implicit && submitter.getAttribute("type") !== "submit") return;
15547
- if (implicit) {
15548
- const explicit = e.currentTarget.querySelector(`button[type="submit"]:not([${DEFAULT_BUTTON_ATTR}])`);
15549
- if (explicit?.disabled) return;
15550
- }
15551
- onSubmitRef.current?.();
15552
- },
15553
- className: "contents",
15554
- children: [
15555
- /* @__PURE__ */ jsx(
15556
- "button",
15557
- {
15558
- ref: defaultButtonRef,
15559
- type: "submit",
15560
- tabIndex: -1,
15561
- "aria-hidden": "true",
15562
- className: "sr-only",
15563
- ...{ [DEFAULT_BUTTON_ATTR]: "" }
15564
- }
15565
- ),
15566
- /* @__PURE__ */ jsxs(
15567
- "div",
15568
- {
15569
- ref: dialogRef,
15570
- role: alert ? "alertdialog" : "dialog",
15571
- ...{ [OVERLAY_ROOT_ATTR]: "" },
15572
- "aria-modal": "true",
15573
- "data-gs": "dialog",
15574
- "aria-labelledby": titleId,
15575
- "aria-describedby": description ? descriptionId : void 0,
15576
- style: { containerType: "inline-size", "--ds-form-max": "none" },
15577
- className: cn(
15578
- // a column: the title row, a body that scrolls when it is taller than the room, and the footer (Sprint 25.14,
15579
- // with A36's full-screen phone dialog: its content ran past the screen with the footer cut off)
15580
- "relative z-10 w-full flex flex-col max-h-full",
15581
- isMobile ? "max-w-none h-full" : sizeClasses[size],
15582
- OVERLAY_SURFACE,
15583
- "text-[var(--ds-text-primary)]",
15584
- GLASS_OVERLAY,
15585
- // full screen on a phone: no edge to draw, so the content sits at the gutter from the screen's edge
15586
- isMobile ? "rounded-none" : "rounded-[var(--ds-radius-xl)] border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
15587
- "overflow-hidden",
15588
- "animate-in fade-in-0 zoom-in-95"
15589
- ),
15590
- children: [
15591
- /* @__PURE__ */ jsxs("div", { className: cn("shrink-0 flex items-start gap-ds-3 pt-ds-6 px-[var(--ds-page-gutter)]", children ? "pb-ds-4" : "pb-ds-6"), children: [
15592
- variant === "destructive" && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center shrink-0 w-10 h-10 rounded-[var(--ds-radius-full)] bg-[var(--ds-status-error-bg)]", children: /* @__PURE__ */ jsx(Icon, { name: "warning", size: 20, className: "text-[var(--ds-status-error)]" }) }),
15593
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
15594
- /* @__PURE__ */ jsx(
15595
- "h2",
15596
- {
15597
- id: titleId,
15598
- className: "font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] leading-none tracking-tight",
15599
- children: title
15600
- }
15601
- ),
15602
- description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description })
15603
- ] }),
15604
- /* @__PURE__ */ jsx(
15605
- "button",
15606
- {
15607
- type: "button",
15608
- onClick: onClose,
15609
- ...{ [OVERLAY_CLOSE_ATTR]: "" },
15610
- className: "shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer",
15611
- "aria-label": "Close",
15612
- children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
15613
- }
15614
- )
15615
- ] }),
15616
- children && /* @__PURE__ */ jsx(
15617
- "div",
15618
- {
15619
- ref: bodyRef,
15620
- className: "min-h-0 flex-[0_1_auto] overflow-y-auto pb-ds-6",
15621
- style: {
15622
- paddingLeft: variant === "destructive" ? "calc(var(--ds-page-gutter) + 2.5rem + var(--ds-space-3))" : "var(--ds-page-gutter)",
15623
- paddingRight: insetLessScrollbar("var(--ds-page-gutter)")
15624
- },
15625
- children: /* @__PURE__ */ jsx(SurfaceBoundary, { children: /* @__PURE__ */ jsx(DrawerFooterCtx, { value: null, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) }) }) })
15626
- }
15627
- ),
15628
- isMobile && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "flex-1 min-h-0" }),
15629
- /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
15630
- footer && /* @__PURE__ */ jsx("div", { className: cn(
15631
- "shrink-0 flex justify-end gap-ds-2 py-ds-4 px-[var(--ds-page-gutter)]",
15632
- "border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
15633
- "bg-[var(--ds-bg-muted)]",
15634
- !isMobile && "rounded-b-[var(--ds-radius-xl)]"
15635
- ), children: footer })
15636
- ]
15637
- }
15638
- )
15639
- ]
15640
- }
15641
- )
15642
- ]
15643
- }
15644
- ),
15645
- container ?? document.body
15646
- );
15647
- }
15648
16700
  function HamburgerButton({ onClick, open, className: cls }) {
15649
16701
  return /* @__PURE__ */ jsx(
15650
16702
  "button",
@@ -17130,7 +18182,7 @@ function useFlowSteps(options) {
17130
18182
  const prev = { ...initialAnswers, ...before.answers };
17131
18183
  const next2 = { ...prev, ...patch };
17132
18184
  const here = Math.max(step, before.current ?? step);
17133
- const ahead = (list) => list.slice(here).map((s) => s.label).join("\0");
18185
+ const ahead = (list2) => list2.slice(here).map((s) => s.label).join("\0");
17134
18186
  const changes = ahead(shownSteps(steps, prev)) !== ahead(shownSteps(steps, next2));
17135
18187
  return { ...before, furthest: changes ? Math.min(before.furthest, here) : before.furthest, answers: next2 };
17136
18188
  });
@@ -17139,10 +18191,12 @@ function useFlowSteps(options) {
17139
18191
  if (key) storeFor(key).focusStep = target;
17140
18192
  go(href(target));
17141
18193
  }, [key, go, href]);
18194
+ const canGoBack = useShellPreferences().canNavigateBack ?? PREFERENCE_HOUSE_DEFAULTS.canNavigateBack;
17142
18195
  const goTo = useCallback((target) => {
17143
18196
  if (!Number.isInteger(target) || target < 1 || target > furthest || target === step) return;
18197
+ if (!canGoBack && target < step) return;
17144
18198
  moveTo(target);
17145
- }, [furthest, step, moveTo]);
18199
+ }, [furthest, step, moveTo, canGoBack]);
17146
18200
  const next = useCallback((patch) => {
17147
18201
  if (!key) return;
17148
18202
  if (patch) setAnswers(patch);
@@ -17151,7 +18205,7 @@ function useFlowSteps(options) {
17151
18205
  update(key, (v) => ({ ...v, furthest: Math.max(v.furthest, step + 1), current: step + 1 }));
17152
18206
  moveTo(step + 1);
17153
18207
  }, [key, setAnswers, steps, answers, shown, step, moveTo]);
17154
- const back = step > 1 ? () => moveTo(step - 1) : void 0;
18208
+ const back = canGoBack && step > 1 ? () => moveTo(step - 1) : void 0;
17155
18209
  const saveAndExit = useCallback(() => {
17156
18210
  if (key && stored) update(key, (v) => ({ ...v, current: step }));
17157
18211
  const left = focusAfterLeaving();
@@ -17187,7 +18241,8 @@ function useFlowSteps(options) {
17187
18241
  disabled: i + 1 > furthest || void 0
17188
18242
  })),
17189
18243
  currentStep: step - 1,
17190
- onStepChange: (index) => goTo(index + 1)
18244
+ // with going back off the list only shows progress: nothing in it can be pressed
18245
+ onStepChange: canGoBack ? (index) => goTo(index + 1) : void 0
17191
18246
  },
17192
18247
  saveAndExit,
17193
18248
  finish
@@ -17559,6 +18614,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
17559
18614
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
17560
18615
  });
17561
18616
 
17562
- export { Accordion, ActionPanel, ActionPanelGroup, AddAnother, Alert, AsidePanel, AvatarGroup, BottomNav, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DangerZone, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, DropdownMenu, EmptyState, EntityCell, FileUpload, FlowSaved, FormWrapper, Gantt, HoverCard, Kbd, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, PageActions, PageContainer, PageHeader, PinInput, Progress, RadioGroup, Rating, RelativeTime, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sheet, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, TIME_ZOOMS, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, TimeBar, TimePicker, TimeRow, TimeScaleHeader, TimeViewport, Timeline, TimezoneSelect, Toast, ToastProvider, Tour, TransferList, TreeView, browserTimeZone, canonicalTimeZone, dateOfDay, dayOf, endDayOf, fitLabel, fitLabels, formatDateRange, formatFullTime, formatRelativeTime, readFlowProgress, timeExtent, timeZoneOffset, useContentWidth, useDestructiveAction, useFittedLabels, useFlowSteps, useRelativeTimeFormat, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
18617
+ export { Accordion, ActionPanel, ActionPanelGroup, AddAnother, Alert, AsidePanel, AvatarGroup, BottomNav, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DangerZone, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, DropdownMenu, EmptyState, EntityCell, FileUpload, FlowSaved, FormWrapper, Gantt, HoverCard, Kbd, Launcher, LocaleProvider, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, PageActions, PageContainer, PageHeader, PinInput, Progress, RadioGroup, Rating, RecurrencePicker, RecurrenceScopeDialog, RelativeTime, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sheet, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, TIME_ZOOMS, Table, TableToolbar, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TagInput, TextOverflow, TimeBar, TimePicker, TimeRow, TimeScaleHeader, TimeViewport, Timeline, TimezoneSelect, Toast, ToastProvider, Tour, TransferList, TreeView, browserTimeZone, canonicalTimeZone, countEdited, dateOfDay, dayOf, deleteOccurrence, describeRecurrence, endDayOf, fitLabel, fitLabels, formatDateRange, nthOf, occurrenceOn, occurrences, parseRRule, readFlowProgress, readRecurrence, recurrencePresets, sameRecurrence, saveOccurrence, timeExtent, timeZoneOffset, toRRule, useBrowserTimeZone, useContentWidth, useDestructiveAction, useFittedLabels, useFlowSteps, useRelativeTimeFormat, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
17563
18618
  //# sourceMappingURL=index.js.map
17564
18619
  //# sourceMappingURL=index.js.map