gridsmith-ui 0.29.1 → 0.30.1

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 (108) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.js +24 -9
  5. package/dist/charts.js.map +1 -1
  6. package/dist/chat.js +7 -7
  7. package/dist/{chunk-MFAWJSMJ.js → chunk-6C2IUMY7.js} +4 -23
  8. package/dist/chunk-6C2IUMY7.js.map +1 -0
  9. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  10. package/dist/chunk-7QLXYRPD.js.map +1 -0
  11. package/dist/{chunk-FD4YED7O.js → chunk-DBHVJ7Z3.js} +5 -49
  12. package/dist/chunk-DBHVJ7Z3.js.map +1 -0
  13. package/dist/chunk-IELIF4PJ.js +110 -0
  14. package/dist/chunk-IELIF4PJ.js.map +1 -0
  15. package/dist/chunk-ISONVOA7.js +316 -0
  16. package/dist/chunk-ISONVOA7.js.map +1 -0
  17. package/dist/{chunk-4TUJXDRU.js → chunk-KMKBOTFR.js} +93 -32
  18. package/dist/chunk-KMKBOTFR.js.map +1 -0
  19. package/dist/{chunk-MKFWHWBK.js → chunk-L6HWACK7.js} +7 -7
  20. package/dist/chunk-L6HWACK7.js.map +1 -0
  21. package/dist/chunk-LJYSVZMI.js +25 -0
  22. package/dist/chunk-LJYSVZMI.js.map +1 -0
  23. package/dist/{chunk-6A5RHW2W.js → chunk-NEUWZUK6.js} +4 -5
  24. package/dist/chunk-NEUWZUK6.js.map +1 -0
  25. package/dist/chunk-NKUCS67T.js +13 -0
  26. package/dist/chunk-NKUCS67T.js.map +1 -0
  27. package/dist/{chunk-RECCCA7Z.js → chunk-OVHBQT3Z.js} +6 -5
  28. package/dist/chunk-OVHBQT3Z.js.map +1 -0
  29. package/dist/{chunk-I3MTQ3CS.js → chunk-OX3NNMWG.js} +6 -6
  30. package/dist/{chunk-I3MTQ3CS.js.map → chunk-OX3NNMWG.js.map} +1 -1
  31. package/dist/{chunk-ZI3OQDET.js → chunk-R5QXMNW7.js} +297 -15
  32. package/dist/chunk-R5QXMNW7.js.map +1 -0
  33. package/dist/chunk-SJZWEBHL.js +58 -0
  34. package/dist/chunk-SJZWEBHL.js.map +1 -0
  35. package/dist/{chunk-FUKSLQ77.js → chunk-SLRTSBCJ.js} +91 -51
  36. package/dist/chunk-SLRTSBCJ.js.map +1 -0
  37. package/dist/chunk-T52LAFKG.js +1350 -0
  38. package/dist/chunk-T52LAFKG.js.map +1 -0
  39. package/dist/chunk-TYNDXMBV.js +105 -0
  40. package/dist/chunk-TYNDXMBV.js.map +1 -0
  41. package/dist/{chunk-37WOC7F7.js → chunk-UDJOIVKQ.js} +31 -113
  42. package/dist/chunk-UDJOIVKQ.js.map +1 -0
  43. package/dist/{chunk-RJEUQ7RL.js → chunk-ULCACQSA.js} +47 -90
  44. package/dist/chunk-ULCACQSA.js.map +1 -0
  45. package/dist/{chunk-HP7WUZPK.js → chunk-WIUGPZOH.js} +10 -3
  46. package/dist/chunk-WIUGPZOH.js.map +1 -0
  47. package/dist/{chunk-JTU7OBYJ.js → chunk-YL5KD5RC.js} +4 -3
  48. package/dist/chunk-YL5KD5RC.js.map +1 -0
  49. package/dist/data-grid.d.ts +25 -6
  50. package/dist/data-grid.js +180 -58
  51. package/dist/data-grid.js.map +1 -1
  52. package/dist/editor-mentions.js +4 -5
  53. package/dist/editor-mentions.js.map +1 -1
  54. package/dist/editor.js +11 -8
  55. package/dist/editor.js.map +1 -1
  56. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  57. package/dist/feather-XFFEFLTV.js.map +1 -0
  58. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  59. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  60. package/dist/icons.d.ts +1 -1
  61. package/dist/icons.js +1 -1
  62. package/dist/index.d.ts +138 -39
  63. package/dist/index.js +846 -1667
  64. package/dist/index.js.map +1 -1
  65. package/dist/kanban.d.ts +26 -8
  66. package/dist/kanban.js +204 -79
  67. package/dist/kanban.js.map +1 -1
  68. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  69. package/dist/lucide-JOORDTO6.js.map +1 -0
  70. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  71. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  72. package/dist/resizable.js +47 -16
  73. package/dist/resizable.js.map +1 -1
  74. package/dist/roots.css +12 -8
  75. package/dist/{table-utils-CxnpFHRF.d.ts → table-utils-BXkP-2Hx.d.ts} +3 -1
  76. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  77. package/dist/tabler-YGETLYRU.js.map +1 -0
  78. package/dist/tokens.css +14 -0
  79. package/dist/utils.js.map +1 -1
  80. package/dist/virtualized.d.ts +6 -1
  81. package/dist/virtualized.js +17 -13
  82. package/dist/virtualized.js.map +1 -1
  83. package/dist-static/tokens.css +14 -0
  84. package/package.json +1 -1
  85. package/dist/chunk-37WOC7F7.js.map +0 -1
  86. package/dist/chunk-4TUJXDRU.js.map +0 -1
  87. package/dist/chunk-6A5RHW2W.js.map +0 -1
  88. package/dist/chunk-FD4YED7O.js.map +0 -1
  89. package/dist/chunk-FUKSLQ77.js.map +0 -1
  90. package/dist/chunk-HP7WUZPK.js.map +0 -1
  91. package/dist/chunk-JTU7OBYJ.js.map +0 -1
  92. package/dist/chunk-MFAWJSMJ.js.map +0 -1
  93. package/dist/chunk-MKFWHWBK.js.map +0 -1
  94. package/dist/chunk-OYNUU346.js.map +0 -1
  95. package/dist/chunk-QM4SJKWV.js +0 -51
  96. package/dist/chunk-QM4SJKWV.js.map +0 -1
  97. package/dist/chunk-QNHVPE2B.js +0 -117
  98. package/dist/chunk-QNHVPE2B.js.map +0 -1
  99. package/dist/chunk-RECCCA7Z.js.map +0 -1
  100. package/dist/chunk-RJEUQ7RL.js.map +0 -1
  101. package/dist/chunk-WH2FUFAC.js +0 -15
  102. package/dist/chunk-WH2FUFAC.js.map +0 -1
  103. package/dist/chunk-ZI3OQDET.js.map +0 -1
  104. package/dist/feather-FNBQ74RR.js.map +0 -1
  105. package/dist/heroicons-DODCSYWD.js.map +0 -1
  106. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  107. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  108. package/dist/tabler-D3VYTSGI.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,171 +1,59 @@
1
1
  "use client";
2
- import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useCornerInset, NAVBAR_INSET_PRIORITY, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, besideRoom, PANEL_INSET_PRIORITY, Drawer, useRememberedWidth, pxOf, ResizeEdge, LAUNCHER_INSET_PRIORITY, defaultMaxWidth } from './chunk-4TUJXDRU.js';
3
- export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-4TUJXDRU.js';
4
- import { Skeleton, TITLE_TEXT, useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-MFAWJSMJ.js';
5
- export { Skeleton, useFormatLocale, useHydrated } from './chunk-MFAWJSMJ.js';
2
+ import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useCornerInset, NAVBAR_INSET_PRIORITY, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, besideRoom, PANEL_INSET_PRIORITY, Drawer, useRememberedWidth, pxOf, ResizeEdge, LAUNCHER_INSET_PRIORITY, defaultMaxWidth } from './chunk-KMKBOTFR.js';
3
+ export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-KMKBOTFR.js';
4
+ import { Skeleton, TITLE_TEXT } from './chunk-6C2IUMY7.js';
5
+ export { Skeleton } from './chunk-6C2IUMY7.js';
6
6
  import { useScrollEdges, EDGE_FADE } from './chunk-3BRQRKDO.js';
7
- import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-6A5RHW2W.js';
8
- export { Avatar } from './chunk-6A5RHW2W.js';
9
- import { Link } from './chunk-I3MTQ3CS.js';
10
- export { CodeBlock, Divider, Link } from './chunk-I3MTQ3CS.js';
11
- import { useFillHeight, FillContext, useKeyboardScroll } from './chunk-FD4YED7O.js';
12
- import { Badge } from './chunk-RECCCA7Z.js';
13
- export { Badge } from './chunk-RECCCA7Z.js';
14
- import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-QNHVPE2B.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-FUKSLQ77.js';
16
- export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-FUKSLQ77.js';
17
- import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-RJEUQ7RL.js';
18
- export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-RJEUQ7RL.js';
19
- import { elementType, formatLocale, localeChoice } from './chunk-OYNUU346.js';
20
- import { useInputModality } from './chunk-7RKC5C7G.js';
7
+ import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-NEUWZUK6.js';
8
+ export { Avatar } from './chunk-NEUWZUK6.js';
9
+ import { Link } from './chunk-OX3NNMWG.js';
10
+ export { CodeBlock, Divider, Link } from './chunk-OX3NNMWG.js';
11
+ import { DropdownMenu } from './chunk-ISONVOA7.js';
12
+ export { DropdownMenu } from './chunk-ISONVOA7.js';
13
+ import { useFillHeight, FillContext } from './chunk-DBHVJ7Z3.js';
14
+ import { useKeyboardScroll } from './chunk-SJZWEBHL.js';
15
+ import { Badge } from './chunk-OVHBQT3Z.js';
16
+ export { Badge } from './chunk-OVHBQT3Z.js';
17
+ import { localeCache, startOfDay, addDays, useWeekStart, useDateWords, localeDayFirst, dayKey, moveDay, addMonths, DateSegmentFields, getMonthGrid, isSameMonth, isSameDay, DatePicker, timeSegments, pad2, inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, Checkbox, CardValue, SortIndicator, startOfWeek, endOfWeek, addMonthsClamped, isExcludedDay, CutCell, CutEmail, stepValue, separatorMovesOn, TIME_SEPARATORS, typeDigit, getEventsForDay, layoutWeek, isWeekend, getWeekDays, isBarEvent, layoutPieces, dayPieces, groupEventsByDay, timeToFraction } from './chunk-T52LAFKG.js';
18
+ export { Checkbox, CheckboxGroup, CheckboxGroupItem, DatePicker, HoverCard, TextOverflow } from './chunk-T52LAFKG.js';
19
+ 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-SLRTSBCJ.js';
20
+ export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-SLRTSBCJ.js';
21
+ import { useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, datedMillis, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-TYNDXMBV.js';
22
+ export { PreferencesContext, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-TYNDXMBV.js';
23
+ import { useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-LJYSVZMI.js';
24
+ export { useFormatLocale, useHydrated } from './chunk-LJYSVZMI.js';
25
+ import { formatLocale, localeChoice } from './chunk-7QLXYRPD.js';
26
+ import { useInputModality, inputModality } from './chunk-7RKC5C7G.js';
21
27
  export { INPUT_MODALITY_ATTR, inputModality, installInputModality, useInputModality } from './chunk-7RKC5C7G.js';
22
- import { useBreakpoint, readBreakpointPx } from './chunk-ZHUVLBCC.js';
23
- export { BreakpointWidthProvider, useBreakpoint } from './chunk-ZHUVLBCC.js';
24
28
  import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-72SDAX54.js';
25
29
  export { useFormContext } from './chunk-72SDAX54.js';
26
- import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-QM4SJKWV.js';
30
+ import { Button, CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, ControlSizeContext, triggerType, SurfaceBoundary, Spinner, useFramesItself, TableCardCtx } from './chunk-ULCACQSA.js';
31
+ export { Button, ButtonSizeContext, ControlSizeContext, Spinner } from './chunk-ULCACQSA.js';
32
+ import { elementType } from './chunk-NKUCS67T.js';
33
+ import { useBreakpoint, readBreakpointPx } from './chunk-ZHUVLBCC.js';
34
+ export { BreakpointWidthProvider, useBreakpoint } from './chunk-ZHUVLBCC.js';
27
35
  import { useRevealFocus } from './chunk-HTXP5BMM.js';
28
36
  export { useRevealFocus } from './chunk-HTXP5BMM.js';
29
- import { FOCUSABLE, usePopupFocus, FIELD, ZOOM_KEYS, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, rovingTabStop, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus } from './chunk-ZI3OQDET.js';
30
- export { useGridKeyboard } from './chunk-ZI3OQDET.js';
31
- import { useTextOverflow, AutoTooltip } from './chunk-37WOC7F7.js';
32
- export { Tooltip, useTextOverflow } from './chunk-37WOC7F7.js';
33
- import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
34
- import { Icon, slotIcon, warnOnce } from './chunk-MKFWHWBK.js';
35
- import { usePosition } from './chunk-HP7WUZPK.js';
36
- import { useLatest } from './chunk-WH2FUFAC.js';
37
- import { cn, FOCUS_RING, GLASS_SURFACE, FOCUS_INSET, PEER_FOCUS_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, GROUP_FOCUS_RING, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, ERROR_FOCUS_RING, FIELD_FOCUS_RING, atLeastFocusRoom, FOCUS_ROOM } from './chunk-JTU7OBYJ.js';
38
- import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, isValidElement, useId, memo, useImperativeHandle, useEffect, Children, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
39
- import { createPortal, flushSync } from 'react-dom';
37
+ import { focusAfterSelfRemoval, FOCUSABLE, useFocusKeptInList, FIELD, ZOOM_KEYS, rovingTabStop, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberOpener, focusInitial, OVERLAY_CLOSE_ATTR, usePopupFocus, CONTROL_ANCESTOR, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus } from './chunk-R5QXMNW7.js';
38
+ export { useGridKeyboard } from './chunk-R5QXMNW7.js';
39
+ import { useTextOverflow } from './chunk-IELIF4PJ.js';
40
+ export { useTextOverflow } from './chunk-IELIF4PJ.js';
41
+ import { AutoTooltip } from './chunk-UDJOIVKQ.js';
42
+ export { Tooltip } from './chunk-UDJOIVKQ.js';
43
+ import { Icon, slotIcon, warnOnce } from './chunk-L6HWACK7.js';
44
+ import { mergeRefs } from './chunk-7Q46JXSK.js';
45
+ import { useLatest, usePosition } from './chunk-WIUGPZOH.js';
46
+ import { cn, FOCUS_RING, GLASS_SURFACE, FOCUS_INSET, PEER_FOCUS_RING, GLASS_BACKDROP, FOCUS_ROOM, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, GROUP_FOCUS_RING, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, GLASS_DROPDOWN, ERROR_FOCUS_RING, FIELD_FOCUS_RING, atLeastFocusRoom } from './chunk-YL5KD5RC.js';
40
47
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
48
+ import { forwardRef, useRef, useId, useEffect, useState, createContext, useCallback, useLayoutEffect, useMemo, useContext, isValidElement, memo, useImperativeHandle, useSyncExternalStore, Children, Fragment as Fragment$1, cloneElement, useReducer } from 'react';
49
+ import { createPortal, flushSync } from 'react-dom';
41
50
 
42
- function isDivider(entry) {
43
- return "type" in entry && entry.type === "divider";
44
- }
45
- function DropdownMenu({ trigger, items, align = "end", className, container }) {
46
- const [open, setOpen] = useState(false);
47
- const [focusedIdx, setFocusedIdx] = useState(-1);
48
- const triggerRef = useRef(null);
49
- const menuRef = useRef(null);
50
- const pos = usePosition(triggerRef, menuRef, open, { side: "bottom", align, offset: 4, flip: true });
51
- const enabledIndices = items.reduce((acc, item, i) => {
52
- if (!isDivider(item) && !item.disabled) acc.push(i);
53
- return acc;
54
- }, []);
55
- const [prevOpen, setPrevOpen] = useState(open);
56
- if (open !== prevOpen) {
57
- setPrevOpen(open);
58
- if (!open) setFocusedIdx(-1);
59
- }
60
- const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: menuRef, close: () => setOpen(false), tabExits: "any" });
61
- useEffect(() => {
62
- if (!open) return;
63
- const onKeyDown = (e) => {
64
- if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
65
- e.preventDefault();
66
- returnFocus();
67
- setOpen(false);
68
- };
69
- const onClick = (e) => {
70
- const target = e.target;
71
- if (triggerRef.current?.contains(target) || menuRef.current?.contains(target)) return;
72
- setOpen(false);
73
- };
74
- document.addEventListener("keydown", onKeyDown);
75
- document.addEventListener("mousedown", onClick);
76
- return () => {
77
- document.removeEventListener("keydown", onKeyDown);
78
- document.removeEventListener("mousedown", onClick);
79
- };
80
- }, [open, isTopLayer, returnFocus]);
81
- useEffect(() => {
82
- if (open && focusedIdx >= 0) {
83
- menuRef.current?.querySelector(`[data-index="${focusedIdx}"]`)?.focus();
84
- }
85
- }, [open, focusedIdx]);
86
- function handleMenuKeyDown(e) {
87
- if (e.key === "ArrowDown") {
88
- e.preventDefault();
89
- const curPos = enabledIndices.indexOf(focusedIdx);
90
- setFocusedIdx(enabledIndices[(curPos + 1) % enabledIndices.length]);
91
- } else if (e.key === "ArrowUp") {
92
- e.preventDefault();
93
- const curPos = enabledIndices.indexOf(focusedIdx);
94
- setFocusedIdx(enabledIndices[(curPos - 1 + enabledIndices.length) % enabledIndices.length]);
95
- }
51
+ function selectAllFor(bar) {
52
+ for (let box = bar?.parentElement; bar && box && box !== document.body; box = box.parentElement) {
53
+ const all = Array.from(box.querySelectorAll("[data-ds-select-all] input"));
54
+ if (all.length) return all.find((el) => bar.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) ?? all[all.length - 1];
96
55
  }
97
- return /* @__PURE__ */ jsxs(Fragment, { children: [
98
- /* @__PURE__ */ jsx(
99
- Slot,
100
- {
101
- ...triggerType(trigger),
102
- "aria-haspopup": "menu",
103
- "aria-expanded": open,
104
- ref: triggerRef,
105
- onClick: () => {
106
- setOpen(!open);
107
- if (!open && enabledIndices.length) setFocusedIdx(enabledIndices[0]);
108
- },
109
- children: trigger
110
- }
111
- ),
112
- open && createPortal(
113
- /* @__PURE__ */ jsx(
114
- "div",
115
- {
116
- ref: menuRef,
117
- role: "menu",
118
- "data-gs": "menu",
119
- onKeyDown: handleMenuKeyDown,
120
- className: cn(
121
- // the items' outline has its 4 px of room at any density (Sprint 30, P3)
122
- "fixed z-[var(--ds-z-popover)] min-w-[180px] p-[max(var(--ds-space-1),4px)]",
123
- "bg-[var(--ds-bg-surface-elevated)]",
124
- GLASS_DROPDOWN,
125
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
126
- "rounded-[var(--ds-radius-md)] overflow-hidden",
127
- className
128
- ),
129
- style: { top: pos.top, left: pos.left },
130
- onClick: (e) => e.stopPropagation(),
131
- children: items.map((item, i) => {
132
- if (isDivider(item)) {
133
- return /* @__PURE__ */ jsx("div", { role: "separator", className: "my-ds-1 h-px bg-[var(--ds-border-default)]" }, i);
134
- }
135
- return /* @__PURE__ */ jsxs(
136
- "button",
137
- {
138
- type: "button",
139
- "data-index": i,
140
- role: "menuitem",
141
- disabled: item.disabled,
142
- tabIndex: focusedIdx === i ? 0 : -1,
143
- onClick: () => {
144
- if (!item.disabled) {
145
- returnFocus();
146
- item.onClick();
147
- setOpen(false);
148
- }
149
- },
150
- className: cn(
151
- "flex items-center gap-ds-2 w-full text-left px-ds-3 py-ds-2 text-sm cursor-pointer",
152
- "transition-colors rounded-[var(--ds-radius-sm)]",
153
- FOCUS_RING,
154
- item.disabled ? "opacity-disabled cursor-not-allowed" : item.variant === "destructive" ? "text-[var(--ds-status-error-text)] hover:bg-[var(--ds-status-error-bg)] focus-visible:bg-[var(--ds-status-error-bg)]" : "text-[var(--ds-text-primary)] hover:bg-[var(--ds-bg-emphasis)] focus-visible:bg-[var(--ds-bg-emphasis)]"
155
- ),
156
- children: [
157
- item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 16, className: "shrink-0" }),
158
- /* @__PURE__ */ jsx("span", { children: item.label })
159
- ]
160
- },
161
- i
162
- );
163
- })
164
- }
165
- ),
166
- container ?? document.body
167
- )
168
- ] });
56
+ return null;
169
57
  }
170
58
  function BulkActionBar({ selectedCount, actions, onClearSelection, className }) {
171
59
  const isMobile = useBreakpoint("sm");
@@ -217,7 +105,11 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
217
105
  "button",
218
106
  {
219
107
  type: "button",
220
- onClick: onClearSelection,
108
+ onClick: (e) => {
109
+ const bar = e.currentTarget.closest('[role="toolbar"]');
110
+ focusAfterSelfRemoval(e.currentTarget, { item: bar, to: selectAllFor(bar) });
111
+ onClearSelection();
112
+ },
221
113
  className: cn("ml-auto text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
222
114
  "aria-label": "Clear selection",
223
115
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
@@ -468,25 +360,6 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
468
360
  return /* @__PURE__ */ jsx(AutoTooltip, { content: selectedLabel, disabled: !labelTruncated || open, children: triggerContent });
469
361
  }
470
362
 
471
- // ../../showcase/src/lib/locale-cache.ts
472
- var caches = /* @__PURE__ */ new Set();
473
- function localeCache(limit) {
474
- const map = /* @__PURE__ */ new Map();
475
- caches.add(map);
476
- return (key, make) => {
477
- const hit = map.get(key);
478
- if (hit !== void 0) {
479
- map.delete(key);
480
- map.set(key, hit);
481
- return hit;
482
- }
483
- const made = make();
484
- map.set(key, made);
485
- if (map.size > limit) map.delete(map.keys().next().value);
486
- return made;
487
- };
488
- }
489
-
490
363
  // ../../showcase/src/lib/time-zones.ts
491
364
  function canonicalTimeZone(zone) {
492
365
  try {
@@ -566,7 +439,6 @@ function TimezoneSelect({ value, onChange, placeholder = "Search time zones\u202
566
439
  return /* @__PURE__ */ jsx(Combobox, { value: stored, onChange, options, placeholder, ...rest });
567
440
  }
568
441
  var LONGEST_WAIT = DAY;
569
- 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"]';
570
442
  var noChange = () => () => {
571
443
  };
572
444
  var inBrowser = () => true;
@@ -635,109 +507,6 @@ function RelativeTimeAt({ at, hours, className }) {
635
507
  mounted ? "browser" : "first"
636
508
  ) });
637
509
  }
638
- var CHECKBOX_SIZES = {
639
- sm: { box: "h-[16px] w-[16px]", glyph: 10, label: "text-xs" },
640
- md: { box: "h-[18px] w-[18px]", glyph: 12, label: "text-sm" }
641
- };
642
- function Checkbox({ checked, onChange: onChangeProp, label, size = "md", disabled = false, indeterminate = false, className, ref, ...wiring }) {
643
- const onChange = useReportedChange(onChangeProp, checked);
644
- const internalRef = useRef(null);
645
- const dims = CHECKBOX_SIZES[size];
646
- const control = useFieldControl(wiring);
647
- const mergedRef = useCallback(
648
- (node) => {
649
- internalRef.current = node;
650
- if (typeof ref === "function") ref(node);
651
- else if (ref) ref.current = node;
652
- },
653
- [ref]
654
- );
655
- useEffect(() => {
656
- if (internalRef.current) internalRef.current.indeterminate = indeterminate;
657
- }, [indeterminate]);
658
- const showDash = indeterminate;
659
- const filled = checked || indeterminate;
660
- return /* @__PURE__ */ jsxs(
661
- "label",
662
- {
663
- className: cn(
664
- "flex items-center gap-ds-2.5 cursor-pointer select-none py-ds-1",
665
- disabled && "opacity-disabled cursor-not-allowed",
666
- className
667
- ),
668
- children: [
669
- /* @__PURE__ */ jsx(
670
- "input",
671
- {
672
- ref: mergedRef,
673
- type: "checkbox",
674
- id: control.id,
675
- "aria-labelledby": wiring["aria-labelledby"],
676
- "aria-describedby": control.describedBy,
677
- "aria-invalid": control.invalid || void 0,
678
- required: control.required || void 0,
679
- checked,
680
- disabled,
681
- onChange: (e) => onChange(e.target.checked),
682
- className: "peer sr-only"
683
- }
684
- ),
685
- /* @__PURE__ */ jsx(
686
- "span",
687
- {
688
- className: cn(
689
- "flex items-center justify-center",
690
- dims.box,
691
- "shrink-0",
692
- "rounded-[calc(var(--ds-radius-sm)*0.6)]",
693
- "border-[2px]",
694
- "transition-all",
695
- PEER_FOCUS_RING,
696
- filled ? cn(CHECK_FILL, "border-[var(--ds-check-border)]") : "bg-[var(--ds-input-bg)] border-[var(--ds-input-border)]",
697
- disabled && "pointer-events-none"
698
- ),
699
- children: /* @__PURE__ */ jsx(
700
- "svg",
701
- {
702
- width: dims.glyph,
703
- height: dims.glyph,
704
- viewBox: "0 0 12 12",
705
- fill: "none",
706
- className: cn(
707
- "text-[var(--ds-check-fg)] transition-opacity",
708
- filled ? "opacity-100" : "opacity-0"
709
- ),
710
- children: showDash ? /* @__PURE__ */ jsx("path", { d: "M3 6H9", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }) : /* @__PURE__ */ jsx("path", { d: "M2.5 6L5 8.5L9.5 3.5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" })
711
- }
712
- )
713
- }
714
- ),
715
- label && /* @__PURE__ */ jsx("span", { className: cn(dims.label, "text-[var(--ds-text-primary)]"), children: label })
716
- ]
717
- }
718
- );
719
- }
720
- var CheckboxGroupContext = createContext(null);
721
- function CheckboxGroup({ value, onChange, children, className }) {
722
- return /* @__PURE__ */ jsx(CheckboxGroupContext, { value: { value, onChange }, children: /* @__PURE__ */ jsx("div", { role: "group", className: cn("flex flex-col gap-ds-2", className), children }) });
723
- }
724
- function CheckboxGroupItem({ value, label, disabled }) {
725
- const ctx = useContext(CheckboxGroupContext);
726
- const resolvedValue = ctx?.value ?? [];
727
- const resolvedOnChange = ctx?.onChange;
728
- const checked = resolvedValue.includes(value);
729
- return /* @__PURE__ */ jsx(
730
- Checkbox,
731
- {
732
- checked,
733
- disabled,
734
- label,
735
- onChange: (c) => {
736
- resolvedOnChange?.(c ? [...resolvedValue, value] : resolvedValue.filter((v) => v !== value));
737
- }
738
- }
739
- );
740
- }
741
510
  function RadioGroup({ value, onChange: onChangeProp, options, orientation = "vertical", className, label, ...wiring }) {
742
511
  const onChange = useReportedChange(onChangeProp, value);
743
512
  const groupRef = useRef(null);
@@ -774,6 +543,7 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
774
543
  ref: groupRef,
775
544
  role: "radiogroup",
776
545
  id: control.id,
546
+ "aria-label": label || control.labelId ? void 0 : control.label,
777
547
  "aria-labelledby": label ? groupId : control.labelId,
778
548
  "aria-describedby": control.describedBy,
779
549
  "aria-invalid": control.invalid || void 0,
@@ -833,400 +603,6 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
833
603
  )
834
604
  ] });
835
605
  }
836
-
837
- // ../../showcase/src/lib/date-grid.ts
838
- function addDays(d, n) {
839
- return new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
840
- }
841
- function addMonthsClamped(d, n) {
842
- const first = new Date(d.getFullYear(), d.getMonth() + n, 1);
843
- const last2 = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
844
- return new Date(first.getFullYear(), first.getMonth(), Math.min(d.getDate(), last2));
845
- }
846
- function dayKey(d) {
847
- return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
848
- }
849
- function moveDay(from, key, shift, weekStartsOn = 0) {
850
- const intoWeek = (from.getDay() - weekStartsOn + 7) % 7;
851
- switch (key) {
852
- case "ArrowLeft":
853
- return addDays(from, -1);
854
- case "ArrowRight":
855
- return addDays(from, 1);
856
- case "ArrowUp":
857
- return addDays(from, -7);
858
- case "ArrowDown":
859
- return addDays(from, 7);
860
- case "Home":
861
- return addDays(from, -intoWeek);
862
- case "End":
863
- return addDays(from, 6 - intoWeek);
864
- case "PageUp":
865
- return addMonthsClamped(from, shift ? -12 : -1);
866
- case "PageDown":
867
- return addMonthsClamped(from, shift ? 12 : 1);
868
- default:
869
- return null;
870
- }
871
- }
872
-
873
- // ../../showcase/src/lib/segments.ts
874
- function stepValue(spec, current, delta) {
875
- if (current === null) return delta > 0 ? spec.min : spec.max;
876
- const span = spec.max - spec.min + 1;
877
- return spec.min + ((current - spec.min + delta) % span + span) % span;
878
- }
879
- function typeDigit(spec, buffer, digit) {
880
- if (!/^\d$/.test(digit)) return { buffer, value: buffer ? Number(buffer) : null, advance: false };
881
- let next = buffer + digit;
882
- if (next.length > spec.maxDigits || Number(next) > spec.max) next = digit;
883
- const n = Number(next);
884
- const value = n >= spec.min && n <= spec.max ? n : null;
885
- const full = next.length >= spec.maxDigits;
886
- const noRoom = n > 0 && n * 10 > spec.max;
887
- return { buffer: next, value, advance: full || noRoom };
888
- }
889
- var pad2 = (n) => String(n).padStart(2, "0");
890
- function timeSegments(use24Hour) {
891
- return [
892
- { key: "hour", label: "Hours", min: use24Hour ? 0 : 1, max: use24Hour ? 23 : 12, maxDigits: 2, placeholder: "--" },
893
- { key: "minute", label: "Minutes", min: 0, max: 59, maxDigits: 2, placeholder: "--" }
894
- ];
895
- }
896
- function dateSegments(dayFirst, daysInMonth = 31) {
897
- const day = { key: "day", label: "Day", min: 1, max: daysInMonth, maxDigits: 2, placeholder: "dd" };
898
- const month = { key: "month", label: "Month", min: 1, max: 12, maxDigits: 2, placeholder: "mm" };
899
- const year = { key: "year", label: "Year", min: 1, max: 9999, maxDigits: 4, placeholder: "yyyy" };
900
- return dayFirst ? [day, month, year] : [month, day, year];
901
- }
902
- var FIRST_DAY_BY_REGION = Object.fromEntries([
903
- ...["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]),
904
- ...["AF", "BH", "DJ", "DZ", "EG", "IQ", "IR", "JO", "KW", "LY", "OM", "QA", "SD", "SY"].map((r) => [r, 6]),
905
- ["MV", 5]
906
- ]);
907
- function weekStartOf(tag) {
908
- if (!tag) return void 0;
909
- try {
910
- const locale = new Intl.Locale(tag);
911
- const info = typeof locale.getWeekInfo === "function" ? locale.getWeekInfo() : locale.weekInfo;
912
- if (info && typeof info.firstDay === "number" && info.firstDay >= 1 && info.firstDay <= 7) return info.firstDay % 7;
913
- const region = locale.region ?? locale.maximize().region;
914
- if (!region) return void 0;
915
- return FIRST_DAY_BY_REGION[region] ?? 1;
916
- } catch {
917
- return void 0;
918
- }
919
- }
920
- var last = null;
921
- function firstDayFor(spoken, page) {
922
- for (const tag of [...spoken, page]) {
923
- const found = weekStartOf(tag);
924
- if (found !== void 0) return found;
925
- }
926
- return 1;
927
- }
928
- function regionWeekStart() {
929
- if (typeof document === "undefined") return 1;
930
- const spoken = typeof navigator === "undefined" ? [] : navigator.languages?.length ? navigator.languages : navigator.language ? [navigator.language] : [];
931
- const page = formatLocale();
932
- const key = `${spoken.join(",")}|${page ?? ""}`;
933
- if (last?.key === key) return last.day;
934
- const day = firstDayFor(spoken, page);
935
- last = { key, day };
936
- return day;
937
- }
938
- function useWeekStart(own, locale) {
939
- const readable = useHydrated();
940
- const choice = useContext(LocaleContext);
941
- if (own !== void 0) return own;
942
- if (locale !== void 0) return weekStartOf(locale) ?? 1;
943
- if (readable) return regionWeekStart();
944
- return choice?.hasHeader ? firstDayFor(choice.spoken, choice.server) : 1;
945
- }
946
-
947
- // ../../showcase/src/components/Calendar/calendar-utils.ts
948
- var formatters = localeCache(512);
949
- function dtfFor(locale, options) {
950
- return formatters(`${locale ?? ""}|${JSON.stringify(options)}`, () => new Intl.DateTimeFormat(locale, options));
951
- }
952
- function isSameDay(a, b) {
953
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
954
- }
955
- function isSameMonth(a, b) {
956
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
957
- }
958
- function isWeekend(d) {
959
- const day = d.getDay();
960
- return day === 0 || day === 6;
961
- }
962
- function startOfDay(d) {
963
- return new Date(d.getFullYear(), d.getMonth(), d.getDate());
964
- }
965
- function startOfWeek(d, weekStartsOn = 0) {
966
- const date = new Date(d);
967
- const day = date.getDay();
968
- const diff = (day - weekStartsOn + 7) % 7;
969
- date.setDate(date.getDate() - diff);
970
- return startOfDay(date);
971
- }
972
- function endOfWeek(d, weekStartsOn = 0) {
973
- const start = startOfWeek(d, weekStartsOn);
974
- const end = new Date(start);
975
- end.setDate(end.getDate() + 6);
976
- end.setHours(23, 59, 59, 999);
977
- return end;
978
- }
979
- function addDays2(d, n) {
980
- const result = new Date(d);
981
- result.setDate(result.getDate() + n);
982
- return result;
983
- }
984
- function addMonths(d, n) {
985
- const result = new Date(d);
986
- const day = result.getDate();
987
- result.setDate(1);
988
- result.setMonth(result.getMonth() + n);
989
- result.setDate(Math.min(day, new Date(result.getFullYear(), result.getMonth() + 1, 0).getDate()));
990
- return result;
991
- }
992
- function getMonthGrid(year, month, weekStartsOn = 0) {
993
- const firstOfMonth = new Date(year, month, 1);
994
- const gridStart = startOfWeek(firstOfMonth, weekStartsOn);
995
- const weeks = [];
996
- const current = new Date(gridStart);
997
- for (let w = 0; w < 6; w++) {
998
- const week = [];
999
- for (let d = 0; d < 7; d++) {
1000
- week.push(new Date(current));
1001
- current.setDate(current.getDate() + 1);
1002
- }
1003
- weeks.push(week);
1004
- }
1005
- return weeks;
1006
- }
1007
- function getWeekDays(date, weekStartsOn = 0) {
1008
- const start = startOfWeek(date, weekStartsOn);
1009
- const days = [];
1010
- for (let i = 0; i < 7; i++) {
1011
- days.push(addDays2(start, i));
1012
- }
1013
- return days;
1014
- }
1015
- var DAY_MS = 864e5;
1016
- function daysBetween(a, b) {
1017
- return Math.round((startOfDay(b).getTime() - startOfDay(a).getTime()) / DAY_MS);
1018
- }
1019
- function eventSpan(e) {
1020
- const first = startOfDay(e.start);
1021
- if (!e.end) return { first, last: first };
1022
- const lastInstant = e.allDay ? e.end : new Date(e.end.getTime() - 1);
1023
- const last2 = startOfDay(lastInstant);
1024
- return { first, last: last2 < first ? first : last2 };
1025
- }
1026
- function isBarEvent(e) {
1027
- if (e.allDay) return true;
1028
- if (!e.end) return false;
1029
- const dayLater = new Date(e.start);
1030
- dayLater.setDate(dayLater.getDate() + 1);
1031
- return e.end.getTime() >= dayLater.getTime();
1032
- }
1033
- function getEventsForDay(events, date) {
1034
- const day = startOfDay(date).getTime();
1035
- return events.filter((e) => {
1036
- const { first, last: last2 } = eventSpan(e);
1037
- return first.getTime() <= day && day <= last2.getTime();
1038
- });
1039
- }
1040
- function groupEventsByDay(events, from, to) {
1041
- const out = /* @__PURE__ */ new Map();
1042
- const lo = startOfDay(from), hi = startOfDay(to);
1043
- for (const e of events) {
1044
- const { first, last: last2 } = eventSpan(e);
1045
- for (let day = first < lo ? lo : first; day <= last2 && day < hi; day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1)) {
1046
- const key = dayKey(day);
1047
- const list2 = out.get(key);
1048
- if (list2) list2.push(e);
1049
- else out.set(key, [e]);
1050
- }
1051
- }
1052
- return out;
1053
- }
1054
- function dayPieces(events, date) {
1055
- const dayStart = startOfDay(date);
1056
- const next = new Date(dayStart.getFullYear(), dayStart.getMonth(), dayStart.getDate() + 1);
1057
- const pieces = [];
1058
- for (const event of events) {
1059
- if (isBarEvent(event)) continue;
1060
- const ownDayEnd = new Date(event.start.getFullYear(), event.start.getMonth(), event.start.getDate() + 1);
1061
- const end = event.end && event.end > event.start ? event.end : new Date(Math.min(event.start.getTime() + 36e5, ownDayEnd.getTime()));
1062
- const start = event.start < dayStart ? dayStart : event.start;
1063
- const stop = end > next ? next : end;
1064
- if (stop > start) pieces.push({ event, start, end: stop });
1065
- }
1066
- return pieces.sort((a, b) => a.start.getTime() - b.start.getTime() || b.end.getTime() - a.end.getTime());
1067
- }
1068
- function endDays(start, end) {
1069
- return end > start ? daysBetween(start, new Date(end.getTime() - 1)) : daysBetween(start, end);
1070
- }
1071
- function makeDateWords(locale) {
1072
- const dtf = (options) => dtfFor(locale, options);
1073
- const twelveHour = () => /^h1[12]$/.test(dtf({ hour: "numeric" }).resolvedOptions().hourCycle ?? "");
1074
- const timeOptions = () => ({ hour: twelveHour() ? "numeric" : "2-digit", minute: "2-digit" });
1075
- const dayOffset = (days) => new Intl.NumberFormat(locale, { signDisplay: "always" }).format(days);
1076
- function monthName2(month, width = "long") {
1077
- return dtf({ month: width }).format(new Date(2026, month, 1));
1078
- }
1079
- function weekdayNames(weekStartsOn, width = "short") {
1080
- const f = dtf({ weekday: width });
1081
- return Array.from({ length: 7 }, (_, i) => f.format(new Date(2026, 0, 4 + weekStartsOn + i)));
1082
- }
1083
- function formatTime2(d) {
1084
- return dtf(timeOptions()).format(d);
1085
- }
1086
- function formatHourLabel(hour) {
1087
- const at = new Date(2026, 0, 5, hour);
1088
- return twelveHour() ? dtf({ hour: "numeric" }).format(at) : dtf({ hour: "2-digit", minute: "2-digit" }).format(at);
1089
- }
1090
- function formatTimeRange(start, end) {
1091
- if (!end) return formatTime2(start);
1092
- const f = dtf(timeOptions());
1093
- if (daysBetween(start, end) === 0) return f.formatRange(start, end);
1094
- const sep = f.formatRangeToParts(start, end).find((p) => p.source === "shared" && p.type === "literal")?.value ?? " \u2013 ";
1095
- const days = endDays(start, end);
1096
- return days === 0 ? `${f.format(start)}${sep}${f.format(end)}` : `${f.format(start)}${sep}${f.format(end)} (${dayOffset(days)})`;
1097
- }
1098
- function speakTimeRange(start, end) {
1099
- if (!end) return formatTime2(start);
1100
- const days = endDays(start, end);
1101
- const to = `${formatTime2(start)} to ${formatTime2(end)}`;
1102
- return days === 0 ? to : days === 1 ? `${to} the next day` : `${to}, ${days} days later`;
1103
- }
1104
- function formatMonthYear(d) {
1105
- return dtf({ month: "long", year: "numeric" }).format(d);
1106
- }
1107
- function formatDayLong(d) {
1108
- return dtf({ weekday: "long", day: "numeric", month: "long", year: "numeric" }).format(d);
1109
- }
1110
- function formatDayShort(d) {
1111
- return dtf({ weekday: "short", day: "numeric", month: "short" }).format(d);
1112
- }
1113
- function formatDayMedium(d) {
1114
- return dtf({ day: "numeric", month: "short", year: "numeric" }).format(d);
1115
- }
1116
- function formatDayRange(start, end) {
1117
- return dtf({ day: "numeric", month: "short", year: "numeric" }).formatRange(startOfDay(start), startOfDay(end));
1118
- }
1119
- function formatMonthRange(start, end) {
1120
- return dtf({ month: "long", year: "numeric" }).formatRange(new Date(start.getFullYear(), start.getMonth(), 1), new Date(end.getFullYear(), end.getMonth(), 1));
1121
- }
1122
- function formatWeekRange(d, weekStartsOn = 0) {
1123
- return dtf({ day: "numeric", month: "short", year: "numeric" }).formatRange(startOfWeek(d, weekStartsOn), startOfDay(endOfWeek(d, weekStartsOn)));
1124
- }
1125
- function formatEventWhen(e, spoken = false) {
1126
- const { first, last: last2 } = eventSpan(e);
1127
- const multiDay = last2 > first;
1128
- if (e.allDay) {
1129
- if (!multiDay) return spoken ? formatDayLong(first) : formatDayShort(first);
1130
- return dtf(spoken ? { weekday: "long", day: "numeric", month: "long" } : { day: "numeric", month: "short" }).formatRange(first, last2);
1131
- }
1132
- if (!e.end || !isBarEvent(e)) {
1133
- const day = spoken ? formatDayLong(e.start) : formatDayShort(e.start);
1134
- const end = e.end && e.end > e.start ? e.end : void 0;
1135
- return `${day}, ${spoken ? speakTimeRange(e.start, end) : formatTimeRange(e.start, end)}`;
1136
- }
1137
- return dtf({ weekday: "short", day: "numeric", month: "short", hour: "numeric", minute: "2-digit" }).formatRange(e.start, e.end);
1138
- }
1139
- return { monthName: monthName2, weekdayNames, formatTime: formatTime2, formatHourLabel, formatTimeRange, speakTimeRange, formatMonthYear, formatDayLong, formatDayShort, formatDayMedium, formatDayRange, formatMonthRange, formatWeekRange, formatEventWhen };
1140
- }
1141
- var wordsByLocale = localeCache(32);
1142
- function dateWords(locale) {
1143
- return wordsByLocale(locale ?? "", () => makeDateWords(locale));
1144
- }
1145
- function useDateWords() {
1146
- return dateWords(useFormatLocale());
1147
- }
1148
- function layoutWeek(events, days, capacity) {
1149
- const weekStart = startOfDay(days[0]);
1150
- const weekEnd = startOfDay(days[days.length - 1]);
1151
- const items = [];
1152
- for (const event of events) {
1153
- const { first, last: last2 } = eventSpan(event);
1154
- if (last2 < weekStart || first > weekEnd) continue;
1155
- items.push({
1156
- event,
1157
- startCol: Math.max(0, daysBetween(weekStart, first)),
1158
- endCol: Math.min(days.length - 1, daysBetween(weekStart, last2)),
1159
- before: first < weekStart,
1160
- after: last2 > weekEnd,
1161
- length: daysBetween(first, last2) + 1
1162
- });
1163
- }
1164
- items.sort((a, b) => {
1165
- const barA = isBarEvent(a.event) || a.length > 1, barB = isBarEvent(b.event) || b.length > 1;
1166
- if (barA !== barB) return barA ? -1 : 1;
1167
- if (barA && a.length !== b.length) return b.length - a.length;
1168
- return a.event.start.getTime() - b.event.start.getTime() || a.event.title.localeCompare(b.event.title);
1169
- });
1170
- const taken = [];
1171
- const laneOf = /* @__PURE__ */ new Map();
1172
- for (const item of items) {
1173
- let lane = 0;
1174
- while (Array.from({ length: item.endCol - item.startCol + 1 }, (_, k) => taken[lane]?.[item.startCol + k]).some(Boolean)) lane++;
1175
- taken[lane] ??= [];
1176
- for (let c = item.startCol; c <= item.endCol; c++) taken[lane][c] = true;
1177
- laneOf.set(item, lane);
1178
- }
1179
- const cap = Math.max(1, capacity);
1180
- const overflow = days.map((_, c) => items.some((it) => laneOf.get(it) >= cap && it.startCol <= c && c <= it.endCol));
1181
- const more = days.map((_, c) => overflow[c] ? items.filter((it) => laneOf.get(it) >= cap - 1 && it.startCol <= c && c <= it.endCol).map((it) => it.event) : []);
1182
- const segments = [];
1183
- for (const item of items) {
1184
- const lane = laneOf.get(item);
1185
- if (lane >= cap) continue;
1186
- if (lane < cap - 1) {
1187
- segments.push({ event: item.event, startCol: item.startCol, span: item.endCol - item.startCol + 1, lane, continuesBefore: item.before, continuesAfter: item.after });
1188
- continue;
1189
- }
1190
- let runStart = -1;
1191
- for (let c = item.startCol; c <= item.endCol + 1; c++) {
1192
- const free = c <= item.endCol && !overflow[c];
1193
- if (free && runStart < 0) runStart = c;
1194
- if (!free && runStart >= 0) {
1195
- segments.push({ event: item.event, startCol: runStart, span: c - runStart, lane, continuesBefore: runStart > item.startCol || item.before, continuesAfter: c - 1 < item.endCol || item.after });
1196
- runStart = -1;
1197
- }
1198
- }
1199
- }
1200
- const lanes = Math.min(cap, taken.length);
1201
- return { segments, more, lanes };
1202
- }
1203
- function layoutPieces(pieces, minMinutes = 0) {
1204
- const out = [];
1205
- let group = [];
1206
- let columnEnds = [];
1207
- let groupEnd = -Infinity;
1208
- const close = () => {
1209
- for (const placed of group) placed.columns = columnEnds.length;
1210
- out.push(...group);
1211
- group = [];
1212
- columnEnds = [];
1213
- };
1214
- for (const piece of pieces) {
1215
- const at = piece.start.getTime();
1216
- const end = Math.max(piece.end.getTime(), at + minMinutes * 6e4);
1217
- if (group.length && at >= groupEnd) close();
1218
- let column = columnEnds.findIndex((e) => e <= at);
1219
- if (column < 0) column = columnEnds.push(0) - 1;
1220
- columnEnds[column] = end;
1221
- groupEnd = Math.max(group.length ? groupEnd : -Infinity, end);
1222
- group.push({ piece, column, columns: 0 });
1223
- }
1224
- if (group.length) close();
1225
- return out;
1226
- }
1227
- function timeToFraction(d) {
1228
- return (d.getHours() * 60 + d.getMinutes()) / (24 * 60);
1229
- }
1230
606
  var CONTROL_HEIGHT2 = {
1231
607
  sm: "h-[var(--ds-control-height-sm)]",
1232
608
  md: "h-[var(--ds-control-height-md)]",
@@ -1237,552 +613,88 @@ var TEXT_SIZE2 = {
1237
613
  md: "text-sm",
1238
614
  lg: "text-base"
1239
615
  };
1240
- var ENGLISH_MONTHS = [
1241
- "january",
1242
- "february",
1243
- "march",
1244
- "april",
1245
- "may",
1246
- "june",
1247
- "july",
1248
- "august",
1249
- "september",
1250
- "october",
1251
- "november",
1252
- "december"
616
+ var DEFAULT_DATE_RANGE_PRESETS = [
617
+ { label: "Today", range: () => {
618
+ const t = startOfDay(/* @__PURE__ */ new Date());
619
+ return { start: t, end: t };
620
+ } },
621
+ { label: "Last 7 days", range: () => {
622
+ const t = startOfDay(/* @__PURE__ */ new Date());
623
+ return { start: addDays(t, -6), end: t };
624
+ } },
625
+ { label: "Last 30 days", range: () => {
626
+ const t = startOfDay(/* @__PURE__ */ new Date());
627
+ return { start: addDays(t, -29), end: t };
628
+ } },
629
+ { label: "This month", range: () => {
630
+ const t = /* @__PURE__ */ new Date();
631
+ return { start: new Date(t.getFullYear(), t.getMonth(), 1), end: startOfDay(t) };
632
+ } },
633
+ { label: "Last month", range: () => {
634
+ const t = /* @__PURE__ */ new Date();
635
+ return { start: new Date(t.getFullYear(), t.getMonth() - 1, 1), end: new Date(t.getFullYear(), t.getMonth(), 0) };
636
+ } }
1253
637
  ];
1254
- function getDaysInMonth(year, month) {
1255
- return new Date(year, month + 1, 0).getDate();
1256
- }
1257
- function getFirstDayOfWeek(year, month, weekStartsOn) {
1258
- return (new Date(year, month, 1).getDay() - weekStartsOn + 7) % 7;
1259
- }
1260
- function isSameDay2(a, b) {
1261
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1262
- }
1263
- function localeDayFirst(locale = formatLocale()) {
1264
- try {
1265
- const parts = new Intl.DateTimeFormat(locale).formatToParts(new Date(2e3, 11, 25));
1266
- return parts.findIndex((p) => p.type === "day") < parts.findIndex((p) => p.type === "month");
1267
- } catch {
1268
- return true;
1269
- }
1270
- }
1271
- var NO_SEGMENTS = { day: null, month: null, year: null };
1272
- function validDate(year, monthIndex2, day) {
1273
- if (monthIndex2 < 0 || monthIndex2 > 11 || day < 1) return null;
1274
- const d = new Date(year, monthIndex2, day);
1275
- return d.getFullYear() === year && d.getMonth() === monthIndex2 && d.getDate() === day ? d : null;
1276
- }
1277
- function monthIndex(name, locale) {
1278
- const typed = name.toLocaleLowerCase().replace(/\.$/, "");
1279
- const fold = (m) => m.toLocaleLowerCase().replace(/\.$/, "");
1280
- const { monthName: monthName2 } = dateWords(locale);
1281
- const lists = [ENGLISH_MONTHS, Array.from({ length: 12 }, (_, i) => fold(monthName2(i, "long"))), Array.from({ length: 12 }, (_, i) => fold(monthName2(i, "short")))];
1282
- for (const list2 of lists) {
1283
- const i = list2.findIndex((m) => m.startsWith(typed) || typed.length >= 3 && typed.startsWith(m));
1284
- if (i >= 0) return i;
1285
- }
1286
- return -1;
1287
- }
1288
- function parseTypedDate(text, dayFirst = localeDayFirst(), locale = formatLocale()) {
1289
- const t = text.trim().replace(/\s+/g, " ");
1290
- if (!t) return null;
1291
- let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(t);
1292
- if (m) return validDate(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
1293
- m = /^(\p{L}{3,})\.?\s+(\d{1,2}),?\s+(\d{4})$/u.exec(t);
1294
- if (m) {
1295
- const mi = monthIndex(m[1], locale);
1296
- return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[2]));
1297
- }
1298
- m = /^(\d{1,2})\.?\s+(\p{L}{3,})\.?,?\s+(\d{4})$/u.exec(t);
1299
- if (m) {
1300
- const mi = monthIndex(m[2], locale);
1301
- return mi < 0 ? null : validDate(Number(m[3]), mi, Number(m[1]));
1302
- }
1303
- m = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{2,4})$/.exec(t);
1304
- if (m) {
1305
- const a = Number(m[1]), b = Number(m[2]);
1306
- const year = m[3].length === 2 ? 2e3 + Number(m[3]) : Number(m[3]);
1307
- const [day, month] = a > 12 ? [a, b] : b > 12 ? [b, a] : dayFirst ? [a, b] : [b, a];
1308
- return validDate(year, month - 1, day);
1309
- }
1310
- return null;
1311
- }
1312
- 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 }) {
638
+ function DateRangePicker({
639
+ value,
640
+ onChange: onChangeProp,
641
+ placeholder = "Pick a date range",
642
+ disabled,
643
+ error = false,
644
+ label,
645
+ id,
646
+ className,
647
+ ref,
648
+ container,
649
+ min,
650
+ max,
651
+ disabledDates,
652
+ presets = DEFAULT_DATE_RANGE_PRESETS,
653
+ weekStartsOn: weekStartsOnProp,
654
+ size: sizeProp,
655
+ ...wiring
656
+ }) {
1313
657
  const weekStartsOn = useWeekStart(weekStartsOnProp);
1314
- const locale = useFormatLocale();
1315
- const { formatDayLong, formatMonthYear, weekdayNames } = useDateWords();
658
+ const { weekdayNames, formatDayLong, formatDayMedium, formatDayRange, formatMonthRange, formatMonthYear } = useDateWords();
1316
659
  const hydrated = useHydrated();
1317
- const today = useToday();
660
+ const now = useToday();
1318
661
  const onChange = useReportedChange(onChangeProp, value);
1319
662
  const contextSize = useContext(ControlSizeContext);
1320
663
  const size = sizeProp ?? contextSize ?? "md";
1321
664
  const [open, setOpen] = useState(false);
665
+ const [viewDate, setViewDate] = useState(() => value.start ?? /* @__PURE__ */ new Date());
1322
666
  const [hover, setHover] = useState(null);
1323
- const [viewDate, setViewDate] = useState(() => value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date());
1324
667
  const triggerRef = useRef(null);
1325
668
  const buttonRef = useRef(null);
1326
669
  const panelRef = useRef(null);
1327
- const gridRef = useRef(null);
670
+ const isPhone = useBreakpoint("sm");
671
+ const presetEdges = useScrollEdges({ enabled: isPhone });
672
+ useRevealFocus(presetEdges.elRef, isPhone);
1328
673
  const generatedId = useId();
1329
674
  const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1330
675
  useInputModality();
1331
- const inputId = control.id ?? generatedId;
1332
- const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
1333
- const year = viewDate.getFullYear();
1334
- const month = viewDate.getMonth();
1335
- const daysInMonth = getDaysInMonth(year, month);
1336
- const firstDay = getFirstDayOfWeek(year, month, weekStartsOn);
1337
- const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
1338
- const isDisabledDay = (d) => !!min && d < startOf(min) || !!max && d > startOf(max);
1339
- const [focusDate, setFocusDate] = useState(() => startOf(value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date()));
1340
- const [prevOpen, setPrevOpen] = useState(open);
1341
- if (open !== prevOpen) {
1342
- setPrevOpen(open);
1343
- if (open) {
1344
- const start = startOf(value ?? range2?.start ?? range2?.end ?? today ?? /* @__PURE__ */ new Date());
1345
- setFocusDate(start);
1346
- setViewDate(new Date(start.getFullYear(), start.getMonth(), 1));
1347
- }
1348
- }
1349
- const showDay = (d) => {
1350
- setFocusDate(d);
1351
- if (d.getFullYear() !== year || d.getMonth() !== month) setViewDate(new Date(d.getFullYear(), d.getMonth(), 1));
1352
- };
1353
- const grid = useGridKeyboard({
1354
- container: gridRef,
1355
- active: focusDate,
1356
- setActive: showDay,
1357
- keyOf: dayKey,
1358
- move: (from, e) => moveDay(from, e.key, e.shiftKey, weekStartsOn)
1359
- });
1360
- const prevMonth = () => {
1361
- setViewDate(new Date(year, month - 1, 1));
1362
- setFocusDate(addMonthsClamped(focusDate, -1));
1363
- };
1364
- const nextMonth = () => {
1365
- setViewDate(new Date(year, month + 1, 1));
1366
- setFocusDate(addMonthsClamped(focusDate, 1));
1367
- };
1368
- const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: buttonRef, panel: panelRef, close: () => setOpen(false), tabExits: "edges" });
1369
- const focusActiveDay = useLatest(grid.focusActive);
1370
- useEffect(() => {
1371
- if (!open) return;
1372
- const raf = requestAnimationFrame(() => focusActiveDay.current());
1373
- return () => cancelAnimationFrame(raf);
1374
- }, [open, focusActiveDay]);
1375
- const choose = (d) => {
1376
- returnFocus();
1377
- onChange(d);
1378
- setOpen(false);
1379
- };
676
+ const triggerId = control.id ?? generatedId;
677
+ const locale = useFormatLocale();
1380
678
  const dayFirst = useMemo(() => localeDayFirst(locale), [locale]);
1381
- const [segs, setSegs] = useState(() => value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
1382
- const [buffers, setBuffers] = useState({});
1383
- const lastCommitted = useRef(null);
1384
- useEffect(() => {
1385
- if (value && value.getTime() === lastCommitted.current) return;
1386
- setSegs(value ? { day: value.getDate(), month: value.getMonth() + 1, year: value.getFullYear() } : { day: null, month: null, year: null });
1387
- setBuffers({});
1388
- }, [value]);
1389
- const specs = dateSegments(dayFirst, hydrated && segs.month && segs.year ? getDaysInMonth(segs.year, segs.month - 1) : 31);
1390
- const segmentRefs = useRef([]);
1391
- const focusSegment = (i) => {
1392
- segmentRefs.current[Math.max(0, Math.min(specs.length - 1, i))]?.focus();
1393
- };
1394
- useEffect(() => {
1395
- if (!open) return;
1396
- const handler = (e) => {
1397
- const t = e.target;
1398
- if (triggerRef.current?.contains(t) || panelRef.current?.contains(t)) return;
1399
- setOpen(false);
1400
- };
1401
- const keyHandler = (e) => {
1402
- if (e.key !== "Escape" || e.defaultPrevented || !isTopLayer()) return;
1403
- e.preventDefault();
1404
- returnFocus();
1405
- setOpen(false);
679
+ const focusPart = useRef(() => {
680
+ });
681
+ const [typedInvalid, setTypedInvalid] = useState(false);
682
+ useLayoutEffect(() => {
683
+ const panel = panelRef.current;
684
+ if (!open || !isPhone || !panel) return;
685
+ const fit = () => {
686
+ const field = triggerRef.current;
687
+ if (field) panel.style.width = `${field.getBoundingClientRect().width}px`;
1406
688
  };
1407
- document.addEventListener("mousedown", handler);
1408
- document.addEventListener("keydown", keyHandler);
689
+ fit();
690
+ window.addEventListener("resize", fit);
1409
691
  return () => {
1410
- document.removeEventListener("mousedown", handler);
1411
- document.removeEventListener("keydown", keyHandler);
692
+ window.removeEventListener("resize", fit);
693
+ panel.style.width = "";
1412
694
  };
1413
- }, [open, isTopLayer, returnFocus]);
1414
- const [typedInvalid, setTypedInvalid] = useState(false);
1415
- const commitIfComplete = (next) => {
1416
- if (next.day === null || next.month === null || next.year === null) {
1417
- setTypedInvalid(false);
1418
- return;
1419
- }
1420
- const d = validDate(next.year, next.month - 1, next.day);
1421
- const ok = !!d && (!min || d >= min) && (!max || d <= max);
1422
- setTypedInvalid(!ok);
1423
- if (d && ok && !(value && isSameDay2(value, d))) {
1424
- lastCommitted.current = d.getTime();
1425
- onChange(d);
1426
- setViewDate(d);
1427
- }
1428
- };
1429
- const setSegment = (key, v, commit2 = true) => {
1430
- setSegs((cur) => {
1431
- const next = { ...cur, [key]: v };
1432
- if (commit2) commitIfComplete(next);
1433
- return next;
1434
- });
1435
- };
1436
- const applyTyped = (text) => {
1437
- const d = parseTypedDate(text, dayFirst, locale);
1438
- if (!d) return;
1439
- const next = { day: d.getDate(), month: d.getMonth() + 1, year: d.getFullYear() };
1440
- setSegs(next);
1441
- setBuffers({});
1442
- commitIfComplete(next);
1443
- };
1444
- const onSegmentKeyDown = (e, index, spec) => {
1445
- const key = e.key;
1446
- if (key === "Tab" || key === "Shift" || key === "Escape" || key.startsWith("F")) return;
1447
- e.preventDefault();
1448
- const k = spec.key;
1449
- const clearBuffer = () => setBuffers((b) => ({ ...b, [k]: "" }));
1450
- if (key === "ArrowLeft") {
1451
- focusSegment(index - 1);
1452
- return;
1453
- }
1454
- if (key === "ArrowRight") {
1455
- focusSegment(index + 1);
1456
- return;
1457
- }
1458
- if (key === "ArrowUp" || key === "ArrowDown") {
1459
- clearBuffer();
1460
- setSegment(k, stepValue(spec, segs[k], key === "ArrowUp" ? 1 : -1));
1461
- return;
1462
- }
1463
- if (key === "Home") {
1464
- clearBuffer();
1465
- setSegment(k, spec.min);
1466
- return;
1467
- }
1468
- if (key === "End") {
1469
- clearBuffer();
1470
- setSegment(k, spec.max);
1471
- return;
1472
- }
1473
- if (key === "Backspace" || key === "Delete") {
1474
- if (segs[k] === null && !buffers[k]) {
1475
- focusSegment(index - 1);
1476
- return;
1477
- }
1478
- clearBuffer();
1479
- setSegment(k, null, false);
1480
- return;
1481
- }
1482
- if (/^\d$/.test(key)) {
1483
- const r = typeDigit(spec, buffers[k] ?? "", key);
1484
- setBuffers((b) => ({ ...b, [k]: r.advance ? "" : r.buffer }));
1485
- setSegment(k, r.value);
1486
- if (r.advance) focusSegment(index + 1);
1487
- }
1488
- };
1489
- const drawn = hydrated ? segs : NO_SEGMENTS;
1490
- const shown = (spec) => {
1491
- const k = spec.key;
1492
- const b = buffers[k];
1493
- if (b && drawn[k] === null) return b;
1494
- if (drawn[k] === null) return spec.placeholder;
1495
- return k === "year" ? String(drawn[k]) : pad2(drawn[k]);
1496
- };
1497
- const selectDay = (day) => {
1498
- const d = new Date(year, month, day);
1499
- if (isDisabledDay(d)) return;
1500
- choose(d);
1501
- };
1502
- const rangeA = range2?.start ?? null;
1503
- const rangeB = range2?.end ?? null;
1504
- const otherEnd = value ? rangeA && !isSameDay2(rangeA, value) ? rangeA : rangeB : rangeA ?? rangeB;
1505
- const thisEnd = value ?? (otherEnd && hover ? hover : null);
1506
- const [bandStart, bandEnd] = otherEnd && thisEnd ? otherEnd <= thisEnd ? [otherEnd, thisEnd] : [thisEnd, otherEnd] : [null, null];
1507
- const inBand = (d) => !!(bandStart && bandEnd && d > bandStart && d < bandEnd);
1508
- const cells = [];
1509
- for (let i = 0; i < firstDay; i++) cells.push(null);
1510
- for (let d = 1; d <= daysInMonth; d++) cells.push(d);
1511
- while (cells.length % 7) cells.push(null);
1512
- const weeks = [];
1513
- for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
1514
- return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1515
- label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1516
- control.requiredNote,
1517
- /* @__PURE__ */ jsxs(
1518
- "div",
1519
- {
1520
- ref: triggerRef,
1521
- "data-date-field": "",
1522
- role: "group",
1523
- id: inputId,
1524
- "aria-label": control.labelId ? void 0 : label ?? "Date",
1525
- "aria-labelledby": control.labelId,
1526
- "aria-describedby": control.describedBy,
1527
- "aria-description": !value ? placeholder : void 0,
1528
- "aria-invalid": control.invalid || typedInvalid || void 0,
1529
- onPaste: (e) => {
1530
- const text = e.clipboardData.getData("text");
1531
- if (text) {
1532
- e.preventDefault();
1533
- applyTyped(text);
1534
- }
1535
- },
1536
- className: cn(
1537
- "flex items-center gap-ds-2 w-full",
1538
- CONTROL_HEIGHT2[size],
1539
- TEXT_SIZE2[size],
1540
- "border-[length:var(--ds-border-width)]",
1541
- control.invalid || typedInvalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1542
- "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
1543
- "shadow-[var(--ds-input-shadow)]",
1544
- "transition-colors",
1545
- "px-ds-3 py-ds-2",
1546
- !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
1547
- open && (control.invalid || typedInvalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
1548
- // the segments mark which part has focus (their tint); the field draws the focus (a hover keeps its border)
1549
- control.invalid || typedInvalid ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
1550
- disabled && "opacity-disabled cursor-not-allowed",
1551
- "text-[var(--ds-text-primary)]"
1552
- ),
1553
- children: [
1554
- /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)] shrink-0" }),
1555
- /* @__PURE__ */ jsx("div", { className: "flex items-center min-w-0 flex-1 tabular-nums", onClick: () => focusSegment(0), children: specs.map((spec, i) => {
1556
- const k = spec.key;
1557
- return /* @__PURE__ */ jsxs("span", { className: "contents", children: [
1558
- i > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "px-[1px] text-[var(--ds-text-muted)]", children: "/" }),
1559
- /* @__PURE__ */ jsx(
1560
- "span",
1561
- {
1562
- ref: (el) => {
1563
- segmentRefs.current[i] = el;
1564
- },
1565
- role: "spinbutton",
1566
- tabIndex: disabled ? -1 : 0,
1567
- "data-segment": spec.key,
1568
- "aria-label": spec.label,
1569
- "aria-valuemin": spec.min,
1570
- "aria-valuemax": spec.max,
1571
- "aria-valuenow": drawn[k] ?? void 0,
1572
- "aria-valuetext": drawn[k] === null ? "empty" : String(drawn[k]),
1573
- onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
1574
- onClick: (e) => e.stopPropagation(),
1575
- className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-ink)] cursor-text", drawn[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
1576
- children: shown(spec)
1577
- }
1578
- )
1579
- ] }, spec.key);
1580
- }) }),
1581
- /* @__PURE__ */ jsx(
1582
- "button",
1583
- {
1584
- ref: buttonRef,
1585
- type: "button",
1586
- "aria-label": "Choose a date",
1587
- "aria-haspopup": "dialog",
1588
- "aria-expanded": open,
1589
- onClick: () => !disabled && setOpen(!open),
1590
- disabled,
1591
- className: cn("shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] disabled:cursor-not-allowed", FOCUS_RING),
1592
- children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
1593
- }
1594
- )
1595
- ]
1596
- }
1597
- ),
1598
- open && createPortal(
1599
- /* @__PURE__ */ jsxs(
1600
- "div",
1601
- {
1602
- ref: panelRef,
1603
- role: "dialog",
1604
- "aria-label": "Choose a date",
1605
- tabIndex: -1,
1606
- className: cn(
1607
- "fixed z-[var(--ds-z-popover)] w-[280px] outline-none",
1608
- "bg-[var(--ds-bg-surface-elevated)]",
1609
- "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1610
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
1611
- "rounded-[var(--ds-radius-md)]",
1612
- "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]"
1613
- ),
1614
- style: { top: pos.top, left: pos.left },
1615
- children: [
1616
- /* @__PURE__ */ jsxs(
1617
- "div",
1618
- {
1619
- className: "flex items-center justify-between bg-[var(--ds-bg-muted)] rounded-t-[var(--ds-radius-md)]",
1620
- style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1621
- children: [
1622
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: prevMonth, "aria-label": "Previous month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14, className: "rotate-180" }) }),
1623
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthYear(viewDate) }),
1624
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: nextMonth, "aria-label": "Next month", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) })
1625
- ]
1626
- }
1627
- ),
1628
- /* @__PURE__ */ jsxs(
1629
- "div",
1630
- {
1631
- ref: gridRef,
1632
- role: "grid",
1633
- "aria-label": formatMonthYear(viewDate),
1634
- onKeyDown: grid.onKeyDown,
1635
- className: "flex flex-col gap-[4px] tabular-nums",
1636
- style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1637
- children: [
1638
- /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px] 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)) }),
1639
- weeks.map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px]", onMouseLeave: () => setHover(null), children: week.map((day, i) => {
1640
- if (day === null) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, `empty-${i}`);
1641
- const dateObj = new Date(year, month, day);
1642
- const isSelected = !!value && isSameDay2(dateObj, value);
1643
- const isOtherEnd = !!otherEnd && isSameDay2(dateObj, otherEnd);
1644
- const isToday = today !== null && isSameDay2(dateObj, today);
1645
- const between = inBand(dateObj);
1646
- const dis = isDisabledDay(dateObj);
1647
- return /* @__PURE__ */ jsx(
1648
- "button",
1649
- {
1650
- type: "button",
1651
- role: "gridcell",
1652
- "data-grid-key": dayKey(dateObj),
1653
- tabIndex: grid.tabIndexOf(dateObj),
1654
- "aria-disabled": dis || void 0,
1655
- onClick: () => selectDay(day),
1656
- onFocus: () => {
1657
- if (!isSameDay2(dateObj, focusDate)) setFocusDate(dateObj);
1658
- },
1659
- onMouseEnter: () => setHover(dateObj),
1660
- "aria-selected": isSelected,
1661
- "aria-current": isToday ? "date" : void 0,
1662
- "aria-label": formatDayLong(dateObj),
1663
- className: cn(
1664
- "w-full aspect-square flex items-center justify-center text-xs",
1665
- "transition-colors cursor-pointer",
1666
- FOCUS_RING,
1667
- 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-ink)] text-[var(--ds-accent-ink)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1668
- dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1669
- ),
1670
- children: day
1671
- },
1672
- day
1673
- );
1674
- }) }, w))
1675
- ]
1676
- }
1677
- ),
1678
- /* @__PURE__ */ jsxs(
1679
- "div",
1680
- {
1681
- className: "flex justify-between items-center bg-[var(--ds-bg-muted)] border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-b-[var(--ds-radius-md)]",
1682
- style: { padding: "var(--ds-space-2) var(--ds-space-3)" },
1683
- children: [
1684
- /* @__PURE__ */ jsx(
1685
- Button,
1686
- {
1687
- type: "button",
1688
- variant: "ghost",
1689
- size: "sm",
1690
- onClick: () => {
1691
- const now = /* @__PURE__ */ new Date();
1692
- setViewDate(now);
1693
- choose(now);
1694
- },
1695
- children: "Today"
1696
- }
1697
- ),
1698
- value && /* @__PURE__ */ jsx(
1699
- Button,
1700
- {
1701
- type: "button",
1702
- variant: "ghost",
1703
- size: "sm",
1704
- onClick: () => choose(null),
1705
- children: "Clear"
1706
- }
1707
- )
1708
- ]
1709
- }
1710
- )
1711
- ]
1712
- }
1713
- ),
1714
- container ?? document.body
1715
- )
1716
- ] });
1717
- }
1718
- var CONTROL_HEIGHT3 = {
1719
- sm: "h-[var(--ds-control-height-sm)]",
1720
- md: "h-[var(--ds-control-height-md)]",
1721
- lg: "h-[var(--ds-control-height-lg)]"
1722
- };
1723
- var TEXT_SIZE3 = {
1724
- sm: "text-xs",
1725
- md: "text-sm",
1726
- lg: "text-base"
1727
- };
1728
- var DEFAULT_DATE_RANGE_PRESETS = [
1729
- { label: "Today", range: () => {
1730
- const t = startOfDay(/* @__PURE__ */ new Date());
1731
- return { start: t, end: t };
1732
- } },
1733
- { label: "Last 7 days", range: () => {
1734
- const t = startOfDay(/* @__PURE__ */ new Date());
1735
- return { start: addDays2(t, -6), end: t };
1736
- } },
1737
- { label: "Last 30 days", range: () => {
1738
- const t = startOfDay(/* @__PURE__ */ new Date());
1739
- return { start: addDays2(t, -29), end: t };
1740
- } },
1741
- { label: "This month", range: () => {
1742
- const t = /* @__PURE__ */ new Date();
1743
- return { start: new Date(t.getFullYear(), t.getMonth(), 1), end: startOfDay(t) };
1744
- } },
1745
- { label: "Last month", range: () => {
1746
- const t = /* @__PURE__ */ new Date();
1747
- return { start: new Date(t.getFullYear(), t.getMonth() - 1, 1), end: new Date(t.getFullYear(), t.getMonth(), 0) };
1748
- } }
1749
- ];
1750
- function DateRangePicker({
1751
- value,
1752
- onChange: onChangeProp,
1753
- placeholder = "Pick a date range",
1754
- disabled,
1755
- error = false,
1756
- label,
1757
- id,
1758
- className,
1759
- ref,
1760
- container,
1761
- min,
1762
- max,
1763
- presets = DEFAULT_DATE_RANGE_PRESETS,
1764
- weekStartsOn: weekStartsOnProp,
1765
- size: sizeProp,
1766
- ...wiring
1767
- }) {
1768
- const weekStartsOn = useWeekStart(weekStartsOnProp);
1769
- const { weekdayNames, formatDayLong, formatDayMedium, formatDayRange, formatMonthRange, formatMonthYear } = useDateWords();
1770
- const hydrated = useHydrated();
1771
- const now = useToday();
1772
- const onChange = useReportedChange(onChangeProp, value);
1773
- const contextSize = useContext(ControlSizeContext);
1774
- const size = sizeProp ?? contextSize ?? "md";
1775
- const [open, setOpen] = useState(false);
1776
- const [viewDate, setViewDate] = useState(() => value.start ?? /* @__PURE__ */ new Date());
1777
- const [hover, setHover] = useState(null);
1778
- const triggerRef = useRef(null);
1779
- const panelRef = useRef(null);
1780
- const generatedId = useId();
1781
- const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1782
- useInputModality();
1783
- const triggerId = control.id ?? generatedId;
1784
- const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
695
+ }, [open, isPhone]);
1785
696
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
697
+ const invalid = control.invalid || typedInvalid;
1786
698
  const today = now && startOfDay(now);
1787
699
  const dayNames = weekdayNames(weekStartsOn, "narrow");
1788
700
  const dayNamesLong = weekdayNames(weekStartsOn, "long");
@@ -1799,8 +711,8 @@ function DateRangePicker({
1799
711
  }
1800
712
  const showDay = (d) => {
1801
713
  setFocusDate(d);
1802
- const monthIndex2 = (x) => x.getFullYear() * 12 + x.getMonth();
1803
- const offset = monthIndex2(d) - monthIndex2(viewDate);
714
+ const monthIndex = (x) => x.getFullYear() * 12 + x.getMonth();
715
+ const offset = monthIndex(d) - monthIndex(viewDate);
1804
716
  if (offset < 0) setViewDate(new Date(d.getFullYear(), d.getMonth(), 1));
1805
717
  else if (offset > 1) setViewDate(new Date(d.getFullYear(), d.getMonth() - 1, 1));
1806
718
  };
@@ -1815,7 +727,7 @@ function DateRangePicker({
1815
727
  setViewDate(addMonths(viewDate, n));
1816
728
  setFocusDate(addMonthsClamped(focusDate, n));
1817
729
  };
1818
- const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: panelRef, close: () => setOpen(false), tabExits: "edges" });
730
+ const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: buttonRef, panel: panelRef, close: () => setOpen(false), tabExits: "edges" });
1819
731
  const focusActiveDay = useLatest(grid.focusActive);
1820
732
  useEffect(() => {
1821
733
  if (!open) return;
@@ -1842,7 +754,20 @@ function DateRangePicker({
1842
754
  document.removeEventListener("keydown", onKey);
1843
755
  };
1844
756
  }, [open, isTopLayer, returnFocus]);
1845
- const isDisabledDay = (d) => min && d < startOfDay(min) || max && d > startOfDay(max) || false;
757
+ const isDisabledDay = (d) => min && d < startOfDay(min) || max && d > startOfDay(max) || isExcludedDay(disabledDates, d);
758
+ const takeTyped = ([s, e], [fs, fe]) => {
759
+ const refused = (d, filled) => filled && (!d || isDisabledDay(d));
760
+ const start = fs ? s : value.start;
761
+ const end = fe ? e : value.end;
762
+ const bad = refused(s, fs) || refused(e, fe) || !!start && !!end && end < start;
763
+ setTypedInvalid(bad);
764
+ if (bad) return;
765
+ const same = (a, b) => a && b ? isSameDay(a, b) : a === b;
766
+ if (!same(start, value.start) || !same(end, value.end)) {
767
+ onChange({ start, end });
768
+ if (start) setViewDate(start);
769
+ }
770
+ };
1846
771
  const selectDay = (d) => {
1847
772
  if (isDisabledDay(d)) return;
1848
773
  if (!value.start || value.start && value.end) {
@@ -1861,44 +786,70 @@ function DateRangePicker({
1861
786
  const rangeStart = value.start;
1862
787
  const rangeEnd2 = value.end ?? previewEnd;
1863
788
  const inRange = (d) => !!(rangeStart && rangeEnd2 && d > rangeStart && d < rangeEnd2);
1864
- const months = [viewDate, addMonths(viewDate, 1)];
789
+ const months = isPhone ? [viewDate] : [viewDate, addMonths(viewDate, 1)];
1865
790
  const summary = !hydrated ? null : value.start && value.end ? formatDayRange(value.start, value.end) : value.start ? `${formatDayMedium(value.start)} \u2013 \u2026` : null;
1866
- const pendingValue = !hydrated && !!value.start;
1867
791
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1868
792
  label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1869
793
  control.requiredNote,
1870
794
  /* @__PURE__ */ jsxs(
1871
- "button",
795
+ "div",
1872
796
  {
1873
- id: triggerId,
1874
797
  ref: triggerRef,
1875
- type: "button",
1876
- onClick: () => !disabled && setOpen((o) => !o),
1877
- disabled,
1878
- "aria-labelledby": labelledBy,
798
+ "data-date-range-field": "",
799
+ role: "group",
800
+ id: triggerId,
801
+ "aria-label": control.labelId ? void 0 : label ?? control.label ?? "Dates",
802
+ "aria-labelledby": control.labelId,
1879
803
  "aria-describedby": control.describedBy,
1880
- "aria-invalid": control.invalid || void 0,
1881
- "aria-haspopup": "dialog",
1882
- "aria-expanded": open,
804
+ "aria-description": !value.start && !value.end ? placeholder : void 0,
805
+ "aria-invalid": invalid || void 0,
1883
806
  className: cn(
1884
807
  "flex items-center gap-ds-2 w-full",
1885
- CONTROL_HEIGHT3[size],
1886
- TEXT_SIZE3[size],
808
+ CONTROL_HEIGHT2[size],
809
+ TEXT_SIZE2[size],
1887
810
  "border-[length:var(--ds-border-width)]",
1888
- control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
811
+ invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1889
812
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
1890
813
  "shadow-[var(--ds-input-shadow)]",
1891
- "transition-colors cursor-pointer",
814
+ "transition-colors",
1892
815
  "px-ds-3 py-ds-2",
1893
- !control.invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
1894
- open && (control.invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
1895
- control.invalid ? ERROR_FOCUS_RING : FIELD_FOCUS_RING,
816
+ !invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
817
+ open && (invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
818
+ // the parts mark which has focus (their tint); the field draws the focus, as DatePicker's does
819
+ invalid ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
1896
820
  disabled && "opacity-disabled cursor-not-allowed",
1897
- summary ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
821
+ "text-[var(--ds-text-primary)]"
1898
822
  ),
1899
823
  children: [
1900
- /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)]" }),
1901
- /* @__PURE__ */ jsx("span", { className: "truncate", children: summary ?? (pendingValue ? "" : placeholder) })
824
+ /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)] shrink-0" }),
825
+ /* @__PURE__ */ jsx("div", { className: "flex items-center min-w-0 flex-1 tabular-nums overflow-hidden", onClick: () => focusPart.current(0), children: /* @__PURE__ */ jsx(
826
+ DateSegmentFields,
827
+ {
828
+ values: [value.start, value.end],
829
+ names: ["Start", "End"],
830
+ dayFirst,
831
+ hydrated,
832
+ disabled,
833
+ focusRef: (focus) => {
834
+ focusPart.current = focus;
835
+ },
836
+ onChange: takeTyped
837
+ }
838
+ ) }),
839
+ /* @__PURE__ */ jsx(AutoTooltip, { content: "Choose a date range", children: /* @__PURE__ */ jsx(
840
+ "button",
841
+ {
842
+ ref: buttonRef,
843
+ type: "button",
844
+ "aria-label": "Choose a date range",
845
+ "aria-haspopup": "dialog",
846
+ "aria-expanded": open,
847
+ onClick: () => !disabled && setOpen((o) => !o),
848
+ disabled,
849
+ className: cn("shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] disabled:cursor-not-allowed", FOCUS_RING),
850
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
851
+ }
852
+ ) })
1902
853
  ]
1903
854
  }
1904
855
  ),
@@ -1912,6 +863,9 @@ function DateRangePicker({
1912
863
  tabIndex: -1,
1913
864
  className: cn(
1914
865
  "fixed z-[var(--ds-z-popover)] flex outline-none",
866
+ // on a phone the panel stays inside the screen: its quick ranges above the month, the month scrolling under
867
+ // them if it must, and its footer, with Clear, always in view (item 35)
868
+ isPhone && "flex-col max-w-[calc(100vw-16px)] max-h-[calc(100dvh-16px)]",
1915
869
  "bg-[var(--ds-bg-surface-elevated)]",
1916
870
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1917
871
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -1920,57 +874,73 @@ function DateRangePicker({
1920
874
  ),
1921
875
  style: { top: pos.top, left: pos.left },
1922
876
  children: [
1923
- 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: () => {
1924
- const r = p.range();
1925
- returnFocus();
1926
- onChange(r);
1927
- setViewDate(r.start ?? /* @__PURE__ */ new Date());
1928
- setOpen(false);
1929
- }, children: p.label }, p.label)) }),
1930
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1931
- /* @__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: [
877
+ presets.length > 0 && /* @__PURE__ */ jsx(
878
+ "div",
879
+ {
880
+ "data-range-presets": "",
881
+ ref: isPhone ? presetEdges.ref : void 0,
882
+ ...isPhone ? presetEdges.fadeProps : {},
883
+ className: cn(
884
+ "border-[var(--ds-border-subtle)]",
885
+ isPhone ? "flex flex-nowrap gap-ds-1.5 overflow-x-auto px-ds-3 py-ds-2.5 scroll-p-[4px] border-b-[length:var(--ds-border-width)] shrink-0 [scrollbar-width:none]" : "p-ds-2 flex flex-col gap-ds-0.5 border-r-[length:var(--ds-border-width)] min-w-[140px]"
886
+ ),
887
+ children: presets.map((p) => /* @__PURE__ */ jsx(Button, { type: "button", variant: isPhone ? "secondary" : "ghost", size: "sm", className: isPhone ? "shrink-0 whitespace-nowrap rounded-[var(--ds-radius-full)]" : "justify-start", onClick: () => {
888
+ const r = p.range();
889
+ returnFocus();
890
+ onChange(r);
891
+ setViewDate(r.start ?? /* @__PURE__ */ new Date());
892
+ setOpen(false);
893
+ }, children: p.label }, p.label))
894
+ }
895
+ ),
896
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", isPhone && "min-h-0"), children: [
897
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between bg-[var(--ds-bg-muted)] px-ds-3 py-ds-2 shrink-0", !isPhone && "rounded-tr-[var(--ds-radius-md)]"), children: [
1932
898
  /* @__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" }) }),
1933
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: formatMonthRange(months[0], months[1]) }),
1934
- /* @__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 }) })
1935
- ] }),
1936
- /* @__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-[4px]", children: [
1937
- /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px] 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)) }),
1938
- getMonthGrid(m.getFullYear(), m.getMonth(), weekStartsOn).map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px]", children: week.map((d) => {
1939
- if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
1940
- const isStart = !!rangeStart && isSameDay(d, rangeStart);
1941
- const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
1942
- const between = inRange(d);
1943
- const dis = isDisabledDay(d);
1944
- return /* @__PURE__ */ jsx(
1945
- "button",
1946
- {
1947
- type: "button",
1948
- role: "gridcell",
1949
- "data-grid-key": dayKey(d),
1950
- tabIndex: grid.tabIndexOf(d),
1951
- "aria-disabled": dis || void 0,
1952
- onClick: () => selectDay(d),
1953
- onFocus: () => {
1954
- if (!isSameDay(d, focusDate)) setFocusDate(d);
899
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: months[1] ? formatMonthRange(months[0], months[1]) : formatMonthYear(months[0]) }),
900
+ /* @__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 }) })
901
+ ] }),
902
+ /* @__PURE__ */ jsx("div", { ref: gridRef, onKeyDown: grid.onKeyDown, className: cn("flex gap-ds-4 px-ds-3 py-ds-2", isPhone && "min-h-0 overflow-y-auto"), onMouseLeave: () => setHover(null), children: months.map((m) => (
903
+ // on a phone the one month fills the panel, its days a comfortable tap (the look check's second round)
904
+ /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": formatMonthYear(m), className: cn(isPhone ? "w-full" : "w-[224px]", "flex flex-col gap-[4px]"), children: [
905
+ /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px] 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)) }),
906
+ getMonthGrid(m.getFullYear(), m.getMonth(), weekStartsOn).map((week, w) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid grid-cols-7 gap-[4px]", children: week.map((d) => {
907
+ if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
908
+ const isStart = !!rangeStart && isSameDay(d, rangeStart);
909
+ const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
910
+ const between = inRange(d);
911
+ const dis = isDisabledDay(d);
912
+ return /* @__PURE__ */ jsx(
913
+ "button",
914
+ {
915
+ type: "button",
916
+ role: "gridcell",
917
+ "data-grid-key": dayKey(d),
918
+ tabIndex: grid.tabIndexOf(d),
919
+ "aria-disabled": dis || void 0,
920
+ onClick: () => selectDay(d),
921
+ onFocus: () => {
922
+ if (!isSameDay(d, focusDate)) setFocusDate(d);
923
+ },
924
+ onMouseEnter: () => setHover(d),
925
+ "aria-selected": isStart || isEnd || between,
926
+ "aria-current": today !== null && isSameDay(d, today) ? "date" : void 0,
927
+ "aria-label": formatDayLong(d),
928
+ className: cn(
929
+ "w-full aspect-square flex items-center justify-center",
930
+ isPhone ? "text-sm" : "text-xs",
931
+ "transition-colors cursor-pointer",
932
+ FOCUS_RING,
933
+ 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-ink)] text-[var(--ds-accent-ink)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
934
+ dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
935
+ ),
936
+ children: d.getDate()
1955
937
  },
1956
- onMouseEnter: () => setHover(d),
1957
- "aria-selected": isStart || isEnd || between,
1958
- "aria-current": today !== null && isSameDay(d, today) ? "date" : void 0,
1959
- "aria-label": formatDayLong(d),
1960
- className: cn(
1961
- "w-full aspect-square flex items-center justify-center text-xs",
1962
- "transition-colors cursor-pointer",
1963
- FOCUS_RING,
1964
- 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-ink)] text-[var(--ds-accent-ink)] font-medium rounded-[var(--ds-radius-sm)]" : "text-[var(--ds-text-secondary)] hover:bg-[var(--ds-bg-emphasis)] rounded-[var(--ds-radius-sm)]",
1965
- dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
1966
- ),
1967
- children: d.getDate()
1968
- },
1969
- d.toISOString()
1970
- );
1971
- }) }, w))
1972
- ] }, `${m.getFullYear()}-${m.getMonth()}`)) }),
1973
- /* @__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: [
938
+ d.toISOString()
939
+ );
940
+ }) }, w))
941
+ ] }, `${m.getFullYear()}-${m.getMonth()}`)
942
+ )) }),
943
+ /* @__PURE__ */ jsxs("div", { className: cn("flex justify-between items-center bg-[var(--ds-bg-muted)] border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] px-ds-3 py-ds-2 shrink-0", isPhone ? "rounded-b-[var(--ds-radius-md)]" : "rounded-br-[var(--ds-radius-md)]"), children: [
1974
944
  /* @__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" }),
1975
945
  (value.start || value.end) && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
1976
946
  onChange({ start: null, end: null });
@@ -2103,10 +1073,10 @@ var atDay = (year, month, day, time) => new Date(year, month, day, time.getHours
2103
1073
  var dayIndex = (d) => Math.round(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / 864e5);
2104
1074
  var isMidnight = (d) => d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0 && d.getMilliseconds() === 0;
2105
1075
  function nthWeekdayOf(year, month, week, weekday2) {
2106
- const last2 = daysIn(year, month);
2107
- if (week === -1) return last2 - (new Date(year, month, last2).getDay() - weekday2 + 7) % 7;
1076
+ const last = daysIn(year, month);
1077
+ if (week === -1) return last - (new Date(year, month, last).getDay() - weekday2 + 7) % 7;
2108
1078
  const day = 1 + (weekday2 - new Date(year, month, 1).getDay() + 7) % 7 + (week - 1) * 7;
2109
- return day <= last2 ? day : null;
1079
+ return day <= last ? day : null;
2110
1080
  }
2111
1081
  function nthOf(date) {
2112
1082
  const d = date.getDate();
@@ -2179,8 +1149,8 @@ function occurrenceAt(event, start) {
2179
1149
  }
2180
1150
  function stopOf(event) {
2181
1151
  if (event.allDay) {
2182
- const last2 = event.end ?? event.start;
2183
- return new Date(last2.getFullYear(), last2.getMonth(), last2.getDate() + 1);
1152
+ const last = event.end ?? event.start;
1153
+ return new Date(last.getFullYear(), last.getMonth(), last.getDate() + 1);
2184
1154
  }
2185
1155
  return event.end ?? new Date(event.start.getTime() + 36e5);
2186
1156
  }
@@ -2746,8 +1716,8 @@ var sizeClasses = {
2746
1716
  full: "max-w-[calc(100vw-2rem)]"
2747
1717
  };
2748
1718
  function Dialog({ open, onClose: onCloseProp, title, description, children, footer, variant = "default", size = "md", alert = false, container, onSubmit, dirty, onDiscard }) {
2749
- const { marked, mark } = useOverlayMark(open);
2750
- const guard = useDirtyGuard(dirty ?? marked, onCloseProp, onDiscard);
1719
+ const { marked, pending, mark } = useOverlayMark(open);
1720
+ const guard = useDirtyGuard((dirty ?? marked) || pending, onCloseProp, onDiscard);
2751
1721
  const onClose = guard.requestClose;
2752
1722
  const titleId = useId();
2753
1723
  const descriptionId = useId();
@@ -2756,7 +1726,13 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
2756
1726
  const dialogRef = useRef(null);
2757
1727
  const opener = useRef(null);
2758
1728
  const defaultButtonRef = useRef(null);
2759
- const bodyRef = useScrollbarInset();
1729
+ const bodyInset = useScrollbarInset();
1730
+ const [bodyEl, setBodyEl] = useState(null);
1731
+ const bodyRef = useCallback((el) => {
1732
+ bodyInset(el);
1733
+ setBodyEl(el);
1734
+ }, [bodyInset]);
1735
+ useKeyboardScroll(bodyEl, title, open);
2760
1736
  const onCloseRef = useLatest(onClose);
2761
1737
  const onSubmitRef = useLatest(onSubmit);
2762
1738
  const isTopLayer = useOverlayLayer(open);
@@ -2764,8 +1740,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
2764
1740
  const before = useFocusBeforeOpen(open);
2765
1741
  useEffect(() => {
2766
1742
  if (!open) return;
2767
- const active = before.current;
2768
- opener.current = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
1743
+ opener.current = rememberOpener(before.current, dialogRef.current);
2769
1744
  }, [open, before]);
2770
1745
  useEffect(() => {
2771
1746
  if (!open) return;
@@ -2880,7 +1855,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
2880
1855
  ),
2881
1856
  description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description })
2882
1857
  ] }),
2883
- /* @__PURE__ */ jsx(
1858
+ /* @__PURE__ */ jsx(AutoTooltip, { content: "Close", children: /* @__PURE__ */ jsx(
2884
1859
  "button",
2885
1860
  {
2886
1861
  type: "button",
@@ -2890,13 +1865,13 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
2890
1865
  "aria-label": "Close",
2891
1866
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 18 })
2892
1867
  }
2893
- )
1868
+ ) })
2894
1869
  ] }),
2895
1870
  children && /* @__PURE__ */ jsx(
2896
1871
  "div",
2897
1872
  {
2898
1873
  ref: bodyRef,
2899
- className: "min-h-0 flex-[0_1_auto] overflow-y-auto pb-ds-6",
1874
+ className: cn("min-h-0 flex-[0_1_auto] overflow-y-auto pb-ds-6", FOCUS_INSET),
2900
1875
  style: {
2901
1876
  paddingLeft: variant === "destructive" ? "calc(var(--ds-page-gutter) + 2.5rem + var(--ds-space-3))" : "var(--ds-page-gutter)",
2902
1877
  paddingRight: insetLessScrollbar("var(--ds-page-gutter)")
@@ -3112,7 +2087,7 @@ var running = (item) => item.status === "queued" || item.status === "uploading";
3112
2087
  var failureOf = (err) => err instanceof Error && err.message ? err.message : "The upload didn't work.";
3113
2088
  var percentOf = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : 0;
3114
2089
  var STILL_UPLOADING = "Wait for your files to finish uploading";
3115
- function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accept, multiple = true, maxSize, className, error = false, label, id }) {
2090
+ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accept, multiple = true, maxSize, className, error = false, label, id, "aria-label": ariaLabel }) {
3116
2091
  const controlled = files !== void 0;
3117
2092
  const [own, setOwn] = useState([]);
3118
2093
  const shown = controlled ? files : own;
@@ -3248,10 +2223,10 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3248
2223
  focusAfterRemoval(rows, gone.index, inputRef.current);
3249
2224
  return;
3250
2225
  }
3251
- const last2 = lastAction.current;
2226
+ const last = lastAction.current;
3252
2227
  const active = document.activeElement;
3253
- if (!last2 || last2.el.isConnected || active && active !== document.body) return;
3254
- const row = Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).find((li) => li.dataset.uploadId === last2.id);
2228
+ if (!last || last.el.isConnected || active && active !== document.body) return;
2229
+ const row = Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).find((li) => li.dataset.uploadId === last.id);
3255
2230
  row?.querySelector("button")?.focus();
3256
2231
  }, [shown]);
3257
2232
  const addFiles = useCallback(
@@ -3343,6 +2318,7 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3343
2318
  accept,
3344
2319
  multiple,
3345
2320
  className: "sr-only",
2321
+ "aria-label": field ? void 0 : ariaLabel,
3346
2322
  "aria-labelledby": field ? field.labelId : void 0,
3347
2323
  "aria-describedby": describedBy,
3348
2324
  "aria-invalid": invalid || void 0,
@@ -3566,12 +2542,12 @@ function fromValue(v, use24Hour) {
3566
2542
  const h24 = Number(m[1]);
3567
2543
  return use24Hour ? { hour: h24, minute: Number(m[2]), period: null } : { hour: h24 % 12 === 0 ? 12 : h24 % 12, minute: Number(m[2]), period: h24 >= 12 ? "PM" : "AM" };
3568
2544
  }
3569
- var CONTROL_HEIGHT4 = {
2545
+ var CONTROL_HEIGHT3 = {
3570
2546
  sm: "h-[var(--ds-control-height-sm)]",
3571
2547
  md: "h-[var(--ds-control-height-md)]",
3572
2548
  lg: "h-[var(--ds-control-height-lg)]"
3573
2549
  };
3574
- var TEXT_SIZE4 = {
2550
+ var TEXT_SIZE3 = {
3575
2551
  sm: "text-xs",
3576
2552
  md: "text-sm",
3577
2553
  lg: "text-base"
@@ -3664,7 +2640,7 @@ function TimePicker({
3664
2640
  if (!open) return;
3665
2641
  const id2 = requestAnimationFrame(() => {
3666
2642
  const panel = panelRef.current;
3667
- (panel?.querySelector('[data-selected="true"]') ?? panel?.querySelector("button"))?.focus();
2643
+ (panel?.querySelector('[aria-selected="true"]') ?? panel?.querySelector("button"))?.focus();
3668
2644
  });
3669
2645
  return () => cancelAnimationFrame(id2);
3670
2646
  }, [open]);
@@ -3680,7 +2656,7 @@ function TimePicker({
3680
2656
  const columns2 = Array.from(panelRef.current?.querySelectorAll("[data-column]") ?? []);
3681
2657
  const idx = columns2.indexOf(column);
3682
2658
  const target = columns2[idx + (e.key === "ArrowRight" ? 1 : -1)];
3683
- next = target?.querySelector('[data-selected="true"]') ?? target?.querySelector("button") ?? null;
2659
+ next = target?.querySelector('[aria-selected="true"]') ?? target?.querySelector("button") ?? null;
3684
2660
  } else return;
3685
2661
  if (next instanceof HTMLElement) {
3686
2662
  e.preventDefault();
@@ -3804,17 +2780,34 @@ function TimePicker({
3804
2780
  return;
3805
2781
  }
3806
2782
  if (/^\d$/.test(key)) {
3807
- const r = typeDigit(spec, buffers[k] ?? "", key);
3808
- setBuffers((b) => ({ ...b, [k]: r.advance ? "" : r.buffer }));
3809
- setSegment(k, r.value);
3810
- if (r.advance) focusSegment(index + 1);
2783
+ typeInto(index, key);
3811
2784
  return;
3812
2785
  }
3813
2786
  if (!use24Hour && /^[aApP]$/.test(key)) {
3814
2787
  setSegment("period", /^[aA]$/.test(key) ? "AM" : "PM");
3815
2788
  return;
3816
2789
  }
2790
+ if (separatorMovesOn(TIME_SEPARATORS, key, !!buffers[k] || segs[k] !== null)) focusSegment(index + 1);
3817
2791
  };
2792
+ const typeInto = (index, digit) => {
2793
+ const spec = specs[index];
2794
+ const k = spec.key;
2795
+ const r = typeDigit(spec, buffers[k] ?? "", digit);
2796
+ if (r.kind === "here") {
2797
+ setBuffers((b) => ({ ...b, [k]: r.buffer }));
2798
+ setSegment(k, r.value);
2799
+ return;
2800
+ }
2801
+ const nextSpec = specs[index + 1];
2802
+ if (!nextSpec) return;
2803
+ focusSegment(index + 1);
2804
+ const carried = typeDigit(nextSpec, "", digit);
2805
+ if (carried.kind !== "here") return;
2806
+ const nk = nextSpec.key;
2807
+ setBuffers((b) => ({ ...b, [nk]: carried.buffer }));
2808
+ setSegment(nk, carried.value);
2809
+ };
2810
+ const freshSegment = (k) => setBuffers((b) => b[k] ? { ...b, [k]: "" } : b);
3818
2811
  const shown = (k, spec) => {
3819
2812
  const b = buffers[k];
3820
2813
  if (b && segs[k] === null) return b;
@@ -3829,7 +2822,7 @@ function TimePicker({
3829
2822
  "data-time-field": "",
3830
2823
  role: "group",
3831
2824
  id: control.id,
3832
- "aria-label": control.labelId ? void 0 : "Time",
2825
+ "aria-label": control.labelId ? void 0 : control.label ?? "Time",
3833
2826
  "aria-labelledby": control.labelId,
3834
2827
  "aria-describedby": control.describedBy,
3835
2828
  "aria-invalid": control.invalid || void 0,
@@ -3842,8 +2835,8 @@ function TimePicker({
3842
2835
  },
3843
2836
  className: cn(
3844
2837
  "flex w-full items-center gap-ds-2",
3845
- CONTROL_HEIGHT4[size],
3846
- TEXT_SIZE4[size],
2838
+ CONTROL_HEIGHT3[size],
2839
+ TEXT_SIZE3[size],
3847
2840
  "border-[length:var(--ds-border-width)]",
3848
2841
  control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
3849
2842
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
@@ -3879,6 +2872,8 @@ function TimePicker({
3879
2872
  "aria-valuenow": segs[k] ?? void 0,
3880
2873
  "aria-valuetext": segs[k] === null ? "empty" : pad2(segs[k]),
3881
2874
  onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
2875
+ onFocus: () => freshSegment(k),
2876
+ onMouseDown: () => freshSegment(k),
3882
2877
  onClick: (e) => e.stopPropagation(),
3883
2878
  className: cn("rounded-[var(--ds-radius-sm)] px-[2px] outline-none focus:bg-[var(--ds-accent-subtle)] focus:text-[var(--ds-accent-ink)] cursor-text", segs[k] === null && !buffers[k] && "text-[var(--ds-text-muted)]"),
3884
2879
  children: shown(k, spec)
@@ -3907,7 +2902,7 @@ function TimePicker({
3907
2902
  }
3908
2903
  )
3909
2904
  ] }),
3910
- /* @__PURE__ */ jsx(
2905
+ /* @__PURE__ */ jsx(AutoTooltip, { content: "Choose a time", children: /* @__PURE__ */ jsx(
3911
2906
  "button",
3912
2907
  {
3913
2908
  ref: buttonRef,
@@ -3921,7 +2916,7 @@ function TimePicker({
3921
2916
  className: cn("shrink-0 -mr-ds-1 rounded-[var(--ds-radius-sm)] text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] disabled:cursor-not-allowed", FOCUS_RING),
3922
2917
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
3923
2918
  }
3924
- )
2919
+ ) })
3925
2920
  ]
3926
2921
  }
3927
2922
  ),
@@ -3944,13 +2939,14 @@ function TimePicker({
3944
2939
  ),
3945
2940
  style: { top: pos.top, left: pos.left },
3946
2941
  children: /* @__PURE__ */ jsxs("div", { ref: setColumns, className: "inline-flex p-ds-1 gap-ds-1 tabular-nums", children: [
3947
- /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex max-h-48 w-[calc(var(--ds-space-10)+8px)] flex-col gap-[4px] overflow-y-auto p-[4px] scroll-p-[4px]", children: hours.map((h, i) => /* @__PURE__ */ jsx(
2942
+ /* @__PURE__ */ jsx("div", { "data-column": true, role: "listbox", "aria-label": "Hours", className: "flex max-h-48 w-[calc(var(--ds-space-10)+8px)] flex-col gap-[4px] overflow-y-auto p-[4px] scroll-p-[4px]", children: hours.map((h, i) => /* @__PURE__ */ jsx(
3948
2943
  "button",
3949
2944
  {
3950
2945
  type: "button",
2946
+ role: "option",
3951
2947
  tabIndex: i === stopOf2("hour", chosenHour) ? 0 : -1,
3952
2948
  onFocus: () => holdStop("hour", i),
3953
- "data-selected": use24Hour ? h === parseInt(value) : h === parsed.hour,
2949
+ "aria-selected": use24Hour ? h === parseInt(value) : h === parsed.hour,
3954
2950
  onMouseDown: (e) => e.preventDefault(),
3955
2951
  onClick: () => emitChange(h, parsed.minute, parsed.period),
3956
2952
  className: cn(
@@ -3964,13 +2960,14 @@ function TimePicker({
3964
2960
  h
3965
2961
  )) }),
3966
2962
  /* @__PURE__ */ jsx("div", { className: "w-px bg-[var(--ds-border-subtle)]" }),
3967
- /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex max-h-48 w-[calc(var(--ds-space-10)+8px)] flex-col gap-[4px] overflow-y-auto p-[4px] scroll-p-[4px]", children: minutes.map((m, i) => /* @__PURE__ */ jsx(
2963
+ /* @__PURE__ */ jsx("div", { "data-column": true, role: "listbox", "aria-label": "Minutes", className: "flex max-h-48 w-[calc(var(--ds-space-10)+8px)] flex-col gap-[4px] overflow-y-auto p-[4px] scroll-p-[4px]", children: minutes.map((m, i) => /* @__PURE__ */ jsx(
3968
2964
  "button",
3969
2965
  {
3970
2966
  type: "button",
2967
+ role: "option",
3971
2968
  tabIndex: i === stopOf2("minute", chosenMinute) ? 0 : -1,
3972
2969
  onFocus: () => holdStop("minute", i),
3973
- "data-selected": m === parsed.minute,
2970
+ "aria-selected": m === parsed.minute,
3974
2971
  onMouseDown: (e) => e.preventDefault(),
3975
2972
  onClick: () => emitChange(parsed.hour, m, parsed.period),
3976
2973
  className: cn(
@@ -3985,13 +2982,14 @@ function TimePicker({
3985
2982
  )) }),
3986
2983
  !use24Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
3987
2984
  /* @__PURE__ */ jsx("div", { className: "w-px bg-[var(--ds-border-subtle)]" }),
3988
- /* @__PURE__ */ jsx("div", { "data-column": true, className: "flex flex-col gap-[4px] w-ds-10", children: ["AM", "PM"].map((p, i) => /* @__PURE__ */ jsx(
2985
+ /* @__PURE__ */ jsx("div", { "data-column": true, role: "listbox", "aria-label": "AM or PM", className: "flex flex-col gap-[4px] w-ds-10", children: ["AM", "PM"].map((p, i) => /* @__PURE__ */ jsx(
3989
2986
  "button",
3990
2987
  {
3991
2988
  type: "button",
2989
+ role: "option",
3992
2990
  tabIndex: i === stopOf2("period", chosenPeriod) ? 0 : -1,
3993
2991
  onFocus: () => holdStop("period", i),
3994
- "data-selected": p === parsed.period,
2992
+ "aria-selected": p === parsed.period,
3995
2993
  onMouseDown: (e) => e.preventDefault(),
3996
2994
  onClick: () => emitChange(parsed.hour, parsed.minute, p),
3997
2995
  className: cn(
@@ -4054,6 +3052,9 @@ function getPaletteClasses(color, variant) {
4054
3052
  const styleFn = paletteByVariant[variant] ?? paletteByVariant.subtle;
4055
3053
  return styleFn(color);
4056
3054
  }
3055
+ var SELECTED = "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)]";
3056
+ var selectedEdge = (bordered) => bordered ? "border-[length:var(--ds-border-width)] border-[var(--ds-accent-ink)]" : "shadow-[inset_0_0_0_var(--ds-border-width)_var(--ds-accent-ink)]";
3057
+ var LABEL_FOCUS_RING = "has-[>[data-tag-label]:focus-visible]:outline-2 has-[>[data-tag-label]:focus-visible]:outline-solid has-[>[data-tag-label]:focus-visible]:outline-offset-2 has-[>[data-tag-label]:focus-visible]:outline-[var(--ds-focus)] has-[>[data-tag-label]:focus-visible]:shadow-none";
4057
3058
  var Tag = forwardRef(function Tag2({
4058
3059
  variant = "default",
4059
3060
  size = "md",
@@ -4061,44 +3062,62 @@ var Tag = forwardRef(function Tag2({
4061
3062
  maxWidth,
4062
3063
  onRemove,
4063
3064
  onClick,
3065
+ selected,
4064
3066
  disabled = false,
4065
3067
  className,
4066
3068
  children
4067
3069
  }, ref) {
3070
+ const chipRef = useRef(null);
3071
+ const setChip = (el) => {
3072
+ chipRef.current = el;
3073
+ if (typeof ref === "function") ref(el);
3074
+ else if (ref) ref.current = el;
3075
+ };
4068
3076
  const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1 });
4069
3077
  const handleRemove = (e) => {
4070
3078
  e.stopPropagation();
3079
+ focusAfterSelfRemoval(e.currentTarget, { item: chipRef.current });
4071
3080
  onRemove?.();
4072
3081
  };
4073
- const colorClasses = color ? getPaletteClasses(color, variant) : variantStyles[variant];
3082
+ const clickable = !!onClick;
3083
+ const isSelected = !!onClick && !!selected;
3084
+ const bordered = variant === "outline" || !color && (variant === "success" || variant === "warning" || variant === "error");
3085
+ const colorClasses = isSelected ? cn(SELECTED, selectedEdge(bordered)) : color ? getPaletteClasses(color, variant) : variantStyles[variant];
3086
+ const label = /* @__PURE__ */ jsxs(Fragment, { children: [
3087
+ isSelected && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "inline-flex shrink-0 mr-[calc(var(--ds-space-1)*0.5)]", children: /* @__PURE__ */ jsx(Icon, { name: "check", size: size === "sm" ? 10 : 12 }) }),
3088
+ /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate min-w-0", children })
3089
+ ] });
4074
3090
  const tag = /* @__PURE__ */ jsxs(
4075
3091
  "span",
4076
3092
  {
4077
- ref,
4078
- role: onClick ? "button" : void 0,
4079
- tabIndex: onClick && !disabled ? 0 : void 0,
4080
- "aria-disabled": disabled || void 0,
4081
- onClick: onClick && !disabled ? onClick : void 0,
4082
- onKeyDown: onClick && !disabled ? (e) => {
4083
- if (e.key === "Enter" || e.key === " ") {
4084
- e.preventDefault();
4085
- onClick();
4086
- }
4087
- } : void 0,
3093
+ ref: setChip,
3094
+ "aria-disabled": !clickable && disabled || void 0,
3095
+ "data-selected": isSelected ? "" : void 0,
4088
3096
  className: cn(
4089
3097
  "inline-flex items-center font-medium max-w-full min-w-0 whitespace-nowrap",
4090
3098
  "rounded-[var(--ds-radius-sm)]",
4091
3099
  "transition-colors duration-[var(--ds-transition-speed)]",
4092
3100
  sizeStyles[size],
4093
3101
  colorClasses,
4094
- onClick && !disabled && cn("cursor-pointer select-none hover:opacity-80", FOCUS_RING),
3102
+ clickable && !disabled && cn("relative cursor-pointer select-none hover:opacity-80", LABEL_FOCUS_RING),
4095
3103
  disabled && "opacity-disabled cursor-not-allowed",
4096
3104
  className
4097
3105
  ),
4098
3106
  style: maxWidth ? { maxWidth } : void 0,
4099
3107
  children: [
4100
- /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate min-w-0", children }),
4101
- onRemove && !disabled && /* @__PURE__ */ jsx(
3108
+ clickable ? /* @__PURE__ */ jsx(
3109
+ "button",
3110
+ {
3111
+ type: "button",
3112
+ "data-tag-label": "",
3113
+ disabled,
3114
+ "aria-pressed": selected,
3115
+ onClick,
3116
+ className: "flex min-w-0 max-w-full items-center cursor-[inherit] outline-none after:absolute after:inset-0 after:content-['']",
3117
+ children: label
3118
+ }
3119
+ ) : label,
3120
+ onRemove && !disabled && /* @__PURE__ */ jsx(AutoTooltip, { content: typeof children === "string" ? `Remove ${children}` : "Remove", children: /* @__PURE__ */ jsx(
4102
3121
  "button",
4103
3122
  {
4104
3123
  type: "button",
@@ -4106,7 +3125,7 @@ var Tag = forwardRef(function Tag2({
4106
3125
  "data-tag-remove": "",
4107
3126
  "aria-label": typeof children === "string" ? `Remove ${children}` : "Remove",
4108
3127
  className: cn(
4109
- "inline-flex items-center justify-center shrink-0",
3128
+ "relative inline-flex items-center justify-center shrink-0",
4110
3129
  "rounded-[var(--ds-radius-sm)]",
4111
3130
  "opacity-60 hover:opacity-100",
4112
3131
  "transition-opacity duration-[var(--ds-transition-speed)] cursor-pointer",
@@ -4114,7 +3133,7 @@ var Tag = forwardRef(function Tag2({
4114
3133
  ),
4115
3134
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: size === "sm" ? 10 : 12 })
4116
3135
  }
4117
- )
3136
+ ) })
4118
3137
  ]
4119
3138
  }
4120
3139
  );
@@ -4163,6 +3182,12 @@ function TagInput({
4163
3182
  const buttons = Array.from(containerRef.current?.querySelectorAll("[data-tag-remove]") ?? []);
4164
3183
  focusAfterRemoval(buttons, pending.index, inputRef.current);
4165
3184
  });
3185
+ const overlayMark = useContext(OverlayMarkCtx);
3186
+ const holdsText = input.trim() !== "";
3187
+ useEffect(() => {
3188
+ if (!holdsText || !overlayMark) return;
3189
+ return overlayMark.pend();
3190
+ }, [holdsText, overlayMark]);
4166
3191
  const handleKeyDown = useCallback(
4167
3192
  (e) => {
4168
3193
  if ((e.key === "Enter" || e.key === ",") && input.trim()) {
@@ -4268,7 +3293,8 @@ function PinInput({
4268
3293
  size: sizeProp,
4269
3294
  className,
4270
3295
  error = false,
4271
- id
3296
+ id,
3297
+ "aria-label": ariaLabel
4272
3298
  }) {
4273
3299
  const onChange = useReportedChange(onChangeProp, value);
4274
3300
  useInputModality();
@@ -4327,7 +3353,7 @@ function PinInput({
4327
3353
  "div",
4328
3354
  {
4329
3355
  role: "group",
4330
- "aria-label": "PIN input",
3356
+ "aria-label": ariaLabel ?? "PIN input",
4331
3357
  "aria-invalid": error || void 0,
4332
3358
  className: cn("inline-flex w-fit items-center gap-ds-2", className),
4333
3359
  children: digits.map((digit, i) => /* @__PURE__ */ jsx(
@@ -4487,7 +3513,7 @@ function Rating({
4487
3513
  {
4488
3514
  role: readOnly ? "img" : "radiogroup",
4489
3515
  id: control.id,
4490
- "aria-label": readOnly || !control.labelId ? `Rating: ${value} out of ${max}` : void 0,
3516
+ "aria-label": readOnly ? `${control.label ?? "Rating"}: ${value} out of ${max}` : control.labelId ? void 0 : control.label ?? `Rating: ${value} out of ${max}`,
4491
3517
  "aria-labelledby": readOnly ? void 0 : control.labelId,
4492
3518
  "aria-describedby": control.describedBy,
4493
3519
  "aria-invalid": readOnly ? void 0 : control.invalid || void 0,
@@ -4575,7 +3601,8 @@ function ColorPicker({
4575
3601
  swatches = DEFAULT_SWATCHES,
4576
3602
  disabled = false,
4577
3603
  className,
4578
- error = false
3604
+ error = false,
3605
+ "aria-label": ariaLabel
4579
3606
  }) {
4580
3607
  const onChange = useReportedChange(onChangeProp, value);
4581
3608
  useInputModality();
@@ -4640,6 +3667,8 @@ function ColorPicker({
4640
3667
  return /* @__PURE__ */ jsxs(
4641
3668
  "div",
4642
3669
  {
3670
+ role: "group",
3671
+ "aria-label": ariaLabel,
4643
3672
  "aria-invalid": error || void 0,
4644
3673
  className: cn(
4645
3674
  "w-full max-w-64 p-ds-3 space-y-ds-3",
@@ -4789,18 +3818,33 @@ var Alert = forwardRef(function Alert2({
4789
3818
  actions,
4790
3819
  placement = "inline",
4791
3820
  expandableContent,
3821
+ consent,
3822
+ confirmation,
4792
3823
  className
4793
3824
  }, ref) {
3825
+ const titleId = useId();
3826
+ const confirmationRef = useRef(null);
3827
+ const confirming = !!consent && confirmation != null;
3828
+ useEffect(() => {
3829
+ if (confirming) confirmationRef.current?.focus();
3830
+ }, [confirming]);
3831
+ const rootRef = useRef(null);
3832
+ const setRoot = (el) => {
3833
+ rootRef.current = el;
3834
+ if (typeof ref === "function") ref(el);
3835
+ else if (ref) ref.current = el;
3836
+ };
4794
3837
  const [detailsExpanded, setDetailsExpanded] = useState(false);
4795
3838
  const isMobile = useBreakpoint("sm");
4796
- const resolvedActions = actions ?? (action ? [action] : []);
3839
+ const resolvedActions = consent || confirming ? [] : actions ?? (action ? [action] : []);
4797
3840
  const shouldCollapseActions = isMobile && resolvedActions.length > 1;
4798
- const placementClasses = placement === "page-top" ? "sticky top-0 z-[var(--ds-z-sticky)] rounded-none" : placement === "section-top" ? "rounded-[var(--ds-radius-md)] my-ds-4" : "rounded-[var(--ds-radius-md)]";
3841
+ const placementClasses = placement === "page-top" && !consent ? "sticky top-0 z-[var(--ds-z-sticky)] rounded-none" : placement === "section-top" ? "rounded-[var(--ds-radius-md)] my-ds-4" : "rounded-[var(--ds-radius-md)]";
4799
3842
  return /* @__PURE__ */ jsxs(
4800
3843
  "div",
4801
3844
  {
4802
- ref,
4803
- role: "alert",
3845
+ ref: setRoot,
3846
+ role: consent ? "region" : variant === "error" || variant === "warning" ? "alert" : "status",
3847
+ "aria-labelledby": consent ? titleId : void 0,
4804
3848
  className: cn(
4805
3849
  "flex items-start gap-ds-3 p-ds-4",
4806
3850
  "border-[length:var(--ds-border-width)]",
@@ -4814,7 +3858,7 @@ var Alert = forwardRef(function Alert2({
4814
3858
  /* @__PURE__ */ jsx("div", { className: cn("mt-[0.3125em] w-3 h-3 shrink-0 rounded-[var(--ds-radius-sm)]", indicators[variant]) }),
4815
3859
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
4816
3860
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-3", children: [
4817
- /* @__PURE__ */ jsx("p", { className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
3861
+ /* @__PURE__ */ jsx("p", { id: titleId, className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
4818
3862
  resolvedActions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: shouldCollapseActions ? /* @__PURE__ */ jsx(
4819
3863
  DropdownMenu,
4820
3864
  {
@@ -4864,7 +3908,34 @@ var Alert = forwardRef(function Alert2({
4864
3908
  );
4865
3909
  }) })
4866
3910
  ] }),
4867
- children && /* @__PURE__ */ jsx("div", { className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)]", children }),
3911
+ confirming ? (
3912
+ // GOV.UK's cookie banner: the confirmation in the choices' place, announced, with focus on it
3913
+ /* @__PURE__ */ jsx("div", { ref: confirmationRef, role: "alert", tabIndex: -1, className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)] outline-none", children: confirmation })
3914
+ ) : children && /* @__PURE__ */ jsx("div", { className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)]", children }),
3915
+ consent && !confirming && // two choices of equal weight, the alert's own action look, side by side at any width (sharing a phone's row);
3916
+ // never a menu
3917
+ /* @__PURE__ */ jsx("div", { "data-alert-consent": "", className: "mt-ds-3 flex flex-wrap gap-ds-2", children: [consent.accept, consent.reject].map((choice) => choice.primary ? (
3918
+ // a page that insists on one primary choice: the library's primary Button in the other choice's box, its
3919
+ // padding, corners and height, wrapping as it does, so the two share a row's height and a phone's row
3920
+ // equally. Its height and padding go in its style: its size's classes set them, and a class can't be relied on
3921
+ // to undo another
3922
+ /* @__PURE__ */ jsx(Button, { type: "button", size: "xs", onClick: choice.onClick, style: { height: "auto", paddingBlock: "var(--ds-space-1)", paddingInline: "var(--ds-space-2\\.5)" }, className: "min-h-ds-6 whitespace-normal rounded-[var(--ds-radius-sm)] max-sm:flex-1 max-sm:min-h-[var(--ds-control-height-sm)]", children: choice.label }, choice.label)
3923
+ ) : /* @__PURE__ */ jsx(
3924
+ "button",
3925
+ {
3926
+ type: "button",
3927
+ onClick: choice.onClick,
3928
+ className: cn(
3929
+ "text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
3930
+ "transition-colors cursor-pointer max-sm:flex-1 max-sm:min-h-[var(--ds-control-height-sm)]",
3931
+ "border-[length:var(--ds-border-width)] border-current",
3932
+ actionColorsByVariant[variant],
3933
+ FOCUS_RING
3934
+ ),
3935
+ children: choice.label
3936
+ },
3937
+ choice.label
3938
+ )) }),
4868
3939
  expandableContent && /* @__PURE__ */ jsxs("div", { className: "mt-ds-2", children: [
4869
3940
  /* @__PURE__ */ jsxs(
4870
3941
  "button",
@@ -4895,13 +3966,21 @@ var Alert = forwardRef(function Alert2({
4895
3966
  detailsExpanded && /* @__PURE__ */ jsx("div", { className: "mt-ds-2 text-xs text-[var(--ds-text-secondary)] border-l-2 border-current/20 pl-ds-3", children: expandableContent })
4896
3967
  ] })
4897
3968
  ] }),
4898
- dismissible && /* @__PURE__ */ jsx(
4899
- "button",
3969
+ dismissible && (!consent || confirming) && // Sprint 25.8.7 item 33: the library's icon Button, so its name shows as its one tooltip, with a 24 px target; at
3970
+ // rest the muted cross where it was (the margin takes back the box's room), on hover the alert's own buttons'
3971
+ // tint. Its alert goes with it, so focus goes to a neighbour (focusAfterSelfRemoval), never to the page
3972
+ /* @__PURE__ */ jsx(
3973
+ Button,
4900
3974
  {
4901
3975
  type: "button",
4902
- onClick: onDismiss,
4903
- className: cn("shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
3976
+ variant: "ghost",
3977
+ size: "icon-xs",
4904
3978
  "aria-label": "Dismiss",
3979
+ onClick: (e) => {
3980
+ focusAfterSelfRemoval(e.currentTarget, { item: rootRef.current });
3981
+ onDismiss?.();
3982
+ },
3983
+ className: cn("-m-ds-1 shrink-0", actionColorsByVariant[variant], "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"),
4905
3984
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
4906
3985
  }
4907
3986
  )
@@ -5256,7 +4335,10 @@ function TransferList({
5256
4335
  "button",
5257
4336
  {
5258
4337
  type: "button",
5259
- onClick: moveAllRight,
4338
+ onClick: (e) => {
4339
+ focusAfterSelfRemoval(e.currentTarget);
4340
+ moveAllRight();
4341
+ },
5260
4342
  disabled: !available.filter((i) => !i.disabled).length,
5261
4343
  "aria-label": isStacked ? "Move all down" : "Move all right",
5262
4344
  className: cn(actionButtonClass),
@@ -5267,7 +4349,10 @@ function TransferList({
5267
4349
  "button",
5268
4350
  {
5269
4351
  type: "button",
5270
- onClick: moveRight,
4352
+ onClick: (e) => {
4353
+ focusAfterSelfRemoval(e.currentTarget);
4354
+ moveRight();
4355
+ },
5271
4356
  disabled: !checkedAvailable.size,
5272
4357
  "aria-label": isStacked ? "Move selected down" : "Move selected right",
5273
4358
  className: cn(actionButtonClass),
@@ -5278,7 +4363,10 @@ function TransferList({
5278
4363
  "button",
5279
4364
  {
5280
4365
  type: "button",
5281
- onClick: moveLeft,
4366
+ onClick: (e) => {
4367
+ focusAfterSelfRemoval(e.currentTarget);
4368
+ moveLeft();
4369
+ },
5282
4370
  disabled: !checkedSelected.size,
5283
4371
  "aria-label": isStacked ? "Move selected up" : "Move selected left",
5284
4372
  className: cn(actionButtonClass),
@@ -5289,7 +4377,10 @@ function TransferList({
5289
4377
  "button",
5290
4378
  {
5291
4379
  type: "button",
5292
- onClick: moveAllLeft,
4380
+ onClick: (e) => {
4381
+ focusAfterSelfRemoval(e.currentTarget);
4382
+ moveAllLeft();
4383
+ },
5293
4384
  disabled: !selected.filter((i) => !i.disabled).length,
5294
4385
  "aria-label": isStacked ? "Move all up" : "Move all left",
5295
4386
  className: cn(actionButtonClass),
@@ -5949,7 +5040,7 @@ var CardHeader = forwardRef(function CardHeader2(allProps, ref) {
5949
5040
  const menu = menuItems ? /* @__PURE__ */ jsx(
5950
5041
  DropdownMenu,
5951
5042
  {
5952
- trigger: /* @__PURE__ */ jsx(
5043
+ trigger: /* @__PURE__ */ jsx(AutoTooltip, { content: "More actions", children: /* @__PURE__ */ jsx(
5953
5044
  "button",
5954
5045
  {
5955
5046
  type: "button",
@@ -5963,7 +5054,7 @@ var CardHeader = forwardRef(function CardHeader2(allProps, ref) {
5963
5054
  onClick: (e) => e.stopPropagation(),
5964
5055
  children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: 16 })
5965
5056
  }
5966
- ),
5057
+ ) }),
5967
5058
  items: menuItems
5968
5059
  }
5969
5060
  ) : null;
@@ -6574,12 +5665,14 @@ function actionsAreBadges(actions) {
6574
5665
  const items = Children.toArray(actions);
6575
5666
  return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : elementType(item.type) === Badge));
6576
5667
  }
6577
- function TruncCell({ children, lines }) {
6578
- const { ref, isTruncated } = useTextOverflow({ lines });
6579
- const clampClass = lines === 1 ? "truncate" : `line-clamp-${lines}`;
6580
- const cell = /* @__PURE__ */ jsx("span", { ref, className: cn(clampClass, "block"), children });
6581
- if (!isTruncated) return cell;
6582
- return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: cell });
5668
+ var SKELETON_ROWS = 5;
5669
+ var SKELETON_WIDTHS = ["72%", "48%", "60%", "36%", "54%"];
5670
+ var LOADING_LABEL = "Loading table data";
5671
+ function RefreshLine({ className }) {
5672
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", "data-table-refreshing": "", className: cn("pointer-events-none absolute top-0 z-20 h-[2px] overflow-hidden", className), children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-[var(--ds-accent-primary)] animate-[gs-indeterminate_1.5s_ease-in-out_infinite] motion-reduce:animate-none motion-reduce:w-full motion-reduce:opacity-50" }) });
5673
+ }
5674
+ function SkeletonLine({ width, end = false, className }) {
5675
+ return /* @__PURE__ */ jsx("div", { className: cn("flex h-5 items-center", end && "justify-end", className), children: /* @__PURE__ */ jsx(Skeleton, { variant: "custom", width, height: "0.75rem" }) });
6583
5676
  }
6584
5677
  var TITLE_CLASSES = TITLE_TEXT;
6585
5678
  var FRAME_CLASSES = cn(
@@ -6824,6 +5917,11 @@ function Table({
6824
5917
  useEffect(() => {
6825
5918
  if (unsortable) warnOnce("Table", `the column "${unsortable}" is sortable, but its cells are elements, which can't be compared: give it a sortValue, (row) => the name, number or date it shows (data_display#1).`);
6826
5919
  }, [unsortable]);
5920
+ const pageSorts = !!onSortChange;
5921
+ const refused = pageSorts ? "" : columns.filter((col) => col.sortable && !canSort.has(col.key)).map((col) => col.key).join(", ");
5922
+ useEffect(() => {
5923
+ if (refused) warnOnce("Table", `the column "${refused}" is marked sortable, but its cells are elements the Table can't compare, so it draws no sort button: give it a sortValue, (row) => the name, number or date it shows, or sort the rows yourself with sortKey and onSortChange (data_display#1).`);
5924
+ }, [refused]);
6827
5925
  useEffect(() => {
6828
5926
  if (pager && data.length !== pager.totalItems) warnOnce("Table", `the pager counts ${pager.totalItems} rows but data holds ${data.length}: give the table and usePagination the same rows, every row of the list, and the table draws the pager's part (Sprint 25.13 A3).`);
6829
5927
  }, [pager, data.length]);
@@ -6843,7 +5941,16 @@ function Table({
6843
5941
  if (allSelected) onSelectionChange?.(selectedRows.filter((r) => !shown.has(r)));
6844
5942
  else onSelectionChange?.([...selectedRows.filter((r) => !shown.has(r)), ...drawnIdx]);
6845
5943
  };
6846
- const toggleRow = (idx) => {
5944
+ const anchor = useRef(null);
5945
+ const toggleRow = (idx, shiftKey2 = false) => {
5946
+ const from = anchor.current === null ? -1 : drawnIdx.indexOf(anchor.current);
5947
+ const to = drawnIdx.indexOf(idx);
5948
+ anchor.current = idx;
5949
+ if (shiftKey2 && from >= 0 && to >= 0 && from !== to) {
5950
+ const run = drawnIdx.slice(Math.min(from, to), Math.max(from, to) + 1);
5951
+ onSelectionChange?.([...selectedRows.filter((r) => !run.includes(r)), ...run]);
5952
+ return;
5953
+ }
6847
5954
  if (selectedSet.has(idx)) onSelectionChange?.(selectedRows.filter((r) => r !== idx));
6848
5955
  else onSelectionChange?.([...selectedRows, idx]);
6849
5956
  };
@@ -6871,6 +5978,9 @@ function Table({
6871
5978
  const narrowScreen = useBreakpoint(mobileBreakpoint);
6872
5979
  const { cards: isMobile, rootRef } = useCardsSwitch(narrowScreen, cardsBelow, title);
6873
5980
  useRevealFocus(rootRef, isMobile);
5981
+ useFocusKeptInList(rootRef);
5982
+ const [cardsEl, setCardsEl] = useState(null);
5983
+ useKeyboardScroll(cardsEl, title ?? ariaLabel ?? "Table", isMobile);
6874
5984
  const setRoot = useCallback((node) => {
6875
5985
  rootRef.current = node;
6876
5986
  if (typeof ref === "function") ref(node);
@@ -6998,8 +6108,14 @@ function Table({
6998
6108
  cardHeading,
6999
6109
  body
7000
6110
  ] });
7001
- const control = pager && !loading && data.length > 0 ? /* @__PURE__ */ jsx(Pagination, { ...pager.props }) : null;
6111
+ const paged = !!pager && data.length > 0;
6112
+ const control = paged ? /* @__PURE__ */ jsx(Pagination, { ...pager.props }) : null;
7002
6113
  const listRoot = control ? void 0 : setRoot;
6114
+ const cardsAreRoot = !title && !paged;
6115
+ const setCards = useCallback((el) => {
6116
+ setCardsEl(el);
6117
+ if (cardsAreRoot) setRoot(el);
6118
+ }, [cardsAreRoot, setRoot]);
7003
6119
  const listClass = control ? void 0 : className;
7004
6120
  const withControl = (list2) => control ? /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
7005
6121
  list2,
@@ -7033,15 +6149,23 @@ function Table({
7033
6149
  (same ?? (row.tabIndex >= 0 ? row : controls[0]))?.focus({ preventScroll: true });
7034
6150
  }, [isMobile, rootRef]);
7035
6151
  if (isMobile) {
7036
- if (loading) {
7037
- if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-ds-10", children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" }) }));
7038
- return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", className: cn("flex items-center justify-center py-ds-10", className), children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" }) });
6152
+ if (loading && data.length === 0) {
6153
+ const skeletonCards = /* @__PURE__ */ jsxs(Fragment, { children: [
6154
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: LOADING_LABEL }),
6155
+ /* @__PURE__ */ jsx("div", { "data-table-skeleton": "", className: "flex flex-col gap-ds-3", "aria-busy": "true", children: Array.from({ length: 3 }, (_, r) => /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)] p-ds-4 space-y-ds-3", children: columns.slice(0, 4).map((col, c) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-ds-2", children: [
6156
+ /* @__PURE__ */ jsx(SkeletonLine, { width: "56%", className: "flex-1" }),
6157
+ /* @__PURE__ */ jsx(SkeletonLine, { width: SKELETON_WIDTHS[(r + c) % SKELETON_WIDTHS.length], end: true, className: "flex-1" })
6158
+ ] }, col.key)) }, r)) })
6159
+ ] });
6160
+ if (title) return titledCards(skeletonCards);
6161
+ return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", className, children: skeletonCards });
7039
6162
  }
7040
6163
  if (data.length === 0 && emptyState) {
7041
6164
  if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", children: emptyState }));
7042
6165
  return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", className: cn("py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", className), children: emptyState });
7043
6166
  }
7044
- const cards = /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: /* @__PURE__ */ jsxs("div", { ref: title ? void 0 : listRoot, "data-ds-list": title ? void 0 : "", "data-gs": title ? void 0 : "table", className: cn("flex flex-col gap-ds-3 overflow-y-auto -m-[4px] p-[4px] scroll-p-[4px]", title ? "min-h-0" : listClass), onFocus: title ? void 0 : noteFocus, onBlur: title ? void 0 : forgetFocus, children: [
6167
+ const cards = /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: /* @__PURE__ */ jsxs("div", { ref: setCards, "data-ds-list": title ? void 0 : "", "data-gs": title ? void 0 : "table", className: cn("relative flex flex-col gap-ds-3 overflow-y-auto -m-[4px] p-[4px] scroll-p-[4px]", FOCUS_INSET, title ? "min-h-0" : listClass), onFocus: title ? void 0 : noteFocus, onBlur: title ? void 0 : forgetFocus, children: [
6168
+ loading && data.length > 0 && /* @__PURE__ */ jsx(RefreshLine, { className: "inset-x-[4px] rounded-full" }),
7045
6169
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select`, children: "Select" }),
7046
6170
  drawn.map(({ row, originalIdx }) => {
7047
6171
  const isSelected = selectedSet.has(originalIdx);
@@ -7085,7 +6209,7 @@ function Table({
7085
6209
  } : void 0,
7086
6210
  children: [
7087
6211
  selectable && // "Select" and the row's name, as in the table (T15)
7088
- /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-card-name-${idPart(id)}` : `${headingId}-select` }),
6212
+ /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: (_, { shiftKey: shiftKey2 }) => toggleRow(originalIdx, shiftKey2), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-card-name-${idPart(id)}` : `${headingId}-select` }),
7089
6213
  columns.map((col, c) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-ds-2 text-sm", children: [
7090
6214
  /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[var(--ds-text-secondary)] font-medium text-xs uppercase tracking-wider", children: col.header }),
7091
6215
  /* @__PURE__ */ jsx(CardValue, { value: row[col.key], id: lead + c === nameIndex ? `${headingId}-card-name-${idPart(id)}` : void 0 })
@@ -7129,9 +6253,10 @@ function Table({
7129
6253
  return withControl(title ? titledCards(cards) : cards);
7130
6254
  }
7131
6255
  const cellCount = columns.length + lead;
7132
- const table = /* @__PURE__ */ jsxs("table", { ref: tableRef, className: cn("w-full text-sm", PINNED_LAYERS), ...title ? { "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : void 0 } : ariaLabel ? { "aria-label": ariaLabel } : {}, children: [
6256
+ const table = /* @__PURE__ */ jsxs("table", { ref: tableRef, "aria-busy": loading || void 0, className: cn("w-full text-sm", PINNED_LAYERS), ...title ? { "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : void 0 } : ariaLabel ? { "aria-label": ariaLabel } : {}, children: [
7133
6257
  /* @__PURE__ */ jsx("thead", { className: cn("bg-[var(--ds-bg-muted)]", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs("tr", { className: "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] [--gs-row-tint:var(--ds-bg-muted)]", children: [
7134
- selectable && /* @__PURE__ */ jsx("th", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(
6258
+ selectable && // data-ds-select-all: the selection's own control, where a BulkActionBar's Clear selection sends focus
6259
+ /* @__PURE__ */ jsx("th", { "data-ds-select-all": "", className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(
7135
6260
  Checkbox,
7136
6261
  {
7137
6262
  checked: allSelected,
@@ -7142,8 +6267,8 @@ function Table({
7142
6267
  ) }),
7143
6268
  expandable && /* @__PURE__ */ jsx("th", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3"), style: pinStyle(selectable ? 1 : 0, cellCount) }),
7144
6269
  columns.map((col, c) => {
7145
- const isSortable = col.header.trim() !== "" && (col.sortable ?? (!!sortable && canSort.has(col.key)));
7146
- const isActiveSort = sortKey === col.key && !!sortDir && unsortable !== col.key;
6270
+ const isSortable = col.header.trim() !== "" && (pageSorts ? col.sortable ?? (!!sortable && canSort.has(col.key)) : (col.sortable ?? !!sortable) && canSort.has(col.key));
6271
+ const isActiveSort = sortKey === col.key && !!sortDir && (pageSorts || unsortable !== col.key);
7147
6272
  const headerKind = kindOf2(col);
7148
6273
  const headerAlign = col.align ?? (headerKind === "number" ? "right" : "left");
7149
6274
  const shrink = (headerKind === "number" || headerKind === "date") && !col.maxWidth;
@@ -7171,21 +6296,18 @@ function Table({
7171
6296
  "inline-flex items-center gap-ds-1 uppercase tracking-wider cursor-pointer select-none",
7172
6297
  "[font:inherit] text-inherit hover:text-[var(--ds-text-primary)]",
7173
6298
  "rounded-[var(--ds-radius-sm)]",
6299
+ // in a right-aligned column the indicator goes before the label, so the label ends where the
6300
+ // column's values end; the sorted column's label in full colour (the look check, 14 October).
6301
+ // Set at the line's top, not its baseline: reversed, the button takes its baseline from the
6302
+ // indicator, which has no text, and stood 2 px above the other headers' labels
6303
+ headerAlign === "right" && "flex-row-reverse",
6304
+ "align-top",
6305
+ isActiveSort && "text-[var(--ds-text-primary)]",
7174
6306
  FOCUS_RING
7175
6307
  ),
7176
6308
  children: [
7177
6309
  col.header,
7178
- /* @__PURE__ */ jsx(
7179
- Icon,
7180
- {
7181
- name: "chevron-right",
7182
- size: 12,
7183
- className: cn(
7184
- "transition-transform",
7185
- !isActiveSort ? "invisible" : sortDir === "asc" ? "-rotate-90" : "rotate-90"
7186
- )
7187
- }
7188
- )
6310
+ /* @__PURE__ */ jsx(SortIndicator, { sorted: isActiveSort ? sortDir : false })
7189
6311
  ]
7190
6312
  }
7191
6313
  )
@@ -7195,14 +6317,15 @@ function Table({
7195
6317
  );
7196
6318
  })
7197
6319
  ] }) }),
7198
- /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
7199
- "td",
7200
- {
7201
- colSpan: cellCount,
7202
- className: "px-ds-4 py-ds-10 text-center",
7203
- children: /* @__PURE__ */ jsx(Spinner, { label: "Loading table data" })
7204
- }
7205
- ) }) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
6320
+ /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: loading && data.length === 0 ? (
6321
+ // the rows' shape on a first load (item 16), each cell's bar where its text will be; the rows are hidden
6322
+ // from a screen reader, which hears the status beside the table
6323
+ Array.from({ length: SKELETON_ROWS }, (_, r) => /* @__PURE__ */ jsxs("tr", { "data-table-skeleton": "", className: "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] last:border-b-0", children: [
6324
+ selectable && /* @__PURE__ */ jsx("td", { "aria-hidden": true, className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), children: /* @__PURE__ */ jsx(SkeletonLine, { width: "16px" }) }),
6325
+ expandable && /* @__PURE__ */ jsx("td", { "aria-hidden": true, className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3") }),
6326
+ columns.map((col, c) => /* @__PURE__ */ jsx("td", { className: density === "compact" ? "px-ds-3 py-ds-2" : "px-ds-4 py-ds-3", children: /* @__PURE__ */ jsx("div", { "aria-hidden": true, children: /* @__PURE__ */ jsx(SkeletonLine, { width: SKELETON_WIDTHS[(r + c) % SKELETON_WIDTHS.length], end: (col.align ?? (kindOf2(col) === "number" ? "right" : "left")) === "right" }) }) }, col.key))
6327
+ ] }, `skeleton-${r}`))
6328
+ ) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
7206
6329
  "td",
7207
6330
  {
7208
6331
  colSpan: cellCount,
@@ -7246,8 +6369,8 @@ function Table({
7246
6369
  }
7247
6370
  } : void 0,
7248
6371
  children: [
7249
- selectable && /* @__PURE__ */ jsx("td", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3", pinFocus(0, cellCount)), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(originalIdx), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-name-${idPart(id)}` : `${headingId}-select` }) }),
7250
- expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3", pinFocus(selectable ? 1 : 0, cellCount)), style: pinStyle(selectable ? 1 : 0, cellCount), children: /* @__PURE__ */ jsx(
6372
+ selectable && /* @__PURE__ */ jsx("td", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3", pinFocus(0, cellCount)), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: (_, { shiftKey: shiftKey2 }) => toggleRow(originalIdx, shiftKey2), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-name-${idPart(id)}` : `${headingId}-select` }) }),
6373
+ expandable && /* @__PURE__ */ jsx("td", { className: cn("w-8", density === "compact" ? "px-ds-1 py-ds-2" : "px-ds-2 py-ds-3", pinFocus(selectable ? 1 : 0, cellCount)), style: pinStyle(selectable ? 1 : 0, cellCount), children: /* @__PURE__ */ jsx(AutoTooltip, { content: "Details", children: /* @__PURE__ */ jsx(
7251
6374
  "button",
7252
6375
  {
7253
6376
  type: "button",
@@ -7264,7 +6387,7 @@ function Table({
7264
6387
  }
7265
6388
  )
7266
6389
  }
7267
- ) }),
6390
+ ) }) }),
7268
6391
  columns.map((col, c) => {
7269
6392
  const truncLines = col.truncate === true ? 1 : col.truncate ? col.truncate.lines : 0;
7270
6393
  const kind = kindOf2(col);
@@ -7274,9 +6397,9 @@ function Table({
7274
6397
  const whole2 = !kind && renderedKinds.has(col.key);
7275
6398
  let inner;
7276
6399
  if (truncLines > 0) {
7277
- inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
6400
+ inner = /* @__PURE__ */ jsx(CutCell, { lines: truncLines, children: cellContent });
7278
6401
  } else if (kind === "email") {
7279
- inner = /* @__PURE__ */ jsx("span", { className: "max-w-[40ch] truncate inline-block align-bottom", title: typeof cellContent === "string" ? cellContent : void 0, children: cellContent });
6402
+ inner = /* @__PURE__ */ jsx(CutEmail, { children: cellContent });
7280
6403
  } else {
7281
6404
  inner = cellContent;
7282
6405
  }
@@ -7335,10 +6458,12 @@ function Table({
7335
6458
  ref: listRoot,
7336
6459
  "data-ds-list": "",
7337
6460
  "data-gs": "table",
7338
- className: cn("w-full min-w-0 flex flex-col", FRAME_CLASSES, FRAME_FOCUS_RING, control && "min-h-0", listClass),
6461
+ className: cn("relative w-full min-w-0 flex flex-col", FRAME_CLASSES, FRAME_FOCUS_RING, control && "min-h-0", listClass),
7339
6462
  onFocus: noteFocus,
7340
6463
  onBlur: forgetFocus,
7341
6464
  children: [
6465
+ loading && data.length > 0 && /* @__PURE__ */ jsx(RefreshLine, { className: "inset-x-0 rounded-t-[inherit]" }),
6466
+ loading && data.length === 0 && /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: LOADING_LABEL }),
7342
6467
  title && /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: true }),
7343
6468
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select-all`, children: "Select all rows shown" }),
7344
6469
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select`, children: "Select" }),
@@ -8144,6 +7269,12 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8144
7269
  useRevealFocus(allDayRef);
8145
7270
  useRevealFocus(scrollerRef);
8146
7271
  const { ref: edgesRef, measure: measureEdges, fadeProps: edgeFade } = useScrollEdges();
7272
+ const [dayStrip, setDayStrip] = useState(null);
7273
+ const setDays = useCallback((el) => {
7274
+ edgesRef(el);
7275
+ setDayStrip(el);
7276
+ }, [edgesRef]);
7277
+ useKeyboardScroll(dayStrip, "Days", true, false, "x");
8147
7278
  const [list2, setList] = useState(null);
8148
7279
  const handleScroll = useCallback((e) => {
8149
7280
  if (syncingScroll.current) return;
@@ -8246,9 +7377,9 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8246
7377
  case "End":
8247
7378
  return cellKey(days[days.length - 1], hour);
8248
7379
  case "PageUp":
8249
- return cellKey(addDays2(day, -7), hour);
7380
+ return cellKey(addDays(day, -7), hour);
8250
7381
  case "PageDown":
8251
- return cellKey(addDays2(day, 7), hour);
7382
+ return cellKey(addDays(day, 7), hour);
8252
7383
  default:
8253
7384
  return null;
8254
7385
  }
@@ -8268,7 +7399,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8268
7399
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: "flex flex-col h-full overflow-hidden", onKeyDown: grid.onKeyDown, children: [
8269
7400
  /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
8270
7401
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]" }),
8271
- /* @__PURE__ */ jsx("div", { ref: edgesRef, onScroll: measureEdges, ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx("div", { className: "grid", style: { gridTemplateColumns: dayGridColumns, minWidth: stripWidth }, children: days.map((day, i) => /* @__PURE__ */ jsxs("div", { className: cn("text-center py-ds-2", i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", selectedDate && isSameDay(day, selectedDate) && "bg-[var(--ds-accent-whisper)]"), children: [
7402
+ /* @__PURE__ */ jsx("div", { ref: setDays, onScroll: measureEdges, ...edgeFade, className: cn("flex-1 overflow-x-auto gs-scrollbar-none", FOCUS_INSET), "data-week-scroll": true, children: /* @__PURE__ */ jsx("div", { className: "grid", style: { gridTemplateColumns: dayGridColumns, minWidth: stripWidth }, children: days.map((day, i) => /* @__PURE__ */ jsxs("div", { className: cn("text-center py-ds-2", i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", selectedDate && isSameDay(day, selectedDate) && "bg-[var(--ds-accent-whisper)]"), children: [
8272
7403
  /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--ds-text-muted)] font-medium", children: names[i] }),
8273
7404
  /* @__PURE__ */ jsx("div", { className: cn("text-lg font-semibold mt-ds-0.5 mx-auto w-8 h-8 flex items-center justify-center rounded-[var(--ds-radius-full)]", isToday(day) ? TODAY_CIRCLE : "text-[var(--ds-text-primary)]"), children: day.getDate() })
8274
7405
  ] }, i)) }) })
@@ -9056,9 +8187,9 @@ function Calendar({
9056
8187
  return getMonthGrid(currentDate.getFullYear(), currentDate.getMonth(), weekStartsOn)[0][0];
9057
8188
  }, [currentView, currentDate, weekStartsOn]);
9058
8189
  const drawnTo = useMemo(() => {
9059
- if (currentView === "week") return addDays2(drawnFrom, 7);
8190
+ if (currentView === "week") return addDays(drawnFrom, 7);
9060
8191
  if (currentView === "year") return new Date(drawnFrom.getFullYear() + 1, 0, 1);
9061
- return addDays2(drawnFrom, 42);
8192
+ return addDays(drawnFrom, 42);
9062
8193
  }, [currentView, drawnFrom]);
9063
8194
  const { drawnEvents, unreadableRepeats } = useMemo(() => {
9064
8195
  const out = [];
@@ -9096,7 +8227,7 @@ function Calendar({
9096
8227
  if (currentView === "month") {
9097
8228
  setCurrentDate(addMonths(currentDate, direction));
9098
8229
  } else if (currentView === "week") {
9099
- setCurrentDate(addDays2(currentDate, direction * 7));
8230
+ setCurrentDate(addDays(currentDate, direction * 7));
9100
8231
  } else {
9101
8232
  setCurrentDate(new Date(currentDate.getFullYear() + direction, currentDate.getMonth(), 1));
9102
8233
  }
@@ -9956,7 +9087,9 @@ function TreeView({
9956
9087
  defaultChecked = [],
9957
9088
  onSelect,
9958
9089
  onCheck,
9959
- className
9090
+ className,
9091
+ "aria-label": ariaLabel,
9092
+ "aria-labelledby": ariaLabelledBy
9960
9093
  }) {
9961
9094
  const [ownSelected, setOwnSelected] = useState(() => new Set(defaultSelected));
9962
9095
  const selected = useMemo(() => selectedProp ? new Set(selectedProp) : ownSelected, [selectedProp, ownSelected]);
@@ -10073,7 +9206,7 @@ function TreeView({
10073
9206
  },
10074
9207
  [flatNodes, baseId, expanded, toggleExpand, checkable, handleCheck, selectable, handleSelect]
10075
9208
  );
10076
- return /* @__PURE__ */ jsx("div", { role: "tree", className: cn("text-sm", className), children: nodes.map((node) => /* @__PURE__ */ jsx(
9209
+ return /* @__PURE__ */ jsx("div", { role: "tree", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className: cn("text-sm", className), children: nodes.map((node) => /* @__PURE__ */ jsx(
10077
9210
  TreeNodeRow,
10078
9211
  {
10079
9212
  node,
@@ -10166,6 +9299,7 @@ function TreeNodeRow({
10166
9299
  CheckboxIndicator2,
10167
9300
  {
10168
9301
  state: checkState,
9302
+ label: node.label,
10169
9303
  onClick: (e) => {
10170
9304
  e.stopPropagation();
10171
9305
  handleCheck(node);
@@ -10199,12 +9333,13 @@ function TreeNodeRow({
10199
9333
  )) })
10200
9334
  ] });
10201
9335
  }
10202
- function CheckboxIndicator2({ state, onClick }) {
9336
+ function CheckboxIndicator2({ state, label, onClick }) {
10203
9337
  const filled = state !== "unchecked";
10204
9338
  return /* @__PURE__ */ jsx(
10205
9339
  "span",
10206
9340
  {
10207
9341
  role: "checkbox",
9342
+ "aria-label": label,
10208
9343
  "aria-checked": state === "indeterminate" ? "mixed" : state === "checked",
10209
9344
  className: cn(
10210
9345
  "flex items-center justify-center shrink-0 h-[18px] w-[18px] rounded-[calc(var(--ds-radius-sm)*0.6)] border-[2px] transition-all cursor-pointer",
@@ -10215,6 +9350,32 @@ function CheckboxIndicator2({ state, onClick }) {
10215
9350
  }
10216
9351
  );
10217
9352
  }
9353
+ function useMediaQuery(query) {
9354
+ const subscribe = useCallback((onChange) => {
9355
+ const mql = window.matchMedia(query);
9356
+ mql.addEventListener("change", onChange);
9357
+ return () => mql.removeEventListener("change", onChange);
9358
+ }, [query]);
9359
+ return useSyncExternalStore(
9360
+ subscribe,
9361
+ () => window.matchMedia(query).matches,
9362
+ () => false
9363
+ );
9364
+ }
9365
+ var ROUND_BUTTON = cn(
9366
+ "flex items-center justify-center w-8 h-8",
9367
+ "rounded-[var(--ds-radius-full)]",
9368
+ "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
9369
+ "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
9370
+ "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
9371
+ FOCUS_RING
9372
+ );
9373
+ var ROTATION_BUTTON = cn(
9374
+ "flex items-center justify-center w-6 h-6 rounded-[var(--ds-radius-full)]",
9375
+ "bg-[color-mix(in_oklab,var(--ds-bg-surface-elevated)_70%,transparent)] [backdrop-filter:blur(6px)] [-webkit-backdrop-filter:blur(6px)]",
9376
+ "text-[var(--ds-text-primary)] transition-colors cursor-pointer hover:bg-[var(--ds-bg-surface-elevated)]",
9377
+ FOCUS_RING
9378
+ );
10218
9379
  function Carousel({
10219
9380
  children,
10220
9381
  autoPlay = false,
@@ -10222,6 +9383,8 @@ function Carousel({
10222
9383
  loop = false,
10223
9384
  showIndicators = true,
10224
9385
  showArrows = true,
9386
+ "aria-label": ariaLabel,
9387
+ "aria-labelledby": ariaLabelledBy,
10225
9388
  className,
10226
9389
  ref
10227
9390
  }) {
@@ -10230,32 +9393,45 @@ function Carousel({
10230
9393
  const [dragOffset, setDragOffset] = useState(0);
10231
9394
  const trackRef = useRef(null);
10232
9395
  const dragStart = useRef(null);
10233
- const pauseRef = useRef(false);
10234
9396
  const count = children.length;
9397
+ const reducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
9398
+ const [stopped, setStopped] = useState(null);
9399
+ const playing = autoPlay && count > 1 && !(stopped ?? reducedMotion);
9400
+ const [hovered, setHovered] = useState(false);
9401
+ const [focusInSlide, setFocusInSlide] = useState(false);
9402
+ const rotating = playing && !hovered && !dragging && !focusInSlide;
9403
+ const [said, setSaid] = useState("");
10235
9404
  const goTo = useCallback(
10236
9405
  (index) => {
10237
- if (loop) {
10238
- setCurrent((index % count + count) % count);
10239
- } else {
10240
- setCurrent(Math.max(0, Math.min(index, count - 1)));
10241
- }
9406
+ const to = loop ? (index % count + count) % count : Math.max(0, Math.min(index, count - 1));
9407
+ setCurrent(to);
9408
+ setSaid(`Slide ${to + 1} of ${count}`);
10242
9409
  },
10243
9410
  [count, loop]
10244
9411
  );
10245
9412
  const next = useCallback(() => goTo(current + 1), [goTo, current]);
10246
9413
  const prev = useCallback(() => goTo(current - 1), [goTo, current]);
10247
9414
  useEffect(() => {
10248
- if (!autoPlay || count <= 1) return;
10249
- const interval = setInterval(() => {
10250
- if (!pauseRef.current) goTo(current + 1);
10251
- }, autoPlayInterval);
9415
+ if (!rotating) return;
9416
+ const interval = setInterval(() => setCurrent((c) => (c + 1) % count), autoPlayInterval);
10252
9417
  return () => clearInterval(interval);
10253
- }, [autoPlay, autoPlayInterval, current, count, goTo]);
9418
+ }, [rotating, autoPlayInterval, count, current]);
9419
+ const onFocus = () => {
9420
+ if (playing && inputModality() !== "pointer") setStopped(true);
9421
+ };
9422
+ const onSlidesBlur = (e) => {
9423
+ if (!e.currentTarget.contains(e.relatedTarget)) setFocusInSlide(false);
9424
+ };
9425
+ const onPointerEnter = (e) => {
9426
+ if (e.pointerType !== "touch") setHovered(true);
9427
+ };
9428
+ const onPointerLeave = (e) => {
9429
+ if (e.pointerType !== "touch") setHovered(false);
9430
+ };
10254
9431
  const handlePointerDown = useCallback((e) => {
10255
9432
  if (e.pointerType === "mouse" && e.button !== 0) return;
10256
9433
  dragStart.current = { x: e.clientX, time: Date.now() };
10257
9434
  setDragging(true);
10258
- pauseRef.current = true;
10259
9435
  e.target.setPointerCapture(e.pointerId);
10260
9436
  }, []);
10261
9437
  const handlePointerMove = useCallback(
@@ -10280,9 +9456,6 @@ function Carousel({
10280
9456
  dragStart.current = null;
10281
9457
  setDragging(false);
10282
9458
  setDragOffset(0);
10283
- setTimeout(() => {
10284
- pauseRef.current = false;
10285
- }, 1e3);
10286
9459
  },
10287
9460
  [next, prev]
10288
9461
  );
@@ -10293,72 +9466,72 @@ function Carousel({
10293
9466
  {
10294
9467
  ref,
10295
9468
  "data-gs-clip": "",
9469
+ role: "group",
9470
+ "aria-roledescription": "carousel",
9471
+ "aria-label": ariaLabel,
9472
+ "aria-labelledby": ariaLabelledBy,
10296
9473
  className: cn(
10297
9474
  "relative overflow-hidden rounded-[var(--ds-radius-md)]",
10298
9475
  "bg-[var(--ds-bg-muted)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10299
9476
  className
10300
9477
  ),
10301
- onMouseEnter: () => {
10302
- pauseRef.current = true;
10303
- },
10304
- onMouseLeave: () => {
10305
- if (!dragging) pauseRef.current = false;
10306
- },
9478
+ onFocus,
9479
+ onPointerEnter,
9480
+ onPointerLeave,
10307
9481
  children: [
9482
+ autoPlay && count > 1 && /* @__PURE__ */ jsx(
9483
+ "button",
9484
+ {
9485
+ type: "button",
9486
+ "data-carousel-rotation": "",
9487
+ onClick: () => setStopped(playing),
9488
+ "aria-label": playing ? "Stop automatic slide show" : "Start automatic slide show",
9489
+ className: cn("absolute left-ds-2 top-ds-2 z-10", ROTATION_BUTTON),
9490
+ children: /* @__PURE__ */ jsx(Icon, { name: playing ? "pause" : "play", size: 12 })
9491
+ }
9492
+ ),
10308
9493
  /* @__PURE__ */ jsx(
10309
9494
  "div",
10310
9495
  {
10311
9496
  ref: trackRef,
10312
- className: cn("flex items-stretch", !dragging && "transition-transform duration-400 ease-[var(--ds-transition-ease)]"),
9497
+ className: cn("flex items-stretch", !dragging && "transition-transform duration-400 ease-[var(--ds-transition-ease)] motion-reduce:transition-none"),
10313
9498
  style: { transform: `translateX(calc(-${current * 100}% + ${dragOffset}px))` },
10314
9499
  onPointerDown: handlePointerDown,
10315
9500
  onPointerMove: handlePointerMove,
10316
9501
  onPointerUp: handlePointerUp,
10317
9502
  onPointerCancel: handlePointerUp,
10318
- children: children.map((child, i) => /* @__PURE__ */ jsx("div", { className: "w-full shrink-0 select-none flex flex-col [&>*]:flex-1", "aria-roledescription": "slide", "aria-label": `Slide ${i + 1} of ${count}`, children: child }, i))
9503
+ onFocus: () => setFocusInSlide(true),
9504
+ onBlur: onSlidesBlur,
9505
+ children: children.map((child, i) => (
9506
+ // each slide named by its place in the set; those out of view are out of reach, for Tab and assistive technology
9507
+ /* @__PURE__ */ jsx("div", { role: "group", "aria-roledescription": "slide", "aria-label": `${i + 1} of ${count}`, inert: i !== current || void 0, className: "w-full shrink-0 select-none flex flex-col [&>*]:flex-1", children: child }, i)
9508
+ ))
10319
9509
  }
10320
9510
  ),
9511
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: said }),
10321
9512
  showArrows && count > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
10322
- /* @__PURE__ */ jsx(
9513
+ /* @__PURE__ */ jsx(AutoTooltip, { content: "Previous slide", children: /* @__PURE__ */ jsx(
10323
9514
  "button",
10324
9515
  {
10325
9516
  type: "button",
10326
9517
  onClick: prev,
10327
9518
  disabled: !canPrev,
10328
9519
  "aria-label": "Previous slide",
10329
- className: cn(
10330
- "absolute left-ds-4 top-1/2 -translate-y-1/2 z-10",
10331
- "flex items-center justify-center w-8 h-8",
10332
- "rounded-[var(--ds-radius-full)]",
10333
- "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10334
- "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10335
- "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10336
- FOCUS_RING,
10337
- !canPrev && "opacity-0 pointer-events-none"
10338
- ),
9520
+ className: cn("absolute left-ds-4 top-1/2 -translate-y-1/2 z-10", ROUND_BUTTON, !canPrev && "opacity-0 pointer-events-none"),
10339
9521
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16, className: "rotate-180" })
10340
9522
  }
10341
- ),
10342
- /* @__PURE__ */ jsx(
9523
+ ) }),
9524
+ /* @__PURE__ */ jsx(AutoTooltip, { content: "Next slide", children: /* @__PURE__ */ jsx(
10343
9525
  "button",
10344
9526
  {
10345
9527
  type: "button",
10346
9528
  onClick: next,
10347
9529
  disabled: !canNext,
10348
9530
  "aria-label": "Next slide",
10349
- className: cn(
10350
- "absolute right-ds-4 top-1/2 -translate-y-1/2 z-10",
10351
- "flex items-center justify-center w-8 h-8",
10352
- "rounded-[var(--ds-radius-full)]",
10353
- "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10354
- "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10355
- "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10356
- FOCUS_RING,
10357
- !canNext && "opacity-0 pointer-events-none"
10358
- ),
9531
+ className: cn("absolute right-ds-4 top-1/2 -translate-y-1/2 z-10", ROUND_BUTTON, !canNext && "opacity-0 pointer-events-none"),
10359
9532
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
10360
9533
  }
10361
- )
9534
+ ) })
10362
9535
  ] }),
10363
9536
  showIndicators && count > 1 && /* @__PURE__ */ jsx("div", { className: "absolute bottom-ds-3 left-1/2 -translate-x-1/2 flex gap-ds-1.5 z-10", children: children.map((_, i) => /* @__PURE__ */ jsx(
10364
9537
  "button",
@@ -10366,6 +9539,7 @@ function Carousel({
10366
9539
  type: "button",
10367
9540
  onClick: () => goTo(i),
10368
9541
  "aria-label": `Go to slide ${i + 1}`,
9542
+ "aria-current": i === current || void 0,
10369
9543
  className: cn(
10370
9544
  "w-2 h-2 rounded-[var(--ds-radius-full)] transition-all cursor-pointer",
10371
9545
  FOCUS_RING,
@@ -10607,7 +9781,10 @@ function ChatInputBar({ onSend, busy, onStop, onAttach, onVoice, placeholder })
10607
9781
  ) });
10608
9782
  }
10609
9783
  function Suggestions({ items, onPick, className }) {
10610
- return /* @__PURE__ */ jsx("div", { "data-suggestions": "", className: cn("flex flex-wrap gap-ds-2", className), children: items.map((s) => /* @__PURE__ */ jsx(Tag, { variant: "default", size: "md", onClick: () => onPick(s), children: s }, s)) });
9784
+ const pressed = useRef(null);
9785
+ return /* @__PURE__ */ jsx("div", { "data-suggestions": "", className: cn("flex flex-wrap gap-ds-2", className), onClickCapture: (e) => {
9786
+ pressed.current = e.target.closest("button");
9787
+ }, children: items.map((s) => /* @__PURE__ */ jsx(Tag, { variant: "default", size: "md", onClick: () => onPick(s, pressed.current), children: s }, s)) });
10611
9788
  }
10612
9789
  var NEAR_BOTTOM_PX = 24;
10613
9790
  function ChatMessageList({
@@ -10636,7 +9813,7 @@ function ChatMessageList({
10636
9813
  scrollRef.current = node;
10637
9814
  setScroller(node);
10638
9815
  }, []);
10639
- useRevealFocus(scroller);
9816
+ useKeyboardScroll(scroller, "Messages");
10640
9817
  const atBottomRef = useRef(true);
10641
9818
  const [showJump, setShowJump] = useState(false);
10642
9819
  const [prevMessages, setPrevMessages] = useState(messages);
@@ -10675,12 +9852,16 @@ function ChatMessageList({
10675
9852
  return () => observer.disconnect();
10676
9853
  }, [autoScroll, scrollToBottom]);
10677
9854
  const empty = messages.length === 0;
10678
- const pick = (text) => onSend?.(text);
9855
+ const rootRef = useRef(null);
9856
+ const pick = (text, from) => {
9857
+ if (from) focusAfterSelfRemoval(from, { item: from.closest("[data-suggestions]"), to: () => rootRef.current?.querySelector("textarea") });
9858
+ onSend?.(text);
9859
+ };
10679
9860
  const showSuggestionsAfterReply = !empty && suggestionsMode === "always" && !!suggestions?.length;
10680
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col flex-1 min-h-0", className), children: [
9861
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("flex flex-col flex-1 min-h-0", className), children: [
10681
9862
  /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: announcement }),
10682
9863
  /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-h-0", children: [
10683
- /* @__PURE__ */ jsx("div", { ref: setScroll, onScroll, className: "h-full overflow-y-auto scroll-p-[4px]", children: empty ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-ds-6 px-ds-4 py-ds-8 text-center", children: [
9864
+ /* @__PURE__ */ jsx("div", { ref: setScroll, onScroll, className: cn("h-full overflow-y-auto scroll-p-[4px]", FOCUS_INSET), children: empty ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-ds-6 px-ds-4 py-ds-8 text-center", children: [
10684
9865
  emptyState ?? /* @__PURE__ */ jsx(
10685
9866
  EmptyState,
10686
9867
  {
@@ -10796,8 +9977,8 @@ function ChatPanel({
10796
9977
  ] });
10797
9978
  }
10798
9979
  function Sheet({ open, onClose: onCloseProp, title, children, footer, dirty, onDiscard, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
10799
- const { marked, mark } = useOverlayMark(open);
10800
- const guard = useDirtyGuard(dirty ?? marked, onCloseProp, onDiscard);
9980
+ const { marked, pending, mark } = useOverlayMark(open);
9981
+ const guard = useDirtyGuard((dirty ?? marked) || pending, onCloseProp, onDiscard);
10801
9982
  return /* @__PURE__ */ jsx(
10802
9983
  DrawerPanel,
10803
9984
  {
@@ -11105,13 +10286,20 @@ var StackedList = forwardRef(function StackedList2({
11105
10286
  const articles = variant === "articles";
11106
10287
  useWholeListCheck("StackedList", items.length, pager);
11107
10288
  const shown = pagerPart(items, pager);
10289
+ const listRef = useRef(null);
10290
+ useFocusKeptInList(listRef);
10291
+ const setList = useCallback((node) => {
10292
+ listRef.current = node;
10293
+ if (typeof ref === "function") ref(node);
10294
+ else if (ref) ref.current = node;
10295
+ }, [ref]);
11108
10296
  const stackMeta = isPhone && articles;
11109
10297
  const selectable = selectedId !== void 0 || items.some((i) => i.selected !== void 0);
11110
10298
  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";
11111
10299
  const list2 = /* @__PURE__ */ jsx(
11112
10300
  "ul",
11113
10301
  {
11114
- ref,
10302
+ ref: setList,
11115
10303
  "data-ds-list": "",
11116
10304
  className: cn(
11117
10305
  divided && "divide-y-[length:var(--ds-border-width)] divide-[var(--ds-border-subtle)]",
@@ -11221,9 +10409,9 @@ var StackedList = forwardRef(function StackedList2({
11221
10409
 
11222
10410
  // ../../showcase/src/lib/time-days.ts
11223
10411
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
11224
- var DAY_MS2 = 864e5;
10412
+ var DAY_MS = 864e5;
11225
10413
  var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
11226
- var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS2);
10414
+ var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
11227
10415
  function dayOfDate(d) {
11228
10416
  const t = d.getTime();
11229
10417
  if (Number.isNaN(t)) return NaN;
@@ -11232,7 +10420,7 @@ function dayOfDate(d) {
11232
10420
  first.setHours(0, 0, 0, 0);
11233
10421
  if (first.getTime() === t) return day;
11234
10422
  const ms = ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
11235
- return day + ms / DAY_MS2;
10423
+ return day + ms / DAY_MS;
11236
10424
  }
11237
10425
  function dayOf(value) {
11238
10426
  if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
@@ -11242,13 +10430,13 @@ function dayOf(value) {
11242
10430
  if (!m) return dayOfDate(new Date(value));
11243
10431
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
11244
10432
  const day = utcDay(y, mo, d);
11245
- const back = new Date(day * DAY_MS2);
10433
+ const back = new Date(day * DAY_MS);
11246
10434
  return back.getUTCFullYear() === y && back.getUTCMonth() === mo && back.getUTCDate() === d ? day : NaN;
11247
10435
  }
11248
10436
  function dateOfDay(day) {
11249
10437
  const whole2 = Math.floor(day);
11250
- const u = new Date(whole2 * DAY_MS2);
11251
- return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole2) * DAY_MS2));
10438
+ const u = new Date(whole2 * DAY_MS);
10439
+ return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole2) * DAY_MS));
11252
10440
  }
11253
10441
  function isZoned(value) {
11254
10442
  if (value instanceof Date) return !Number.isNaN(value.getTime());
@@ -11311,12 +10499,12 @@ function formatDateRange(start, end, words) {
11311
10499
  if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
11312
10500
  const timed = !isWholeDay(start) || !isWholeDay(until);
11313
10501
  const first = timed ? from : Math.floor(from);
11314
- const last2 = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
11315
- const a = new Date(first * DAY_MS2);
11316
- const b = new Date(last2 * DAY_MS2);
10502
+ const last = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
10503
+ const a = new Date(first * DAY_MS);
10504
+ const b = new Date(last * DAY_MS);
11317
10505
  const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
11318
10506
  const f = dayFormatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
11319
- return first === last2 ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
10507
+ return first === last ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
11320
10508
  }
11321
10509
 
11322
10510
  // ../../showcase/src/lib/time-scale.ts
@@ -11327,7 +10515,7 @@ function periodStart(unit, day, weekStartsOn) {
11327
10515
  const d = Math.floor(day);
11328
10516
  if (unit === "day") return d;
11329
10517
  if (unit === "week") return d - (weekday(d) - weekStartsOn + 7) % 7;
11330
- const u = new Date(d * DAY_MS2);
10518
+ const u = new Date(d * DAY_MS);
11331
10519
  const y = u.getUTCFullYear();
11332
10520
  const m = u.getUTCMonth();
11333
10521
  if (unit === "month") return utcDay(y, m, 1);
@@ -11337,7 +10525,7 @@ function periodStart(unit, day, weekStartsOn) {
11337
10525
  function periodAfter(unit, start) {
11338
10526
  if (unit === "day") return start + 1;
11339
10527
  if (unit === "week") return start + 7;
11340
- const u = new Date(start * DAY_MS2);
10528
+ const u = new Date(start * DAY_MS);
11341
10529
  const y = u.getUTCFullYear();
11342
10530
  const m = u.getUTCMonth();
11343
10531
  if (unit === "month") return utcDay(y, m + 1, 1);
@@ -11345,11 +10533,11 @@ function periodAfter(unit, start) {
11345
10533
  return utcDay(y + 1, 0, 1);
11346
10534
  }
11347
10535
  function format2(day, locale, options) {
11348
- return dayFormatter(locale, options).format(new Date(day * DAY_MS2));
10536
+ return dayFormatter(locale, options).format(new Date(day * DAY_MS));
11349
10537
  }
11350
10538
  var pad22 = (n) => String(n).padStart(2, "0");
11351
10539
  function keyOf(unit, day) {
11352
- const u = new Date(day * DAY_MS2);
10540
+ const u = new Date(day * DAY_MS);
11353
10541
  const y = u.getUTCFullYear();
11354
10542
  const m = u.getUTCMonth();
11355
10543
  if (unit === "year") return String(y);
@@ -11364,7 +10552,7 @@ function labelsOf(unit, role, day, locale) {
11364
10552
  else if (unit === "week") forms = [f({ day: "numeric", month: "short" }), f({ day: "numeric" })];
11365
10553
  else if (unit === "month") {
11366
10554
  forms = role === "major" ? [f({ month: "long", year: "numeric" }), f({ month: "short", year: "numeric" }), f({ month: "short" }), f({ month: "narrow" })] : [f({ month: "long" }), f({ month: "short" }), f({ month: "narrow" })];
11367
- } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS2).getUTCMonth() / 3) + 1}`];
10555
+ } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS).getUTCMonth() / 3) + 1}`];
11368
10556
  else forms = [f({ year: "numeric" }), f({ year: "2-digit" })];
11369
10557
  return forms;
11370
10558
  }
@@ -11436,8 +10624,8 @@ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, p
11436
10624
  function extendedEnd(scale, width) {
11437
10625
  if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale.endDay;
11438
10626
  const unit = MINOR[scale.zoom];
11439
- const last2 = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
11440
- return periodAfter(unit, periodStart(unit, last2, scale.weekStartsOn));
10627
+ const last = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
10628
+ return periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
11441
10629
  }
11442
10630
  function extendTimeScaleTo(scale, endDay) {
11443
10631
  if (!(endDay > scale.endDay)) return scale;
@@ -11896,8 +11084,8 @@ var TimeViewport = forwardRef(function TimeViewport2({
11896
11084
  if (shadeDays && drawn.pxPerDay >= SHADE_MIN) {
11897
11085
  for (let d = drawn.startDay; d < drawn.endDay; d++) {
11898
11086
  if (!shadeDays(calendarDay(d))) continue;
11899
- const last2 = shaded[shaded.length - 1];
11900
- if (last2 && Math.abs(last2.x + last2.width - drawn.x(d)) < 0.5) last2.width += drawn.pxPerDay;
11087
+ const last = shaded[shaded.length - 1];
11088
+ if (last && Math.abs(last.x + last.width - drawn.x(d)) < 0.5) last.width += drawn.pxPerDay;
11901
11089
  else shaded.push({ key: String(d), x: drawn.x(d), width: drawn.pxPerDay });
11902
11090
  }
11903
11091
  }
@@ -12004,11 +11192,11 @@ var TimeViewport = forwardRef(function TimeViewport2({
12004
11192
  /* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "min-w-[4.5rem] text-center text-xs text-[var(--ds-text-secondary)]", children: ZOOM_NAMES[scale.zoom] }),
12005
11193
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "Zoom in", "aria-disabled": !canZoomIn || void 0, className: "aria-disabled:opacity-disabled aria-disabled:cursor-not-allowed", onClick: () => zoom(-1), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 14 }) })
12006
11194
  ] }),
12007
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "Previous period", onClick: () => api.page(-1), children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 14 }) }),
11195
+ /* @__PURE__ */ jsx(PeriodButton, { direction: -1, store, end: Math.max(0, drawn.width - visibleWidth), onPage: (d) => api.page(d) }),
12008
11196
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: todayAtX === null, onClick: () => {
12009
11197
  if (todayAtX !== null) setLeft(todayAtX - visibleWidth / 2);
12010
11198
  }, children: "Today" }),
12011
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "Next period", onClick: () => api.page(1), children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 14 }) }),
11199
+ /* @__PURE__ */ jsx(PeriodButton, { direction: 1, store, end: Math.max(0, drawn.width - visibleWidth), onPage: (d) => api.page(d) }),
12012
11200
  toolbar
12013
11201
  ] })
12014
11202
  );
@@ -12129,6 +11317,13 @@ function TimeBar({ start, end, minWidth = 0, labels, children, className, style,
12129
11317
  }
12130
11318
  );
12131
11319
  }
11320
+ function PeriodButton({ direction, store, end, onPage }) {
11321
+ const left = useSyncExternalStore(store.subscribe, () => store.get().scrollLeft, () => 0);
11322
+ const unavailable = direction < 0 ? left <= 0.5 : end > 0 && left >= end - 0.5;
11323
+ return /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": direction < 0 ? "Previous period" : "Next period", "aria-disabled": unavailable || void 0, className: "aria-disabled:opacity-disabled aria-disabled:cursor-not-allowed", onClick: () => {
11324
+ if (!unavailable) onPage(direction);
11325
+ }, children: /* @__PURE__ */ jsx(Icon, { name: direction < 0 ? "chevron-left" : "chevron-right", size: 14 }) });
11326
+ }
12132
11327
  function LocaleProvider({ lang, acceptLanguage, children }) {
12133
11328
  const value = useMemo(() => localeChoice(lang, acceptLanguage), [lang, acceptLanguage]);
12134
11329
  return /* @__PURE__ */ jsx(LocaleContext.Provider, { value, children });
@@ -12267,7 +11462,7 @@ var NO_DATES = "no dates";
12267
11462
  var KNOCKOUT = 2;
12268
11463
  var EDGE = "border-[length:1px] border-transparent";
12269
11464
  var EDGE_WIDTH = 1;
12270
- var SELECTED = "border-[var(--ds-accent-ink)]";
11465
+ var SELECTED2 = "border-[var(--ds-accent-ink)]";
12271
11466
  var UNDATED_INSET = 16;
12272
11467
  var NO_GROUPS = [];
12273
11468
  var NO_STATUSES = {};
@@ -12314,7 +11509,7 @@ function NoDates({ cue, selected, className, ...props }) {
12314
11509
  className: cn(
12315
11510
  "sticky flex cursor-pointer items-center gap-ds-1 whitespace-nowrap rounded-[var(--ds-radius-sm)] border-[length:1px] border-dashed border-[var(--ds-border-default)] px-ds-1.5 py-ds-0.5 text-xs text-[var(--ds-text-secondary)]",
12316
11511
  FOCUS_RING,
12317
- selected && SELECTED,
11512
+ selected && SELECTED2,
12318
11513
  className
12319
11514
  ),
12320
11515
  style: { insetInlineStart: labelWidth + UNDATED_INSET },
@@ -12341,6 +11536,7 @@ function Gantt({
12341
11536
  describe,
12342
11537
  labelHeader = "Task",
12343
11538
  toolbar,
11539
+ shadeDays,
12344
11540
  "aria-label": ariaLabel,
12345
11541
  className
12346
11542
  }) {
@@ -12570,7 +11766,7 @@ function Gantt({
12570
11766
  ] });
12571
11767
  const shapeBox = shape.kind === "milestone" ? (
12572
11768
  // the diamond draws the target's focus: its outline, which forced colours keep, so the target draws none of its own
12573
- /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: "group absolute grid cursor-pointer place-items-center outline-none", style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px]", GROUP_FOCUS_RING, EDGE, tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) })
11769
+ /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: "group absolute grid cursor-pointer place-items-center outline-none", style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px]", GROUP_FOCUS_RING, EDGE, tone.fill, isSelected && SELECTED2), style: { width: DIAMOND, height: DIAMOND } }) })
12574
11770
  ) : (
12575
11771
  // TimeBar keeps the title in view beside the pinned column while the bar's start is scrolled away
12576
11772
  /* @__PURE__ */ jsx(
@@ -12583,7 +11779,7 @@ function Gantt({
12583
11779
  start: dates.start,
12584
11780
  end: dates.end,
12585
11781
  minWidth: MIN_BAR,
12586
- className: cn("cursor-pointer text-xs font-medium", EDGE, tone.fill, tone.on, isSelected && SELECTED),
11782
+ className: cn("cursor-pointer text-xs font-medium", EDGE, tone.fill, tone.on, isSelected && SELECTED2),
12587
11783
  style: { top: (ROW_HEIGHT2 - BAR_HEIGHT) / 2, bottom: "auto", height: BAR_HEIGHT },
12588
11784
  children: place === "inside" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-gs-clip": "", className: "flex min-w-0 items-center gap-ds-1 overflow-hidden", children: title })
12589
11785
  }
@@ -12665,6 +11861,7 @@ function Gantt({
12665
11861
  hasStatuses && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "min-w-0 flex-1" }),
12666
11862
  legend
12667
11863
  ] }),
11864
+ shadeDays,
12668
11865
  tabStop: false,
12669
11866
  "aria-label": regionLabel,
12670
11867
  className: legendApart ? "h-auto flex-1" : className,
@@ -13011,19 +12208,19 @@ function trackClasses(orientation) {
13011
12208
  "border-b-[length:var(--ds-nav-h-track-border)] border-[var(--ds-border-default)]"
13012
12209
  );
13013
12210
  }
13014
- function useScrollTrack(activeTab, enabled) {
12211
+ function useScrollTrack(stopTab, enabled) {
13015
12212
  const { ref, elRef, measure, fadeProps } = useScrollEdges({ enabled });
13016
12213
  useLayoutEffect(() => {
13017
12214
  if (!enabled) return;
13018
12215
  const el = elRef.current;
13019
- const tab = el?.querySelector('[role="tab"][aria-selected="true"]');
12216
+ const tab = el?.querySelector('[role="tab"][tabindex="0"]');
13020
12217
  if (!el || !tab) return;
13021
12218
  const start = tab.offsetLeft;
13022
12219
  const end = start + tab.offsetWidth;
13023
12220
  if (start < el.scrollLeft + EDGE_FADE) el.scrollLeft = Math.max(0, start - EDGE_FADE);
13024
12221
  else if (end > el.scrollLeft + el.clientWidth - EDGE_FADE) el.scrollLeft = end - el.clientWidth + EDGE_FADE;
13025
12222
  measure();
13026
- }, [activeTab, enabled, measure, elRef]);
12223
+ }, [stopTab, enabled, measure, elRef]);
13027
12224
  useEffect(() => {
13028
12225
  if (!enabled) return;
13029
12226
  const el = elRef.current;
@@ -13104,7 +12301,7 @@ function getTabsFromDOM(el) {
13104
12301
  disabled: btn.getAttribute("aria-disabled") === "true"
13105
12302
  }));
13106
12303
  }
13107
- function handleTabKeyDown(e, currentId, getTabs, orientation, onTabChange) {
12304
+ function handleTabKeyDown(e, currentId, getTabs, orientation, select) {
13108
12305
  const prev = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
13109
12306
  const next = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
13110
12307
  let targetId;
@@ -13132,7 +12329,7 @@ function handleTabKeyDown(e, currentId, getTabs, orientation, onTabChange) {
13132
12329
  }
13133
12330
  e.preventDefault();
13134
12331
  if (targetId && targetId !== currentId) {
13135
- onTabChange(targetId);
12332
+ select?.(targetId);
13136
12333
  document.getElementById(`ds-tab-${targetId}`)?.focus();
13137
12334
  }
13138
12335
  }
@@ -13149,10 +12346,21 @@ function TabButton({
13149
12346
  truncate,
13150
12347
  onKeyDown,
13151
12348
  className,
13152
- hasPanel
12349
+ hasPanel,
12350
+ isTabStop,
12351
+ setFocusedTab
13153
12352
  }) {
13154
12353
  const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !truncate && orientation !== "horizontal" });
13155
12354
  const isActive2 = activeTab === id;
12355
+ const forget = useCallback(() => setFocusedTab((f) => f === id ? null : f), [setFocusedTab, id]);
12356
+ useEffect(() => forget, [forget]);
12357
+ const focusTracking = {
12358
+ tabIndex: isTabStop ? 0 : -1,
12359
+ onFocus: () => setFocusedTab(id),
12360
+ onBlur: (e) => {
12361
+ if (!e.currentTarget.closest('[role="tablist"]')?.contains(e.relatedTarget)) forget();
12362
+ }
12363
+ };
13156
12364
  const tabId = `ds-tab-${id}`;
13157
12365
  const panelId = hasPanel === false ? void 0 : `ds-tabpanel-${id}`;
13158
12366
  const badgeBg = orientation === "vertical" ? "bg-[var(--ds-nav-v-badge-bg)]" : "bg-[var(--ds-nav-h-badge-bg)]";
@@ -13171,7 +12379,7 @@ function TabButton({
13171
12379
  "aria-selected": isActive2,
13172
12380
  "aria-controls": panelId,
13173
12381
  "aria-disabled": disabled || void 0,
13174
- tabIndex: isActive2 ? 0 : -1,
12382
+ ...focusTracking,
13175
12383
  onClick: disabled ? void 0 : () => onTabChange(id),
13176
12384
  onKeyDown,
13177
12385
  className: cn(
@@ -13207,7 +12415,7 @@ function TabButton({
13207
12415
  "aria-selected": isActive2,
13208
12416
  "aria-controls": panelId,
13209
12417
  "aria-disabled": disabled || void 0,
13210
- tabIndex: isActive2 ? 0 : -1,
12418
+ ...focusTracking,
13211
12419
  onClick: disabled ? void 0 : () => onTabChange(id),
13212
12420
  onKeyDown,
13213
12421
  className: cn(
@@ -13242,8 +12450,8 @@ function TabButton({
13242
12450
  return btn;
13243
12451
  }
13244
12452
  function TabsList({ children, className }) {
13245
- const { orientation, fullWidth, overflow, activeTab } = useTabsCtx();
13246
- const track = useScrollTrack(activeTab, orientation === "horizontal" && overflow === "scroll");
12453
+ const { orientation, fullWidth, overflow, stopTab } = useTabsCtx();
12454
+ const track = useScrollTrack(stopTab, orientation === "horizontal" && overflow === "scroll");
13247
12455
  if (orientation === "vertical") {
13248
12456
  return /* @__PURE__ */ jsx(
13249
12457
  "div",
@@ -13258,7 +12466,7 @@ function TabsList({ children, className }) {
13258
12466
  return /* @__PURE__ */ jsx(TabStrip, { overflow, fullWidth, track, className, children });
13259
12467
  }
13260
12468
  function TabsTrigger({ value, children, count, icon, disabled, truncate, className }) {
13261
- const { activeTab, onTabChange, orientation, fullWidth, overflow } = useTabsCtx();
12469
+ const { activeTab, onTabChange, orientation, fullWidth, overflow, activation, stopTab, setFocusedTab } = useTabsCtx();
13262
12470
  return /* @__PURE__ */ jsx(
13263
12471
  TabButton,
13264
12472
  {
@@ -13272,7 +12480,9 @@ function TabsTrigger({ value, children, count, icon, disabled, truncate, classNa
13272
12480
  activeTab,
13273
12481
  onTabChange,
13274
12482
  fullWidth,
13275
- onKeyDown: (e) => handleTabKeyDown(e, value, () => getTabsFromDOM(e.currentTarget), orientation, onTabChange),
12483
+ isTabStop: value === stopTab,
12484
+ setFocusedTab,
12485
+ onKeyDown: (e) => handleTabKeyDown(e, value, () => getTabsFromDOM(e.currentTarget), orientation, activation === "automatic" ? onTabChange : void 0),
13276
12486
  className
13277
12487
  }
13278
12488
  );
@@ -13299,15 +12509,20 @@ function TabsRoot(props) {
13299
12509
  orientation = "horizontal",
13300
12510
  fullWidth = false,
13301
12511
  overflow = "scroll",
12512
+ activation = "automatic",
13302
12513
  className
13303
12514
  } = props;
13304
12515
  const isMobile = useBreakpoint("md");
13305
12516
  const effectiveOrientation = orientation === "responsive" ? "vertical" : orientation;
13306
12517
  const arrayApi = "tabs" in props && !!props.tabs;
13307
12518
  const arrayOverflow = arrayApi && props.tabs.some((t) => t.truncate) ? "truncate" : overflow;
13308
- const track = useScrollTrack(activeTab, arrayApi && effectiveOrientation === "horizontal" && arrayOverflow === "scroll");
12519
+ const [focusedTab, setFocusedTab] = useState(null);
12520
+ const stopTab = focusedTab ?? activeTab;
12521
+ const track = useScrollTrack(stopTab, arrayApi && effectiveOrientation === "horizontal" && arrayOverflow === "scroll");
13309
12522
  if ("tabs" in props && props.tabs) {
13310
12523
  const { tabs, mobile = "scroll" } = props;
12524
+ const stopIndex = rovingTabStop(tabs, (t) => t.id === stopTab, (t) => !!t.disabled && t.id !== stopTab);
12525
+ const select = activation === "automatic" ? onTabChange : void 0;
13311
12526
  const collapseToDropdown = isMobile && (orientation === "responsive" || mobile === "dropdown");
13312
12527
  if (collapseToDropdown) {
13313
12528
  return (
@@ -13342,14 +12557,14 @@ function TabsRoot(props) {
13342
12557
  children: tab.group
13343
12558
  }
13344
12559
  ),
13345
- /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, onTabChange) })
12560
+ /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, isTabStop: i === stopIndex, setFocusedTab, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, select) })
13346
12561
  ] }, tab.id))
13347
12562
  }
13348
12563
  );
13349
12564
  }
13350
- return /* @__PURE__ */ jsx(TabStrip, { overflow: arrayOverflow, fullWidth, track, className, children: tabs.map((tab) => /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, onTabChange) }, tab.id)) });
12565
+ return /* @__PURE__ */ jsx(TabStrip, { overflow: arrayOverflow, fullWidth, track, className, children: tabs.map((tab, i) => /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, isTabStop: i === stopIndex, setFocusedTab, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, select) }, tab.id)) });
13351
12566
  }
13352
- return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { activeTab, onTabChange, orientation: effectiveOrientation, fullWidth, overflow }, children: /* @__PURE__ */ jsx("div", { className, children: props.children }) });
12567
+ return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { activeTab, onTabChange, orientation: effectiveOrientation, fullWidth, overflow, activation, stopTab, setFocusedTab }, children: /* @__PURE__ */ jsx("div", { className, children: props.children }) });
13353
12568
  }
13354
12569
  var Tabs = Object.assign(TabsRoot, {
13355
12570
  List: TabsList,
@@ -13900,7 +13115,7 @@ var Sidebar = forwardRef(function Sidebar2({
13900
13115
  ]
13901
13116
  }
13902
13117
  );
13903
- return collapsed ? /* @__PURE__ */ jsx(AutoTooltip, { content: "Expand sidebar", side: "right", delay: 100, container: portalEl ?? void 0, children: collapseButton }) : collapseButton;
13118
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: "Expand sidebar", side: "right", delay: 100, container: portalEl ?? void 0, disabled: !collapsed, children: collapseButton });
13904
13119
  })(),
13905
13120
  footer && /* @__PURE__ */ jsx(
13906
13121
  "div",
@@ -14151,6 +13366,8 @@ function SidebarNav({
14151
13366
  item.onClick?.();
14152
13367
  onItemClick?.(item, index);
14153
13368
  },
13369
+ "aria-label": collapsed && isRoot ? item.label : void 0,
13370
+ "aria-current": item.active ? "page" : void 0,
14154
13371
  className: cn(
14155
13372
  "flex items-center gap-ds-2 transition-colors cursor-pointer",
14156
13373
  FOCUS_RING,
@@ -14331,7 +13548,7 @@ function SidebarNav({
14331
13548
  {
14332
13549
  className: cn("flex flex-col", className),
14333
13550
  style: {
14334
- paddingTop: isCompact ? "var(--ds-space-1)" : dropTopInset ? 0 : "var(--ds-space-3)",
13551
+ paddingTop: isCompact ? "var(--ds-space-1)" : dropTopInset ? `${FOCUS_ROOM}px` : "var(--ds-space-3)",
14335
13552
  paddingRight: isCompact ? "var(--ds-space-1)" : "var(--ds-space-2)",
14336
13553
  paddingBottom: isCompact ? "var(--ds-space-1)" : "var(--ds-space-3)",
14337
13554
  paddingLeft: isCompact ? "var(--ds-space-1)" : "var(--ds-space-2)",
@@ -15105,6 +14322,7 @@ function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
15105
14322
  {
15106
14323
  type: "button",
15107
14324
  role: "menuitem",
14325
+ "aria-current": item.active ? "page" : void 0,
15108
14326
  onClick: () => {
15109
14327
  onItemClick?.(item, maxVisible - 1 + i);
15110
14328
  closeOverflow();
@@ -15132,26 +14350,74 @@ function BottomNav({ items, onItemClick, maxVisible = 5, className, ref }) {
15132
14350
  );
15133
14351
  }
15134
14352
  function NavButton({ item, onClick }) {
15135
- return /* @__PURE__ */ jsxs(
15136
- "button",
15137
- {
15138
- type: "button",
15139
- onClick,
15140
- className: cn(
15141
- "flex flex-col items-center justify-center gap-ds-0.5 flex-1 h-full",
15142
- "text-2xs font-medium transition-colors cursor-pointer",
15143
- // flush with the window's edge (the bar's foot and its ends): the outline sits inside, where it shows whole (D8)
15144
- FOCUS_INSET,
15145
- item.active ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
15146
- ),
15147
- children: [
15148
- /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 20 }),
15149
- /* @__PURE__ */ jsx("span", { children: item.label })
15150
- ]
15151
- }
14353
+ return (
14354
+ // the current page says so, not by its colour alone (Sprint 25.29 item 17)
14355
+ /* @__PURE__ */ jsxs(
14356
+ "button",
14357
+ {
14358
+ type: "button",
14359
+ onClick,
14360
+ "aria-current": item.active ? "page" : void 0,
14361
+ className: cn(
14362
+ "flex flex-col items-center justify-center gap-ds-0.5 flex-1 h-full",
14363
+ "text-2xs font-medium transition-colors cursor-pointer",
14364
+ // flush with the window's edge (the bar's foot and its ends): the outline sits inside, where it shows whole (D8)
14365
+ FOCUS_INSET,
14366
+ item.active ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"
14367
+ ),
14368
+ children: [
14369
+ /* @__PURE__ */ jsx(Icon, { name: item.icon, size: 20 }),
14370
+ /* @__PURE__ */ jsx("span", { children: item.label })
14371
+ ]
14372
+ }
14373
+ )
15152
14374
  );
15153
14375
  }
15154
14376
 
14377
+ // ../../showcase/src/lib/scroll-padding.ts
14378
+ var registry = /* @__PURE__ */ new WeakMap();
14379
+ function apply(scroller, claims) {
14380
+ const px = Math.max(0, ...claims.sizes.values());
14381
+ if (px) {
14382
+ scroller.style.setProperty("scroll-padding-top", claims.floor ? `max(${claims.floor}, ${px}px)` : `${px}px`);
14383
+ return;
14384
+ }
14385
+ if (claims.inline) scroller.style.setProperty("scroll-padding-top", claims.inline, claims.priority);
14386
+ else scroller.style.removeProperty("scroll-padding-top");
14387
+ }
14388
+ function claimScrollPaddingTop(scroller) {
14389
+ let claims = registry.get(scroller);
14390
+ if (!claims) {
14391
+ const own = getComputedStyle(scroller).getPropertyValue("scroll-padding-top").trim();
14392
+ claims = {
14393
+ inline: scroller.style.getPropertyValue("scroll-padding-top"),
14394
+ priority: scroller.style.getPropertyPriority("scroll-padding-top"),
14395
+ floor: own && own !== "auto" && parseFloat(own) !== 0 ? own : null,
14396
+ sizes: /* @__PURE__ */ new Map(),
14397
+ holders: 0
14398
+ };
14399
+ registry.set(scroller, claims);
14400
+ }
14401
+ const held = claims;
14402
+ held.holders++;
14403
+ const token = /* @__PURE__ */ Symbol("scroll-padding-claim");
14404
+ let released = false;
14405
+ return {
14406
+ set(px) {
14407
+ if (released) return;
14408
+ held.sizes.set(token, Math.max(0, Math.ceil(px)));
14409
+ apply(scroller, held);
14410
+ },
14411
+ release() {
14412
+ if (released) return;
14413
+ released = true;
14414
+ held.sizes.delete(token);
14415
+ apply(scroller, held);
14416
+ if (--held.holders === 0) registry.delete(scroller);
14417
+ }
14418
+ };
14419
+ }
14420
+
15155
14421
  // ../../showcase/src/lib/shell-theme.ts
15156
14422
  var PAGE_HEADER_MARK = /* @__PURE__ */ Symbol.for("gridsmith.PageHeader");
15157
14423
  function resolvePageHeader(theme, props, facts) {
@@ -16075,20 +15341,38 @@ function SnappingHeader({
16075
15341
  } : setStickyRef;
16076
15342
  useLayoutEffect(() => {
16077
15343
  const report = container?.setPinnedHeight;
16078
- if (!report) return;
16079
15344
  const el = stickyRef.current;
16080
15345
  if (!(pinned && collapsed) || !el) {
16081
- report(null);
15346
+ report?.(null);
16082
15347
  return;
16083
15348
  }
16084
- const sync = () => report(el.offsetHeight);
15349
+ const doc = el.ownerDocument;
15350
+ const claim = claimScrollPaddingTop(findScrollParent(el) ?? doc.documentElement);
15351
+ let inBar = el.contains(doc.activeElement);
15352
+ const sync = () => {
15353
+ report?.(el.offsetHeight);
15354
+ claim.set(inBar ? 0 : (parseFloat(getComputedStyle(el).top) || 0) + el.offsetHeight + FOCUS_ROOM);
15355
+ };
15356
+ const follow2 = (to) => {
15357
+ const now = to instanceof Node && el.contains(to);
15358
+ if (now !== inBar) {
15359
+ inBar = now;
15360
+ sync();
15361
+ }
15362
+ };
15363
+ const onFocusIn = (e) => follow2(e.target);
15364
+ const onFocusOut = (e) => follow2(e.relatedTarget);
16085
15365
  sync();
16086
- if (typeof ResizeObserver === "undefined") return () => report(null);
16087
- const obs = new ResizeObserver(sync);
16088
- obs.observe(el);
15366
+ doc.addEventListener("focusin", onFocusIn, true);
15367
+ doc.addEventListener("focusout", onFocusOut, true);
15368
+ const obs = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(sync);
15369
+ obs?.observe(el);
16089
15370
  return () => {
16090
- obs.disconnect();
16091
- report(null);
15371
+ obs?.disconnect();
15372
+ doc.removeEventListener("focusin", onFocusIn, true);
15373
+ doc.removeEventListener("focusout", onFocusOut, true);
15374
+ report?.(null);
15375
+ claim.release();
16092
15376
  };
16093
15377
  }, [container?.setPinnedHeight, pinned, collapsed]);
16094
15378
  const sentinelRef = useRef(null);
@@ -17345,68 +16629,6 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
17345
16629
  container ?? document.body
17346
16630
  );
17347
16631
  }
17348
- function HoverCard({
17349
- content,
17350
- children,
17351
- side = "bottom",
17352
- delay = 300,
17353
- closeDelay = 200,
17354
- className,
17355
- container
17356
- }) {
17357
- const [open, setOpen] = useState(false);
17358
- const triggerRef = useRef(null);
17359
- const floatingRef = useRef(null);
17360
- const openTimeout = useRef(void 0);
17361
- const closeTimeout = useRef(void 0);
17362
- const pos = usePosition(triggerRef, floatingRef, open, { side, offset: 8, flip: true });
17363
- const show = useCallback(() => {
17364
- clearTimeout(closeTimeout.current);
17365
- openTimeout.current = setTimeout(() => setOpen(true), delay);
17366
- }, [delay]);
17367
- const hide = useCallback(() => {
17368
- clearTimeout(openTimeout.current);
17369
- closeTimeout.current = setTimeout(() => setOpen(false), closeDelay);
17370
- }, [closeDelay]);
17371
- return /* @__PURE__ */ jsxs(Fragment, { children: [
17372
- /* @__PURE__ */ jsx(
17373
- Slot,
17374
- {
17375
- ref: triggerRef,
17376
- onMouseEnter: show,
17377
- onMouseLeave: hide,
17378
- onFocus: show,
17379
- onBlur: hide,
17380
- children
17381
- }
17382
- ),
17383
- open && createPortal(
17384
- /* @__PURE__ */ jsx(
17385
- "div",
17386
- {
17387
- ref: floatingRef,
17388
- onMouseEnter: () => {
17389
- clearTimeout(closeTimeout.current);
17390
- },
17391
- onMouseLeave: hide,
17392
- className: cn(
17393
- "fixed z-[var(--ds-z-popover)]",
17394
- "w-72 p-ds-4",
17395
- "bg-[var(--ds-bg-surface-elevated)] text-[var(--ds-text-primary)]",
17396
- GLASS_DROPDOWN,
17397
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
17398
- "rounded-[var(--ds-radius-md)]",
17399
- "animate-in fade-in-0 zoom-in-95",
17400
- className
17401
- ),
17402
- style: { top: pos.top, left: pos.left },
17403
- children: /* @__PURE__ */ jsx(RowsCtx, { value: false, children: content })
17404
- }
17405
- ),
17406
- container ?? document.body
17407
- )
17408
- ] });
17409
- }
17410
16632
  function ContextMenu({ items, children, className, ref, container }) {
17411
16633
  const [open, setOpen] = useState(false);
17412
16634
  const [pos, setPos] = useState({ x: 0, y: 0 });
@@ -17611,7 +16833,8 @@ function NotificationCenter({
17611
16833
  document.removeEventListener("mousedown", onClick);
17612
16834
  };
17613
16835
  }, [open, setOpen, isTopLayer, returnFocus]);
17614
- const grouped = groupByDate(filteredNotifications);
16836
+ const today = useCurrentDay();
16837
+ const grouped = groupByDay(filteredNotifications, today);
17615
16838
  if (variant === "page") {
17616
16839
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full", className), children: [
17617
16840
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-ds-4", children: [
@@ -17661,8 +16884,8 @@ function NotificationCenter({
17661
16884
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17662
16885
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: filter === "unread" ? "No unread notifications" : "No notifications" })
17663
16886
  ] }),
17664
- grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { className: "mb-ds-2", children: [
17665
- /* @__PURE__ */ jsx("p", { className: "px-ds-1 pt-ds-3 pb-ds-1 text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)]", children: label }),
16887
+ /* @__PURE__ */ jsx(RowsCtx, { value: true, children: grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { className: "mb-ds-2", children: [
16888
+ label && /* @__PURE__ */ jsx("p", { className: "px-ds-1 pt-ds-3 pb-ds-1 text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)]", children: label }),
17666
16889
  items.map((n) => /* @__PURE__ */ jsxs(
17667
16890
  "div",
17668
16891
  {
@@ -17688,7 +16911,7 @@ function NotificationCenter({
17688
16911
  /* @__PURE__ */ jsx(TruncTitle, { children: n.title })
17689
16912
  ] }),
17690
16913
  n.description && /* @__PURE__ */ jsx(ClampedDesc, { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: n.description }),
17691
- /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: n.timestamp })
16914
+ /* @__PURE__ */ jsx(NotificationTime, { timestamp: n.timestamp })
17692
16915
  ] }),
17693
16916
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-1 shrink-0", children: [
17694
16917
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
@@ -17716,7 +16939,7 @@ function NotificationCenter({
17716
16939
  },
17717
16940
  n.id
17718
16941
  ))
17719
- ] }, label))
16942
+ ] }, label ?? "rows")) })
17720
16943
  ] });
17721
16944
  }
17722
16945
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative inline-flex", className), children: [
@@ -17775,8 +16998,8 @@ function NotificationCenter({
17775
16998
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17776
16999
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: "No notifications" })
17777
17000
  ] }),
17778
- grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { children: [
17779
- /* @__PURE__ */ jsx("p", { className: "px-ds-4 pt-ds-3 pb-ds-1 text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)]", children: label }),
17001
+ /* @__PURE__ */ jsx(RowsCtx, { value: true, children: grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { children: [
17002
+ label && /* @__PURE__ */ jsx("p", { className: "px-ds-4 pt-ds-3 pb-ds-1 text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)]", children: label }),
17780
17003
  items.map((n) => /* @__PURE__ */ jsxs(
17781
17004
  "div",
17782
17005
  {
@@ -17802,7 +17025,7 @@ function NotificationCenter({
17802
17025
  /* @__PURE__ */ jsx(TruncTitle, { children: n.title })
17803
17026
  ] }),
17804
17027
  n.description && /* @__PURE__ */ jsx(ClampedDesc, { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: n.description }),
17805
- /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: n.timestamp })
17028
+ /* @__PURE__ */ jsx(NotificationTime, { timestamp: n.timestamp })
17806
17029
  ] }),
17807
17030
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-1 shrink-0", children: [
17808
17031
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
@@ -17830,7 +17053,7 @@ function NotificationCenter({
17830
17053
  },
17831
17054
  n.id
17832
17055
  ))
17833
- ] }, label))
17056
+ ] }, label ?? "rows")) })
17834
17057
  ] })
17835
17058
  ]
17836
17059
  }
@@ -17839,27 +17062,33 @@ function NotificationCenter({
17839
17062
  )
17840
17063
  ] });
17841
17064
  }
17842
- function groupByDate(notifications) {
17843
- const today = /* @__PURE__ */ new Date();
17844
- today.setHours(0, 0, 0, 0);
17845
- const yesterday = new Date(today);
17846
- yesterday.setDate(yesterday.getDate() - 1);
17847
- const groups = {};
17848
- const order = [];
17065
+ function NotificationTime({ timestamp }) {
17066
+ const at = datedMillis(timestamp);
17067
+ return /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: typeof timestamp === "string" && !Number.isFinite(at) ? timestamp : /* @__PURE__ */ jsx(RelativeTime, { time: at }) });
17068
+ }
17069
+ function useCurrentDay() {
17070
+ const today = useToday();
17071
+ const [redraws, redraw] = useReducer((n) => n + 1, 0);
17072
+ const tomorrow = today && new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1).getTime();
17073
+ useEffect(() => {
17074
+ if (tomorrow === null) return;
17075
+ const timer = setTimeout(redraw, tomorrow - Date.now() + 1e3);
17076
+ return () => clearTimeout(timer);
17077
+ }, [tomorrow, redraws]);
17078
+ return today;
17079
+ }
17080
+ var DAY_GROUPS = ["Today", "Yesterday", "Earlier"];
17081
+ function groupByDay(notifications, today) {
17082
+ if (!today) return notifications.length ? [{ label: null, items: notifications }] : [];
17083
+ const todayStarts = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
17084
+ const yesterdayStarts = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1).getTime();
17085
+ const groups = new Map(DAY_GROUPS.map((label) => [label, []]));
17849
17086
  for (const n of notifications) {
17850
- let label;
17851
- if (n.timestamp.toLowerCase().includes("ago") || n.timestamp.toLowerCase().includes("just now")) {
17852
- label = "Today";
17853
- } else {
17854
- label = "Earlier";
17855
- }
17856
- if (!groups[label]) {
17857
- groups[label] = [];
17858
- order.push(label);
17859
- }
17860
- groups[label].push(n);
17087
+ const at = datedMillis(n.timestamp);
17088
+ const label = !Number.isFinite(at) || at < yesterdayStarts ? "Earlier" : at < todayStarts ? "Yesterday" : "Today";
17089
+ groups.get(label).push(n);
17861
17090
  }
17862
- return order.map((label) => ({ label, items: groups[label] }));
17091
+ return DAY_GROUPS.filter((label) => groups.get(label).length > 0).map((label) => ({ label, items: groups.get(label) }));
17863
17092
  }
17864
17093
  function Tour({ steps, open, onClose, onComplete, container }) {
17865
17094
  const [current, setCurrent] = useState(0);
@@ -18254,8 +17483,8 @@ function useFlowSteps(options) {
18254
17483
  go(target, { replace: true });
18255
17484
  }, [key, stored, valid, furthest, href, go, at]);
18256
17485
  useEffect(() => {
18257
- if (key && ready && stored?.current !== step) update(key, (v) => ({ ...v, current: step }));
18258
- }, [key, ready, stored?.current, step]);
17486
+ if (key && ready) update(key, (v) => v.current === step ? v : { ...v, current: step });
17487
+ }, [key, ready, step]);
18259
17488
  useEffect(() => {
18260
17489
  if (!key || !ready) return;
18261
17490
  const store = storeFor(key);
@@ -18659,57 +17888,7 @@ function DangerZone({ title = "Danger zone", description, children, className })
18659
17888
  )
18660
17889
  ] });
18661
17890
  }
18662
- var lineClampClasses = {
18663
- 1: "truncate",
18664
- 2: "line-clamp-2",
18665
- 3: "line-clamp-3",
18666
- 4: "line-clamp-4",
18667
- 5: "line-clamp-5",
18668
- 6: "line-clamp-6"
18669
- };
18670
- function getTruncationClass(lines) {
18671
- return lineClampClasses[lines] || `line-clamp-[${lines}]`;
18672
- }
18673
- var TextOverflow = forwardRef(function TextOverflow2({
18674
- lines = 1,
18675
- tooltip = true,
18676
- tooltipContent,
18677
- expandMode = "tooltip",
18678
- as = "div",
18679
- children,
18680
- className
18681
- }, forwardedRef) {
18682
- const { ref: overflowRef, isTruncated } = useTextOverflow({ lines });
18683
- const truncClass = getTruncationClass(lines);
18684
- const Tag3 = as;
18685
- const content = /* @__PURE__ */ jsx(
18686
- Tag3,
18687
- {
18688
- ref: (node) => {
18689
- overflowRef(node);
18690
- if (typeof forwardedRef === "function") forwardedRef(node);
18691
- else if (forwardedRef) forwardedRef.current = node;
18692
- },
18693
- className: cn(truncClass, "min-w-0", className),
18694
- children
18695
- }
18696
- );
18697
- if (!tooltip || !isTruncated) {
18698
- return content;
18699
- }
18700
- const resolvedTooltipContent = tooltipContent ?? children;
18701
- if (expandMode === "hovercard") {
18702
- return /* @__PURE__ */ jsx(
18703
- HoverCard,
18704
- {
18705
- content: /* @__PURE__ */ jsx("div", { className: "max-w-[320px] text-sm text-[var(--ds-text-primary)]", children: resolvedTooltipContent }),
18706
- children: content
18707
- }
18708
- );
18709
- }
18710
- return /* @__PURE__ */ jsx(AutoTooltip, { content: resolvedTooltipContent, children: content });
18711
- });
18712
17891
 
18713
- 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 };
17892
+ export { Accordion, ActionPanel, ActionPanelGroup, AddAnother, Alert, AsidePanel, AvatarGroup, BottomNav, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DangerZone, DataList, DateRangePicker, DescriptionList, Dialog, Dock, EmptyState, EntityCell, FileUpload, FlowSaved, FormWrapper, Gantt, 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, 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 };
18714
17893
  //# sourceMappingURL=index.js.map
18715
17894
  //# sourceMappingURL=index.js.map