gridsmith-ui 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.d.ts +5 -0
  5. package/dist/charts.js +5 -3
  6. package/dist/charts.js.map +1 -1
  7. package/dist/chat.js +5 -5
  8. package/dist/{chunk-EIWXVICU.js → chunk-6A5RHW2W.js} +3 -3
  9. package/dist/{chunk-EIWXVICU.js.map → chunk-6A5RHW2W.js.map} +1 -1
  10. package/dist/{chunk-XVB55M3N.js → chunk-72SDAX54.js} +2 -2
  11. package/dist/chunk-72SDAX54.js.map +1 -0
  12. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  13. package/dist/chunk-7QLXYRPD.js.map +1 -0
  14. package/dist/chunk-F2BNJSON.js +1348 -0
  15. package/dist/chunk-F2BNJSON.js.map +1 -0
  16. package/dist/{chunk-GXMTS3YD.js → chunk-GQVBZ4GW.js} +54 -16
  17. package/dist/chunk-GQVBZ4GW.js.map +1 -0
  18. package/dist/{chunk-2ROVDVLZ.js → chunk-INGF2CPW.js} +4 -23
  19. package/dist/chunk-INGF2CPW.js.map +1 -0
  20. package/dist/{chunk-66JJCQU3.js → chunk-JTU7OBYJ.js} +5 -3
  21. package/dist/chunk-JTU7OBYJ.js.map +1 -0
  22. package/dist/{chunk-DQCHCLEJ.js → chunk-KTPGMJUK.js} +6 -6
  23. package/dist/{chunk-DQCHCLEJ.js.map → chunk-KTPGMJUK.js.map} +1 -1
  24. package/dist/{chunk-TJEV7YAY.js → chunk-L6HWACK7.js} +7 -7
  25. package/dist/chunk-L6HWACK7.js.map +1 -0
  26. package/dist/chunk-LJYSVZMI.js +25 -0
  27. package/dist/chunk-LJYSVZMI.js.map +1 -0
  28. package/dist/{chunk-YUTF2X6B.js → chunk-NI5542ZM.js} +42 -38
  29. package/dist/chunk-NI5542ZM.js.map +1 -0
  30. package/dist/chunk-NKUCS67T.js +13 -0
  31. package/dist/chunk-NKUCS67T.js.map +1 -0
  32. package/dist/{chunk-TRDXWHD2.js → chunk-NLXQ323J.js} +6 -6
  33. package/dist/chunk-NLXQ323J.js.map +1 -0
  34. package/dist/{chunk-ZI3OQDET.js → chunk-OIOII5L6.js} +93 -4
  35. package/dist/chunk-OIOII5L6.js.map +1 -0
  36. package/dist/{chunk-R4GXKH4U.js → chunk-Q36JVJX4.js} +52 -93
  37. package/dist/chunk-Q36JVJX4.js.map +1 -0
  38. package/dist/{chunk-FD4YED7O.js → chunk-RHWTMTU5.js} +3 -3
  39. package/dist/{chunk-FD4YED7O.js.map → chunk-RHWTMTU5.js.map} +1 -1
  40. package/dist/chunk-TYNDXMBV.js +105 -0
  41. package/dist/chunk-TYNDXMBV.js.map +1 -0
  42. package/dist/{chunk-OPFJEL3Z.js → chunk-UNS4TGGK.js} +110 -20
  43. package/dist/chunk-UNS4TGGK.js.map +1 -0
  44. package/dist/chunk-Z4MCD73B.js +316 -0
  45. package/dist/chunk-Z4MCD73B.js.map +1 -0
  46. package/dist/{chunk-CRQ6KCRD.js → chunk-ZHUVLBCC.js} +7 -4
  47. package/dist/chunk-ZHUVLBCC.js.map +1 -0
  48. package/dist/data-grid.d.ts +26 -7
  49. package/dist/data-grid.js +179 -57
  50. package/dist/data-grid.js.map +1 -1
  51. package/dist/editor-mentions.js +3 -3
  52. package/dist/editor.js +5 -5
  53. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  54. package/dist/feather-XFFEFLTV.js.map +1 -0
  55. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  56. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  57. package/dist/icons.d.ts +10 -1
  58. package/dist/icons.js +1 -1
  59. package/dist/index.d.ts +174 -50
  60. package/dist/index.js +810 -1731
  61. package/dist/index.js.map +1 -1
  62. package/dist/kanban.d.ts +26 -8
  63. package/dist/kanban.js +203 -79
  64. package/dist/kanban.js.map +1 -1
  65. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  66. package/dist/lucide-JOORDTO6.js.map +1 -0
  67. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  68. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  69. package/dist/resizable.js +12 -11
  70. package/dist/resizable.js.map +1 -1
  71. package/dist/roots.css +14 -8
  72. package/dist/{table-utils-BkjNEWts.d.ts → table-utils-BXkP-2Hx.d.ts} +8 -5
  73. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  74. package/dist/tabler-YGETLYRU.js.map +1 -0
  75. package/dist/tokens.css +14 -0
  76. package/dist/{use-pagination-BxcRlNQG.d.ts → use-pagination-QykGSREv.d.ts} +7 -0
  77. package/dist/utils.js.map +1 -1
  78. package/dist/virtualized.d.ts +1 -1
  79. package/dist/virtualized.js +10 -9
  80. package/dist/virtualized.js.map +1 -1
  81. package/dist-static/tokens.css +14 -0
  82. package/package.json +1 -1
  83. package/dist/chunk-2ROVDVLZ.js.map +0 -1
  84. package/dist/chunk-66JJCQU3.js.map +0 -1
  85. package/dist/chunk-BRAXJEEP.js +0 -117
  86. package/dist/chunk-BRAXJEEP.js.map +0 -1
  87. package/dist/chunk-CRQ6KCRD.js.map +0 -1
  88. package/dist/chunk-GXMTS3YD.js.map +0 -1
  89. package/dist/chunk-OPFJEL3Z.js.map +0 -1
  90. package/dist/chunk-OYNUU346.js.map +0 -1
  91. package/dist/chunk-QM4SJKWV.js +0 -51
  92. package/dist/chunk-QM4SJKWV.js.map +0 -1
  93. package/dist/chunk-R4GXKH4U.js.map +0 -1
  94. package/dist/chunk-TJEV7YAY.js.map +0 -1
  95. package/dist/chunk-TRDXWHD2.js.map +0 -1
  96. package/dist/chunk-XVB55M3N.js.map +0 -1
  97. package/dist/chunk-YUTF2X6B.js.map +0 -1
  98. package/dist/chunk-ZI3OQDET.js.map +0 -1
  99. package/dist/feather-FNBQ74RR.js.map +0 -1
  100. package/dist/heroicons-DODCSYWD.js.map +0 -1
  101. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  102. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  103. package/dist/tabler-D3VYTSGI.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,172 +1,51 @@
1
1
  "use client";
2
- import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, defaultMaxWidth } from './chunk-OPFJEL3Z.js';
3
- export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-OPFJEL3Z.js';
4
- import { Skeleton, TITLE_TEXT, useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-2ROVDVLZ.js';
5
- export { Skeleton, useFormatLocale, useHydrated } from './chunk-2ROVDVLZ.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-UNS4TGGK.js';
3
+ export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-UNS4TGGK.js';
4
+ import { Skeleton, TITLE_TEXT } from './chunk-INGF2CPW.js';
5
+ export { Skeleton } from './chunk-INGF2CPW.js';
6
6
  import { useScrollEdges, EDGE_FADE } from './chunk-3BRQRKDO.js';
7
- import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-EIWXVICU.js';
8
- export { Avatar } from './chunk-EIWXVICU.js';
9
- import { Link } from './chunk-DQCHCLEJ.js';
10
- export { CodeBlock, Divider, Link } from './chunk-DQCHCLEJ.js';
11
- import { useFillHeight, FillContext, useKeyboardScroll } from './chunk-FD4YED7O.js';
12
- import { Badge } from './chunk-TRDXWHD2.js';
13
- export { Badge } from './chunk-TRDXWHD2.js';
14
- import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-BRAXJEEP.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-YUTF2X6B.js';
16
- export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-YUTF2X6B.js';
17
- import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-R4GXKH4U.js';
18
- export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-R4GXKH4U.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-6A5RHW2W.js';
8
+ export { Avatar } from './chunk-6A5RHW2W.js';
9
+ import { Link } from './chunk-KTPGMJUK.js';
10
+ export { CodeBlock, Divider, Link } from './chunk-KTPGMJUK.js';
11
+ import { DropdownMenu } from './chunk-Z4MCD73B.js';
12
+ export { DropdownMenu } from './chunk-Z4MCD73B.js';
13
+ import { useFillHeight, FillContext, useKeyboardScroll } from './chunk-RHWTMTU5.js';
14
+ import { Badge } from './chunk-NLXQ323J.js';
15
+ export { Badge } from './chunk-NLXQ323J.js';
16
+ 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-F2BNJSON.js';
17
+ export { Checkbox, CheckboxGroup, CheckboxGroupItem, DatePicker, HoverCard, TextOverflow } from './chunk-F2BNJSON.js';
18
+ 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-NI5542ZM.js';
19
+ export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-NI5542ZM.js';
20
+ import { useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, datedMillis, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-TYNDXMBV.js';
21
+ export { PreferencesContext, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-TYNDXMBV.js';
22
+ import { useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-LJYSVZMI.js';
23
+ export { useFormatLocale, useHydrated } from './chunk-LJYSVZMI.js';
24
+ import { formatLocale, localeChoice } from './chunk-7QLXYRPD.js';
25
+ import { useInputModality, inputModality } from './chunk-7RKC5C7G.js';
21
26
  export { INPUT_MODALITY_ATTR, inputModality, installInputModality, useInputModality } from './chunk-7RKC5C7G.js';
22
- import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
23
- export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
24
- import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-XVB55M3N.js';
25
- export { useFormContext } from './chunk-XVB55M3N.js';
26
- import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-QM4SJKWV.js';
27
+ import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-72SDAX54.js';
28
+ export { useFormContext } from './chunk-72SDAX54.js';
29
+ import { Button, CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, ControlSizeContext, triggerType, SurfaceBoundary, Spinner, useFramesItself, TableCardCtx } from './chunk-Q36JVJX4.js';
30
+ export { Button, ButtonSizeContext, ControlSizeContext, Spinner } from './chunk-Q36JVJX4.js';
31
+ import { elementType } from './chunk-NKUCS67T.js';
32
+ import { useBreakpoint, readBreakpointPx } from './chunk-ZHUVLBCC.js';
33
+ export { BreakpointWidthProvider, useBreakpoint } from './chunk-ZHUVLBCC.js';
27
34
  import { useRevealFocus } from './chunk-HTXP5BMM.js';
28
35
  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, Tooltip } from './chunk-GXMTS3YD.js';
32
- export { Tooltip, useTextOverflow } from './chunk-GXMTS3YD.js';
33
- import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
34
- import { Icon, slotIcon, warnOnce } from './chunk-TJEV7YAY.js';
36
+ import { focusAfterSelfRemoval, FOCUSABLE, FIELD, ZOOM_KEYS, rovingTabStop, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberFocus, focusInitial, OVERLAY_CLOSE_ATTR, usePopupFocus, CONTROL_ANCESTOR, useGridKeyboard, focusAfterRemoval, useNonModalDialog, useReturnFocus } from './chunk-OIOII5L6.js';
37
+ export { useGridKeyboard } from './chunk-OIOII5L6.js';
38
+ import { useTextOverflow, AutoTooltip } from './chunk-GQVBZ4GW.js';
39
+ export { Tooltip, useTextOverflow } from './chunk-GQVBZ4GW.js';
40
+ import { mergeRefs } from './chunk-7Q46JXSK.js';
41
+ import { Icon, slotIcon, warnOnce } from './chunk-L6HWACK7.js';
35
42
  import { usePosition } from './chunk-HP7WUZPK.js';
36
43
  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 } from './chunk-66JJCQU3.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';
44
+ 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-JTU7OBYJ.js';
40
45
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
46
+ import { forwardRef, useRef, useId, useEffect, useState, createContext, useCallback, useLayoutEffect, useMemo, useContext, isValidElement, memo, useImperativeHandle, Children, Fragment as Fragment$1, cloneElement, useSyncExternalStore, useReducer } from 'react';
47
+ import { createPortal, flushSync } from 'react-dom';
41
48
 
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
- }
96
- }
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
- ] });
169
- }
170
49
  function BulkActionBar({ selectedCount, actions, onClearSelection, className }) {
171
50
  const isMobile = useBreakpoint("sm");
172
51
  if (selectedCount === 0) return null;
@@ -465,26 +344,7 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
465
344
  container ?? document.body
466
345
  )
467
346
  ] });
468
- return /* @__PURE__ */ jsx(Tooltip, { content: selectedLabel, disabled: !labelTruncated || open, children: triggerContent });
469
- }
470
-
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
- };
347
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: selectedLabel, disabled: !labelTruncated || open, children: triggerContent });
488
348
  }
489
349
 
490
350
  // ../../showcase/src/lib/time-zones.ts
@@ -566,7 +426,6 @@ function TimezoneSelect({ value, onChange, placeholder = "Search time zones\u202
566
426
  return /* @__PURE__ */ jsx(Combobox, { value: stored, onChange, options, placeholder, ...rest });
567
427
  }
568
428
  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
429
  var noChange = () => () => {
571
430
  };
572
431
  var inBrowser = () => true;
@@ -619,7 +478,7 @@ function RelativeTimeAt({ at, hours, className }) {
619
478
  return () => clearTimeout(timer);
620
479
  }, [at, now, hours]);
621
480
  const full = formatFullTime(at, locale);
622
- return /* @__PURE__ */ jsx(Tooltip, { content: full, ref: placed, children: /* @__PURE__ */ jsxs(
481
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: full, ref: placed, children: /* @__PURE__ */ jsxs(
623
482
  "time",
624
483
  {
625
484
  tabIndex: inRows || inControl ? void 0 : 0,
@@ -635,109 +494,6 @@ function RelativeTimeAt({ at, hours, className }) {
635
494
  mounted ? "browser" : "first"
636
495
  ) });
637
496
  }
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
497
  function RadioGroup({ value, onChange: onChangeProp, options, orientation = "vertical", className, label, ...wiring }) {
742
498
  const onChange = useReportedChange(onChangeProp, value);
743
499
  const groupRef = useRef(null);
@@ -774,6 +530,7 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
774
530
  ref: groupRef,
775
531
  role: "radiogroup",
776
532
  id: control.id,
533
+ "aria-label": label || control.labelId ? void 0 : control.label,
777
534
  "aria-labelledby": label ? groupId : control.labelId,
778
535
  "aria-describedby": control.describedBy,
779
536
  "aria-invalid": control.invalid || void 0,
@@ -833,400 +590,6 @@ function RadioGroup({ value, onChange: onChangeProp, options, orientation = "ver
833
590
  )
834
591
  ] });
835
592
  }
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
593
  var CONTROL_HEIGHT2 = {
1231
594
  sm: "h-[var(--ds-control-height-sm)]",
1232
595
  md: "h-[var(--ds-control-height-md)]",
@@ -1237,552 +600,88 @@ var TEXT_SIZE2 = {
1237
600
  md: "text-sm",
1238
601
  lg: "text-base"
1239
602
  };
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"
603
+ var DEFAULT_DATE_RANGE_PRESETS = [
604
+ { label: "Today", range: () => {
605
+ const t = startOfDay(/* @__PURE__ */ new Date());
606
+ return { start: t, end: t };
607
+ } },
608
+ { label: "Last 7 days", range: () => {
609
+ const t = startOfDay(/* @__PURE__ */ new Date());
610
+ return { start: addDays(t, -6), end: t };
611
+ } },
612
+ { label: "Last 30 days", range: () => {
613
+ const t = startOfDay(/* @__PURE__ */ new Date());
614
+ return { start: addDays(t, -29), end: t };
615
+ } },
616
+ { label: "This month", range: () => {
617
+ const t = /* @__PURE__ */ new Date();
618
+ return { start: new Date(t.getFullYear(), t.getMonth(), 1), end: startOfDay(t) };
619
+ } },
620
+ { label: "Last month", range: () => {
621
+ const t = /* @__PURE__ */ new Date();
622
+ return { start: new Date(t.getFullYear(), t.getMonth() - 1, 1), end: new Date(t.getFullYear(), t.getMonth(), 0) };
623
+ } }
1253
624
  ];
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 }) {
625
+ function DateRangePicker({
626
+ value,
627
+ onChange: onChangeProp,
628
+ placeholder = "Pick a date range",
629
+ disabled,
630
+ error = false,
631
+ label,
632
+ id,
633
+ className,
634
+ ref,
635
+ container,
636
+ min,
637
+ max,
638
+ disabledDates,
639
+ presets = DEFAULT_DATE_RANGE_PRESETS,
640
+ weekStartsOn: weekStartsOnProp,
641
+ size: sizeProp,
642
+ ...wiring
643
+ }) {
1313
644
  const weekStartsOn = useWeekStart(weekStartsOnProp);
1314
- const locale = useFormatLocale();
1315
- const { formatDayLong, formatMonthYear, weekdayNames } = useDateWords();
645
+ const { weekdayNames, formatDayLong, formatDayMedium, formatDayRange, formatMonthRange, formatMonthYear } = useDateWords();
1316
646
  const hydrated = useHydrated();
1317
- const today = useToday();
647
+ const now = useToday();
1318
648
  const onChange = useReportedChange(onChangeProp, value);
1319
649
  const contextSize = useContext(ControlSizeContext);
1320
650
  const size = sizeProp ?? contextSize ?? "md";
1321
651
  const [open, setOpen] = useState(false);
652
+ const [viewDate, setViewDate] = useState(() => value.start ?? /* @__PURE__ */ new Date());
1322
653
  const [hover, setHover] = useState(null);
1323
- const [viewDate, setViewDate] = useState(() => value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date());
1324
654
  const triggerRef = useRef(null);
1325
655
  const buttonRef = useRef(null);
1326
656
  const panelRef = useRef(null);
1327
- const gridRef = useRef(null);
657
+ const isPhone = useBreakpoint("sm");
658
+ const presetEdges = useScrollEdges({ enabled: isPhone });
659
+ useRevealFocus(presetEdges.elRef, isPhone);
1328
660
  const generatedId = useId();
1329
661
  const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1330
662
  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
- };
663
+ const triggerId = control.id ?? generatedId;
664
+ const locale = useFormatLocale();
1380
665
  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);
666
+ const focusPart = useRef(() => {
667
+ });
668
+ const [typedInvalid, setTypedInvalid] = useState(false);
669
+ useLayoutEffect(() => {
670
+ const panel = panelRef.current;
671
+ if (!open || !isPhone || !panel) return;
672
+ const fit = () => {
673
+ const field = triggerRef.current;
674
+ if (field) panel.style.width = `${field.getBoundingClientRect().width}px`;
1406
675
  };
1407
- document.addEventListener("mousedown", handler);
1408
- document.addEventListener("keydown", keyHandler);
676
+ fit();
677
+ window.addEventListener("resize", fit);
1409
678
  return () => {
1410
- document.removeEventListener("mousedown", handler);
1411
- document.removeEventListener("keydown", keyHandler);
679
+ window.removeEventListener("resize", fit);
680
+ panel.style.width = "";
1412
681
  };
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;
682
+ }, [open, isPhone]);
1785
683
  const pos = usePosition(triggerRef, panelRef, open, { side: "bottom", align: "start", offset: 4, flip: true });
684
+ const invalid = control.invalid || typedInvalid;
1786
685
  const today = now && startOfDay(now);
1787
686
  const dayNames = weekdayNames(weekStartsOn, "narrow");
1788
687
  const dayNamesLong = weekdayNames(weekStartsOn, "long");
@@ -1799,8 +698,8 @@ function DateRangePicker({
1799
698
  }
1800
699
  const showDay = (d) => {
1801
700
  setFocusDate(d);
1802
- const monthIndex2 = (x) => x.getFullYear() * 12 + x.getMonth();
1803
- const offset = monthIndex2(d) - monthIndex2(viewDate);
701
+ const monthIndex = (x) => x.getFullYear() * 12 + x.getMonth();
702
+ const offset = monthIndex(d) - monthIndex(viewDate);
1804
703
  if (offset < 0) setViewDate(new Date(d.getFullYear(), d.getMonth(), 1));
1805
704
  else if (offset > 1) setViewDate(new Date(d.getFullYear(), d.getMonth() - 1, 1));
1806
705
  };
@@ -1815,7 +714,7 @@ function DateRangePicker({
1815
714
  setViewDate(addMonths(viewDate, n));
1816
715
  setFocusDate(addMonthsClamped(focusDate, n));
1817
716
  };
1818
- const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: triggerRef, panel: panelRef, close: () => setOpen(false), tabExits: "edges" });
717
+ const { isTop: isTopLayer, returnFocus } = usePopupFocus(open, { trigger: buttonRef, panel: panelRef, close: () => setOpen(false), tabExits: "edges" });
1819
718
  const focusActiveDay = useLatest(grid.focusActive);
1820
719
  useEffect(() => {
1821
720
  if (!open) return;
@@ -1842,7 +741,20 @@ function DateRangePicker({
1842
741
  document.removeEventListener("keydown", onKey);
1843
742
  };
1844
743
  }, [open, isTopLayer, returnFocus]);
1845
- const isDisabledDay = (d) => min && d < startOfDay(min) || max && d > startOfDay(max) || false;
744
+ const isDisabledDay = (d) => min && d < startOfDay(min) || max && d > startOfDay(max) || isExcludedDay(disabledDates, d);
745
+ const takeTyped = ([s, e], [fs, fe]) => {
746
+ const refused = (d, filled) => filled && (!d || isDisabledDay(d));
747
+ const start = fs ? s : value.start;
748
+ const end = fe ? e : value.end;
749
+ const bad = refused(s, fs) || refused(e, fe) || !!start && !!end && end < start;
750
+ setTypedInvalid(bad);
751
+ if (bad) return;
752
+ const same = (a, b) => a && b ? isSameDay(a, b) : a === b;
753
+ if (!same(start, value.start) || !same(end, value.end)) {
754
+ onChange({ start, end });
755
+ if (start) setViewDate(start);
756
+ }
757
+ };
1846
758
  const selectDay = (d) => {
1847
759
  if (isDisabledDay(d)) return;
1848
760
  if (!value.start || value.start && value.end) {
@@ -1861,44 +773,70 @@ function DateRangePicker({
1861
773
  const rangeStart = value.start;
1862
774
  const rangeEnd2 = value.end ?? previewEnd;
1863
775
  const inRange = (d) => !!(rangeStart && rangeEnd2 && d > rangeStart && d < rangeEnd2);
1864
- const months = [viewDate, addMonths(viewDate, 1)];
776
+ const months = isPhone ? [viewDate] : [viewDate, addMonths(viewDate, 1)];
1865
777
  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
778
  return /* @__PURE__ */ jsxs("div", { ref, className: cn(className), children: [
1868
779
  label && /* @__PURE__ */ jsx("label", { htmlFor: triggerId, className: "block text-sm font-medium text-[var(--ds-text-primary)] mb-ds-1", children: label }),
1869
780
  control.requiredNote,
1870
781
  /* @__PURE__ */ jsxs(
1871
- "button",
782
+ "div",
1872
783
  {
1873
- id: triggerId,
1874
784
  ref: triggerRef,
1875
- type: "button",
1876
- onClick: () => !disabled && setOpen((o) => !o),
1877
- disabled,
1878
- "aria-labelledby": labelledBy,
785
+ "data-date-range-field": "",
786
+ role: "group",
787
+ id: triggerId,
788
+ "aria-label": control.labelId ? void 0 : label ?? control.label ?? "Dates",
789
+ "aria-labelledby": control.labelId,
1879
790
  "aria-describedby": control.describedBy,
1880
- "aria-invalid": control.invalid || void 0,
1881
- "aria-haspopup": "dialog",
1882
- "aria-expanded": open,
791
+ "aria-description": !value.start && !value.end ? placeholder : void 0,
792
+ "aria-invalid": invalid || void 0,
1883
793
  className: cn(
1884
794
  "flex items-center gap-ds-2 w-full",
1885
- CONTROL_HEIGHT3[size],
1886
- TEXT_SIZE3[size],
795
+ CONTROL_HEIGHT2[size],
796
+ TEXT_SIZE2[size],
1887
797
  "border-[length:var(--ds-border-width)]",
1888
- control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
798
+ invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
1889
799
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
1890
800
  "shadow-[var(--ds-input-shadow)]",
1891
- "transition-colors cursor-pointer",
801
+ "transition-colors",
1892
802
  "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,
803
+ !invalid && !disabled && !open && "hover:border-[var(--ds-input-border-hover)]",
804
+ open && (invalid ? ERROR_ACTIVE_RING : ACTIVE_RING),
805
+ // the parts mark which has focus (their tint); the field draws the focus, as DatePicker's does
806
+ invalid ? ERROR_FOCUS_WITHIN_RING : FIELD_FOCUS_WITHIN_RING,
1896
807
  disabled && "opacity-disabled cursor-not-allowed",
1897
- summary ? "text-[var(--ds-text-primary)]" : "text-[var(--ds-text-muted)]"
808
+ "text-[var(--ds-text-primary)]"
1898
809
  ),
1899
810
  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) })
811
+ /* @__PURE__ */ jsx(Icon, { name: "calendar", size: 14, className: "text-[var(--ds-text-muted)] shrink-0" }),
812
+ /* @__PURE__ */ jsx("div", { className: "flex items-center min-w-0 flex-1 tabular-nums overflow-hidden", onClick: () => focusPart.current(0), children: /* @__PURE__ */ jsx(
813
+ DateSegmentFields,
814
+ {
815
+ values: [value.start, value.end],
816
+ names: ["Start", "End"],
817
+ dayFirst,
818
+ hydrated,
819
+ disabled,
820
+ focusRef: (focus) => {
821
+ focusPart.current = focus;
822
+ },
823
+ onChange: takeTyped
824
+ }
825
+ ) }),
826
+ /* @__PURE__ */ jsx(
827
+ "button",
828
+ {
829
+ ref: buttonRef,
830
+ type: "button",
831
+ "aria-label": "Choose a date range",
832
+ "aria-haspopup": "dialog",
833
+ "aria-expanded": open,
834
+ onClick: () => !disabled && setOpen((o) => !o),
835
+ disabled,
836
+ 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),
837
+ children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 14 })
838
+ }
839
+ )
1902
840
  ]
1903
841
  }
1904
842
  ),
@@ -1912,6 +850,9 @@ function DateRangePicker({
1912
850
  tabIndex: -1,
1913
851
  className: cn(
1914
852
  "fixed z-[var(--ds-z-popover)] flex outline-none",
853
+ // on a phone the panel stays inside the screen: its quick ranges above the month, the month scrolling under
854
+ // them if it must, and its footer, with Clear, always in view (item 35)
855
+ isPhone && "flex-col max-w-[calc(100vw-16px)] max-h-[calc(100dvh-16px)]",
1915
856
  "bg-[var(--ds-bg-surface-elevated)]",
1916
857
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
1917
858
  "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
@@ -1920,57 +861,73 @@ function DateRangePicker({
1920
861
  ),
1921
862
  style: { top: pos.top, left: pos.left },
1922
863
  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: [
864
+ presets.length > 0 && /* @__PURE__ */ jsx(
865
+ "div",
866
+ {
867
+ "data-range-presets": "",
868
+ ref: isPhone ? presetEdges.ref : void 0,
869
+ ...isPhone ? presetEdges.fadeProps : {},
870
+ className: cn(
871
+ "border-[var(--ds-border-subtle)]",
872
+ 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]"
873
+ ),
874
+ 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: () => {
875
+ const r = p.range();
876
+ returnFocus();
877
+ onChange(r);
878
+ setViewDate(r.start ?? /* @__PURE__ */ new Date());
879
+ setOpen(false);
880
+ }, children: p.label }, p.label))
881
+ }
882
+ ),
883
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", isPhone && "min-h-0"), children: [
884
+ /* @__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
885
  /* @__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]) }),
886
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: months[1] ? formatMonthRange(months[0], months[1]) : formatMonthYear(months[0]) }),
1934
887
  /* @__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
888
  ] }),
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);
889
+ /* @__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) => (
890
+ // on a phone the one month fills the panel, its days a comfortable tap (the look check's second round)
891
+ /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": formatMonthYear(m), className: cn(isPhone ? "w-full" : "w-[224px]", "flex flex-col gap-[4px]"), children: [
892
+ /* @__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)) }),
893
+ 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) => {
894
+ if (!isSameMonth(d, m)) return /* @__PURE__ */ jsx("div", { role: "gridcell" }, d.toISOString());
895
+ const isStart = !!rangeStart && isSameDay(d, rangeStart);
896
+ const isEnd = !!rangeEnd2 && isSameDay(d, rangeEnd2);
897
+ const between = inRange(d);
898
+ const dis = isDisabledDay(d);
899
+ return /* @__PURE__ */ jsx(
900
+ "button",
901
+ {
902
+ type: "button",
903
+ role: "gridcell",
904
+ "data-grid-key": dayKey(d),
905
+ tabIndex: grid.tabIndexOf(d),
906
+ "aria-disabled": dis || void 0,
907
+ onClick: () => selectDay(d),
908
+ onFocus: () => {
909
+ if (!isSameDay(d, focusDate)) setFocusDate(d);
910
+ },
911
+ onMouseEnter: () => setHover(d),
912
+ "aria-selected": isStart || isEnd || between,
913
+ "aria-current": today !== null && isSameDay(d, today) ? "date" : void 0,
914
+ "aria-label": formatDayLong(d),
915
+ className: cn(
916
+ "w-full aspect-square flex items-center justify-center",
917
+ isPhone ? "text-sm" : "text-xs",
918
+ "transition-colors cursor-pointer",
919
+ FOCUS_RING,
920
+ 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)]",
921
+ dis && "opacity-disabled cursor-not-allowed hover:bg-transparent"
922
+ ),
923
+ children: d.getDate()
1955
924
  },
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: [
925
+ d.toISOString()
926
+ );
927
+ }) }, w))
928
+ ] }, `${m.getFullYear()}-${m.getMonth()}`)
929
+ )) }),
930
+ /* @__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
931
  /* @__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
932
  (value.start || value.end) && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
1976
933
  onChange({ start: null, end: null });
@@ -2103,10 +1060,10 @@ var atDay = (year, month, day, time) => new Date(year, month, day, time.getHours
2103
1060
  var dayIndex = (d) => Math.round(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / 864e5);
2104
1061
  var isMidnight = (d) => d.getHours() === 0 && d.getMinutes() === 0 && d.getSeconds() === 0 && d.getMilliseconds() === 0;
2105
1062
  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;
1063
+ const last = daysIn(year, month);
1064
+ if (week === -1) return last - (new Date(year, month, last).getDay() - weekday2 + 7) % 7;
2108
1065
  const day = 1 + (weekday2 - new Date(year, month, 1).getDay() + 7) % 7 + (week - 1) * 7;
2109
- return day <= last2 ? day : null;
1066
+ return day <= last ? day : null;
2110
1067
  }
2111
1068
  function nthOf(date) {
2112
1069
  const d = date.getDate();
@@ -2179,8 +1136,8 @@ function occurrenceAt(event, start) {
2179
1136
  }
2180
1137
  function stopOf(event) {
2181
1138
  if (event.allDay) {
2182
- const last2 = event.end ?? event.start;
2183
- return new Date(last2.getFullYear(), last2.getMonth(), last2.getDate() + 1);
1139
+ const last = event.end ?? event.start;
1140
+ return new Date(last.getFullYear(), last.getMonth(), last.getDate() + 1);
2184
1141
  }
2185
1142
  return event.end ?? new Date(event.start.getTime() + 36e5);
2186
1143
  }
@@ -3100,7 +2057,7 @@ function TruncFileName({ children, className }) {
3100
2057
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
3101
2058
  const el = /* @__PURE__ */ jsx("p", { ref, className: cn("truncate", className), children });
3102
2059
  if (!isTruncated) return el;
3103
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
2060
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: el });
3104
2061
  }
3105
2062
  var PARALLEL = 3;
3106
2063
  function formatSize(bytes) {
@@ -3112,7 +2069,7 @@ var running = (item) => item.status === "queued" || item.status === "uploading";
3112
2069
  var failureOf = (err) => err instanceof Error && err.message ? err.message : "The upload didn't work.";
3113
2070
  var percentOf = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : 0;
3114
2071
  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 }) {
2072
+ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accept, multiple = true, maxSize, className, error = false, label, id, "aria-label": ariaLabel }) {
3116
2073
  const controlled = files !== void 0;
3117
2074
  const [own, setOwn] = useState([]);
3118
2075
  const shown = controlled ? files : own;
@@ -3248,10 +2205,10 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3248
2205
  focusAfterRemoval(rows, gone.index, inputRef.current);
3249
2206
  return;
3250
2207
  }
3251
- const last2 = lastAction.current;
2208
+ const last = lastAction.current;
3252
2209
  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);
2210
+ if (!last || last.el.isConnected || active && active !== document.body) return;
2211
+ const row = Array.from(listRef.current?.querySelectorAll(":scope > li") ?? []).find((li) => li.dataset.uploadId === last.id);
3255
2212
  row?.querySelector("button")?.focus();
3256
2213
  }, [shown]);
3257
2214
  const addFiles = useCallback(
@@ -3343,6 +2300,7 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
3343
2300
  accept,
3344
2301
  multiple,
3345
2302
  className: "sr-only",
2303
+ "aria-label": field ? void 0 : ariaLabel,
3346
2304
  "aria-labelledby": field ? field.labelId : void 0,
3347
2305
  "aria-describedby": describedBy,
3348
2306
  "aria-invalid": invalid || void 0,
@@ -3566,12 +2524,12 @@ function fromValue(v, use24Hour) {
3566
2524
  const h24 = Number(m[1]);
3567
2525
  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
2526
  }
3569
- var CONTROL_HEIGHT4 = {
2527
+ var CONTROL_HEIGHT3 = {
3570
2528
  sm: "h-[var(--ds-control-height-sm)]",
3571
2529
  md: "h-[var(--ds-control-height-md)]",
3572
2530
  lg: "h-[var(--ds-control-height-lg)]"
3573
2531
  };
3574
- var TEXT_SIZE4 = {
2532
+ var TEXT_SIZE3 = {
3575
2533
  sm: "text-xs",
3576
2534
  md: "text-sm",
3577
2535
  lg: "text-base"
@@ -3664,7 +2622,7 @@ function TimePicker({
3664
2622
  if (!open) return;
3665
2623
  const id2 = requestAnimationFrame(() => {
3666
2624
  const panel = panelRef.current;
3667
- (panel?.querySelector('[data-selected="true"]') ?? panel?.querySelector("button"))?.focus();
2625
+ (panel?.querySelector('[aria-selected="true"]') ?? panel?.querySelector("button"))?.focus();
3668
2626
  });
3669
2627
  return () => cancelAnimationFrame(id2);
3670
2628
  }, [open]);
@@ -3680,7 +2638,7 @@ function TimePicker({
3680
2638
  const columns2 = Array.from(panelRef.current?.querySelectorAll("[data-column]") ?? []);
3681
2639
  const idx = columns2.indexOf(column);
3682
2640
  const target = columns2[idx + (e.key === "ArrowRight" ? 1 : -1)];
3683
- next = target?.querySelector('[data-selected="true"]') ?? target?.querySelector("button") ?? null;
2641
+ next = target?.querySelector('[aria-selected="true"]') ?? target?.querySelector("button") ?? null;
3684
2642
  } else return;
3685
2643
  if (next instanceof HTMLElement) {
3686
2644
  e.preventDefault();
@@ -3804,17 +2762,34 @@ function TimePicker({
3804
2762
  return;
3805
2763
  }
3806
2764
  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);
2765
+ typeInto(index, key);
3811
2766
  return;
3812
2767
  }
3813
2768
  if (!use24Hour && /^[aApP]$/.test(key)) {
3814
2769
  setSegment("period", /^[aA]$/.test(key) ? "AM" : "PM");
3815
2770
  return;
3816
2771
  }
2772
+ if (separatorMovesOn(TIME_SEPARATORS, key, !!buffers[k] || segs[k] !== null)) focusSegment(index + 1);
3817
2773
  };
2774
+ const typeInto = (index, digit) => {
2775
+ const spec = specs[index];
2776
+ const k = spec.key;
2777
+ const r = typeDigit(spec, buffers[k] ?? "", digit);
2778
+ if (r.kind === "here") {
2779
+ setBuffers((b) => ({ ...b, [k]: r.buffer }));
2780
+ setSegment(k, r.value);
2781
+ return;
2782
+ }
2783
+ const nextSpec = specs[index + 1];
2784
+ if (!nextSpec) return;
2785
+ focusSegment(index + 1);
2786
+ const carried = typeDigit(nextSpec, "", digit);
2787
+ if (carried.kind !== "here") return;
2788
+ const nk = nextSpec.key;
2789
+ setBuffers((b) => ({ ...b, [nk]: carried.buffer }));
2790
+ setSegment(nk, carried.value);
2791
+ };
2792
+ const freshSegment = (k) => setBuffers((b) => b[k] ? { ...b, [k]: "" } : b);
3818
2793
  const shown = (k, spec) => {
3819
2794
  const b = buffers[k];
3820
2795
  if (b && segs[k] === null) return b;
@@ -3829,7 +2804,7 @@ function TimePicker({
3829
2804
  "data-time-field": "",
3830
2805
  role: "group",
3831
2806
  id: control.id,
3832
- "aria-label": control.labelId ? void 0 : "Time",
2807
+ "aria-label": control.labelId ? void 0 : control.label ?? "Time",
3833
2808
  "aria-labelledby": control.labelId,
3834
2809
  "aria-describedby": control.describedBy,
3835
2810
  "aria-invalid": control.invalid || void 0,
@@ -3842,8 +2817,8 @@ function TimePicker({
3842
2817
  },
3843
2818
  className: cn(
3844
2819
  "flex w-full items-center gap-ds-2",
3845
- CONTROL_HEIGHT4[size],
3846
- TEXT_SIZE4[size],
2820
+ CONTROL_HEIGHT3[size],
2821
+ TEXT_SIZE3[size],
3847
2822
  "border-[length:var(--ds-border-width)]",
3848
2823
  control.invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]",
3849
2824
  "bg-[var(--ds-input-bg)] rounded-[var(--ds-radius-md)]",
@@ -3879,6 +2854,8 @@ function TimePicker({
3879
2854
  "aria-valuenow": segs[k] ?? void 0,
3880
2855
  "aria-valuetext": segs[k] === null ? "empty" : pad2(segs[k]),
3881
2856
  onKeyDown: (e) => onSegmentKeyDown(e, i, spec),
2857
+ onFocus: () => freshSegment(k),
2858
+ onMouseDown: () => freshSegment(k),
3882
2859
  onClick: (e) => e.stopPropagation(),
3883
2860
  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
2861
  children: shown(k, spec)
@@ -3944,13 +2921,14 @@ function TimePicker({
3944
2921
  ),
3945
2922
  style: { top: pos.top, left: pos.left },
3946
2923
  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(
2924
+ /* @__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
2925
  "button",
3949
2926
  {
3950
2927
  type: "button",
2928
+ role: "option",
3951
2929
  tabIndex: i === stopOf2("hour", chosenHour) ? 0 : -1,
3952
2930
  onFocus: () => holdStop("hour", i),
3953
- "data-selected": use24Hour ? h === parseInt(value) : h === parsed.hour,
2931
+ "aria-selected": use24Hour ? h === parseInt(value) : h === parsed.hour,
3954
2932
  onMouseDown: (e) => e.preventDefault(),
3955
2933
  onClick: () => emitChange(h, parsed.minute, parsed.period),
3956
2934
  className: cn(
@@ -3964,13 +2942,14 @@ function TimePicker({
3964
2942
  h
3965
2943
  )) }),
3966
2944
  /* @__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(
2945
+ /* @__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
2946
  "button",
3969
2947
  {
3970
2948
  type: "button",
2949
+ role: "option",
3971
2950
  tabIndex: i === stopOf2("minute", chosenMinute) ? 0 : -1,
3972
2951
  onFocus: () => holdStop("minute", i),
3973
- "data-selected": m === parsed.minute,
2952
+ "aria-selected": m === parsed.minute,
3974
2953
  onMouseDown: (e) => e.preventDefault(),
3975
2954
  onClick: () => emitChange(parsed.hour, m, parsed.period),
3976
2955
  className: cn(
@@ -3985,13 +2964,14 @@ function TimePicker({
3985
2964
  )) }),
3986
2965
  !use24Hour && /* @__PURE__ */ jsxs(Fragment, { children: [
3987
2966
  /* @__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(
2967
+ /* @__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
2968
  "button",
3990
2969
  {
3991
2970
  type: "button",
2971
+ role: "option",
3992
2972
  tabIndex: i === stopOf2("period", chosenPeriod) ? 0 : -1,
3993
2973
  onFocus: () => holdStop("period", i),
3994
- "data-selected": p === parsed.period,
2974
+ "aria-selected": p === parsed.period,
3995
2975
  onMouseDown: (e) => e.preventDefault(),
3996
2976
  onClick: () => emitChange(parsed.hour, parsed.minute, p),
3997
2977
  className: cn(
@@ -4054,6 +3034,9 @@ function getPaletteClasses(color, variant) {
4054
3034
  const styleFn = paletteByVariant[variant] ?? paletteByVariant.subtle;
4055
3035
  return styleFn(color);
4056
3036
  }
3037
+ var SELECTED = "bg-[var(--ds-accent-subtle)] text-[var(--ds-accent-subtle-text)]";
3038
+ 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)]";
3039
+ 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
3040
  var Tag = forwardRef(function Tag2({
4058
3041
  variant = "default",
4059
3042
  size = "md",
@@ -4061,43 +3044,61 @@ var Tag = forwardRef(function Tag2({
4061
3044
  maxWidth,
4062
3045
  onRemove,
4063
3046
  onClick,
3047
+ selected,
4064
3048
  disabled = false,
4065
3049
  className,
4066
3050
  children
4067
3051
  }, ref) {
3052
+ const chipRef = useRef(null);
3053
+ const setChip = (el) => {
3054
+ chipRef.current = el;
3055
+ if (typeof ref === "function") ref(el);
3056
+ else if (ref) ref.current = el;
3057
+ };
4068
3058
  const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1 });
4069
3059
  const handleRemove = (e) => {
4070
3060
  e.stopPropagation();
3061
+ focusAfterSelfRemoval(e.currentTarget, { item: chipRef.current });
4071
3062
  onRemove?.();
4072
3063
  };
4073
- const colorClasses = color ? getPaletteClasses(color, variant) : variantStyles[variant];
3064
+ const clickable = !!onClick;
3065
+ const isSelected = !!onClick && !!selected;
3066
+ const bordered = variant === "outline" || !color && (variant === "success" || variant === "warning" || variant === "error");
3067
+ const colorClasses = isSelected ? cn(SELECTED, selectedEdge(bordered)) : color ? getPaletteClasses(color, variant) : variantStyles[variant];
3068
+ const label = /* @__PURE__ */ jsxs(Fragment, { children: [
3069
+ 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 }) }),
3070
+ /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate min-w-0", children })
3071
+ ] });
4074
3072
  const tag = /* @__PURE__ */ jsxs(
4075
3073
  "span",
4076
3074
  {
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,
3075
+ ref: setChip,
3076
+ "aria-disabled": !clickable && disabled || void 0,
3077
+ "data-selected": isSelected ? "" : void 0,
4088
3078
  className: cn(
4089
3079
  "inline-flex items-center font-medium max-w-full min-w-0 whitespace-nowrap",
4090
3080
  "rounded-[var(--ds-radius-sm)]",
4091
3081
  "transition-colors duration-[var(--ds-transition-speed)]",
4092
3082
  sizeStyles[size],
4093
3083
  colorClasses,
4094
- onClick && !disabled && cn("cursor-pointer select-none hover:opacity-80", FOCUS_RING),
3084
+ clickable && !disabled && cn("relative cursor-pointer select-none hover:opacity-80", LABEL_FOCUS_RING),
4095
3085
  disabled && "opacity-disabled cursor-not-allowed",
4096
3086
  className
4097
3087
  ),
4098
3088
  style: maxWidth ? { maxWidth } : void 0,
4099
3089
  children: [
4100
- /* @__PURE__ */ jsx("span", { ref: truncRef, className: "truncate min-w-0", children }),
3090
+ clickable ? /* @__PURE__ */ jsx(
3091
+ "button",
3092
+ {
3093
+ type: "button",
3094
+ "data-tag-label": "",
3095
+ disabled,
3096
+ "aria-pressed": selected,
3097
+ onClick,
3098
+ className: "flex min-w-0 max-w-full items-center cursor-[inherit] outline-none after:absolute after:inset-0 after:content-['']",
3099
+ children: label
3100
+ }
3101
+ ) : label,
4101
3102
  onRemove && !disabled && /* @__PURE__ */ jsx(
4102
3103
  "button",
4103
3104
  {
@@ -4106,7 +3107,7 @@ var Tag = forwardRef(function Tag2({
4106
3107
  "data-tag-remove": "",
4107
3108
  "aria-label": typeof children === "string" ? `Remove ${children}` : "Remove",
4108
3109
  className: cn(
4109
- "inline-flex items-center justify-center shrink-0",
3110
+ "relative inline-flex items-center justify-center shrink-0",
4110
3111
  "rounded-[var(--ds-radius-sm)]",
4111
3112
  "opacity-60 hover:opacity-100",
4112
3113
  "transition-opacity duration-[var(--ds-transition-speed)] cursor-pointer",
@@ -4119,7 +3120,7 @@ var Tag = forwardRef(function Tag2({
4119
3120
  }
4120
3121
  );
4121
3122
  if (isTruncated) {
4122
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: tag });
3123
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: tag });
4123
3124
  }
4124
3125
  return tag;
4125
3126
  });
@@ -4268,7 +3269,8 @@ function PinInput({
4268
3269
  size: sizeProp,
4269
3270
  className,
4270
3271
  error = false,
4271
- id
3272
+ id,
3273
+ "aria-label": ariaLabel
4272
3274
  }) {
4273
3275
  const onChange = useReportedChange(onChangeProp, value);
4274
3276
  useInputModality();
@@ -4327,7 +3329,7 @@ function PinInput({
4327
3329
  "div",
4328
3330
  {
4329
3331
  role: "group",
4330
- "aria-label": "PIN input",
3332
+ "aria-label": ariaLabel ?? "PIN input",
4331
3333
  "aria-invalid": error || void 0,
4332
3334
  className: cn("inline-flex w-fit items-center gap-ds-2", className),
4333
3335
  children: digits.map((digit, i) => /* @__PURE__ */ jsx(
@@ -4487,7 +3489,7 @@ function Rating({
4487
3489
  {
4488
3490
  role: readOnly ? "img" : "radiogroup",
4489
3491
  id: control.id,
4490
- "aria-label": readOnly || !control.labelId ? `Rating: ${value} out of ${max}` : void 0,
3492
+ "aria-label": readOnly ? `${control.label ?? "Rating"}: ${value} out of ${max}` : control.labelId ? void 0 : control.label ?? `Rating: ${value} out of ${max}`,
4491
3493
  "aria-labelledby": readOnly ? void 0 : control.labelId,
4492
3494
  "aria-describedby": control.describedBy,
4493
3495
  "aria-invalid": readOnly ? void 0 : control.invalid || void 0,
@@ -4575,7 +3577,8 @@ function ColorPicker({
4575
3577
  swatches = DEFAULT_SWATCHES,
4576
3578
  disabled = false,
4577
3579
  className,
4578
- error = false
3580
+ error = false,
3581
+ "aria-label": ariaLabel
4579
3582
  }) {
4580
3583
  const onChange = useReportedChange(onChangeProp, value);
4581
3584
  useInputModality();
@@ -4640,6 +3643,8 @@ function ColorPicker({
4640
3643
  return /* @__PURE__ */ jsxs(
4641
3644
  "div",
4642
3645
  {
3646
+ role: "group",
3647
+ "aria-label": ariaLabel,
4643
3648
  "aria-invalid": error || void 0,
4644
3649
  className: cn(
4645
3650
  "w-full max-w-64 p-ds-3 space-y-ds-3",
@@ -4789,18 +3794,33 @@ var Alert = forwardRef(function Alert2({
4789
3794
  actions,
4790
3795
  placement = "inline",
4791
3796
  expandableContent,
3797
+ consent,
3798
+ confirmation,
4792
3799
  className
4793
3800
  }, ref) {
3801
+ const titleId = useId();
3802
+ const confirmationRef = useRef(null);
3803
+ const confirming = !!consent && confirmation != null;
3804
+ useEffect(() => {
3805
+ if (confirming) confirmationRef.current?.focus();
3806
+ }, [confirming]);
3807
+ const rootRef = useRef(null);
3808
+ const setRoot = (el) => {
3809
+ rootRef.current = el;
3810
+ if (typeof ref === "function") ref(el);
3811
+ else if (ref) ref.current = el;
3812
+ };
4794
3813
  const [detailsExpanded, setDetailsExpanded] = useState(false);
4795
3814
  const isMobile = useBreakpoint("sm");
4796
- const resolvedActions = actions ?? (action ? [action] : []);
3815
+ const resolvedActions = consent || confirming ? [] : actions ?? (action ? [action] : []);
4797
3816
  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)]";
3817
+ 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
3818
  return /* @__PURE__ */ jsxs(
4800
3819
  "div",
4801
3820
  {
4802
- ref,
4803
- role: "alert",
3821
+ ref: setRoot,
3822
+ role: consent ? "region" : variant === "error" || variant === "warning" ? "alert" : "status",
3823
+ "aria-labelledby": consent ? titleId : void 0,
4804
3824
  className: cn(
4805
3825
  "flex items-start gap-ds-3 p-ds-4",
4806
3826
  "border-[length:var(--ds-border-width)]",
@@ -4814,7 +3834,7 @@ var Alert = forwardRef(function Alert2({
4814
3834
  /* @__PURE__ */ jsx("div", { className: cn("mt-[0.3125em] w-3 h-3 shrink-0 rounded-[var(--ds-radius-sm)]", indicators[variant]) }),
4815
3835
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
4816
3836
  /* @__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 }),
3837
+ /* @__PURE__ */ jsx("p", { id: titleId, className: "text-sm font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)]", children: title }),
4818
3838
  resolvedActions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-ds-2 shrink-0", children: shouldCollapseActions ? /* @__PURE__ */ jsx(
4819
3839
  DropdownMenu,
4820
3840
  {
@@ -4864,7 +3884,34 @@ var Alert = forwardRef(function Alert2({
4864
3884
  );
4865
3885
  }) })
4866
3886
  ] }),
4867
- children && /* @__PURE__ */ jsx("div", { className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)]", children }),
3887
+ confirming ? (
3888
+ // GOV.UK's cookie banner: the confirmation in the choices' place, announced, with focus on it
3889
+ /* @__PURE__ */ jsx("div", { ref: confirmationRef, role: "alert", tabIndex: -1, className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)] outline-none", children: confirmation })
3890
+ ) : children && /* @__PURE__ */ jsx("div", { className: "mt-ds-1 text-xs text-[var(--ds-text-secondary)]", children }),
3891
+ consent && !confirming && // two choices of equal weight, the alert's own action look, side by side at any width (sharing a phone's row);
3892
+ // never a menu
3893
+ /* @__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 ? (
3894
+ // a page that insists on one primary choice: the library's primary Button in the other choice's box, its
3895
+ // padding, corners and height, wrapping as it does, so the two share a row's height and a phone's row
3896
+ // equally. Its height and padding go in its style: its size's classes set them, and a class can't be relied on
3897
+ // to undo another
3898
+ /* @__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)
3899
+ ) : /* @__PURE__ */ jsx(
3900
+ "button",
3901
+ {
3902
+ type: "button",
3903
+ onClick: choice.onClick,
3904
+ className: cn(
3905
+ "text-xs font-medium px-ds-2.5 py-ds-1 rounded-[var(--ds-radius-sm)]",
3906
+ "transition-colors cursor-pointer max-sm:flex-1 max-sm:min-h-[var(--ds-control-height-sm)]",
3907
+ "border-[length:var(--ds-border-width)] border-current",
3908
+ actionColorsByVariant[variant],
3909
+ FOCUS_RING
3910
+ ),
3911
+ children: choice.label
3912
+ },
3913
+ choice.label
3914
+ )) }),
4868
3915
  expandableContent && /* @__PURE__ */ jsxs("div", { className: "mt-ds-2", children: [
4869
3916
  /* @__PURE__ */ jsxs(
4870
3917
  "button",
@@ -4895,13 +3942,21 @@ var Alert = forwardRef(function Alert2({
4895
3942
  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
3943
  ] })
4897
3944
  ] }),
4898
- dismissible && /* @__PURE__ */ jsx(
4899
- "button",
3945
+ 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
3946
+ // rest the muted cross where it was (the margin takes back the box's room), on hover the alert's own buttons'
3947
+ // tint. Its alert goes with it, so focus goes to a neighbour (focusAfterSelfRemoval), never to the page
3948
+ /* @__PURE__ */ jsx(
3949
+ Button,
4900
3950
  {
4901
3951
  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),
3952
+ variant: "ghost",
3953
+ size: "icon-xs",
4904
3954
  "aria-label": "Dismiss",
3955
+ onClick: (e) => {
3956
+ focusAfterSelfRemoval(e.currentTarget, { item: rootRef.current });
3957
+ onDismiss?.();
3958
+ },
3959
+ className: cn("-m-ds-1 shrink-0", actionColorsByVariant[variant], "text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]"),
4905
3960
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
4906
3961
  }
4907
3962
  )
@@ -6057,7 +5112,7 @@ var CardTitle = forwardRef(function CardTitle2({ className, children, lines: lin
6057
5112
  ) : children
6058
5113
  }
6059
5114
  );
6060
- return /* @__PURE__ */ jsx(Tooltip, { content: children, disabled: !isTruncated, children: heading });
5115
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, disabled: !isTruncated, children: heading });
6061
5116
  });
6062
5117
  var CardDescription = forwardRef(function CardDescription2({ className, children, lines = 2, ...props }, forwardedRef) {
6063
5118
  const { ref: overflowRef, isTruncated } = useTextOverflow({ lines, disabled: lines === 0 });
@@ -6075,7 +5130,7 @@ var CardDescription = forwardRef(function CardDescription2({ className, children
6075
5130
  children
6076
5131
  }
6077
5132
  );
6078
- return /* @__PURE__ */ jsx(Tooltip, { content: children, disabled: !isTruncated, children: el });
5133
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, disabled: !isTruncated, children: el });
6079
5134
  });
6080
5135
  var CardContent = forwardRef(function CardContent2({ className, children, ...props }, ref) {
6081
5136
  return /* @__PURE__ */ jsx("div", { ref, className: cn("flex-1 px-ds-5 py-ds-4", className), ...props, children });
@@ -6090,7 +5145,7 @@ function TruncLabel({ children }) {
6090
5145
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
6091
5146
  const el = /* @__PURE__ */ jsx("span", { ref, className: "block text-xs font-medium text-[var(--ds-text-muted)] uppercase tracking-wider truncate min-w-0", children });
6092
5147
  if (!isTruncated) return el;
6093
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
5148
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: el });
6094
5149
  }
6095
5150
  function StatsCard({ value, label, trend, icon, layout = "card", interactive, onClick, children, className }) {
6096
5151
  if (layout === "inline") {
@@ -6173,7 +5228,7 @@ function TrendBadge({ trend }) {
6173
5228
  }
6174
5229
  );
6175
5230
  if (!isTruncated) return badge;
6176
- return /* @__PURE__ */ jsx(Tooltip, { content: trend.value, children: badge });
5231
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: trend.value, children: badge });
6177
5232
  }
6178
5233
  function DataList({ items, columns = 1, className }) {
6179
5234
  return /* @__PURE__ */ jsx(
@@ -6344,7 +5399,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
6344
5399
  ),
6345
5400
  children: [
6346
5401
  item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: s.iconSize, className: "shrink-0" }),
6347
- item.tooltip ? /* @__PURE__ */ jsx(Tooltip, { content: item.tooltip, children: /* @__PURE__ */ jsx("span", { className: "cursor-help border-b-[length:var(--ds-border-width)] border-dashed border-[var(--ds-border-default)]", children: item.label }) }) : item.label
5402
+ item.tooltip ? /* @__PURE__ */ jsx(AutoTooltip, { content: item.tooltip, children: /* @__PURE__ */ jsx("span", { className: "cursor-help border-b-[length:var(--ds-border-width)] border-dashed border-[var(--ds-border-default)]", children: item.label }) }) : item.label
6348
5403
  ]
6349
5404
  }
6350
5405
  ),
@@ -6389,7 +5444,7 @@ var DescriptionList = forwardRef(function DescriptionList2({
6389
5444
  ),
6390
5445
  children: [
6391
5446
  item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon, size: s.iconSize, className: "shrink-0" }),
6392
- item.tooltip ? /* @__PURE__ */ jsx(Tooltip, { content: item.tooltip, children: /* @__PURE__ */ jsx("span", { className: "cursor-help border-b-[length:var(--ds-border-width)] border-dashed border-[var(--ds-border-default)]", children: item.label }) }) : item.label
5447
+ item.tooltip ? /* @__PURE__ */ jsx(AutoTooltip, { content: item.tooltip, children: /* @__PURE__ */ jsx("span", { className: "cursor-help border-b-[length:var(--ds-border-width)] border-dashed border-[var(--ds-border-default)]", children: item.label }) }) : item.label
6393
5448
  ]
6394
5449
  }
6395
5450
  ),
@@ -6574,12 +5629,14 @@ function actionsAreBadges(actions) {
6574
5629
  const items = Children.toArray(actions);
6575
5630
  return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : elementType(item.type) === Badge));
6576
5631
  }
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(Tooltip, { content: children, children: cell });
5632
+ var SKELETON_ROWS = 5;
5633
+ var SKELETON_WIDTHS = ["72%", "48%", "60%", "36%", "54%"];
5634
+ var LOADING_LABEL = "Loading table data";
5635
+ function RefreshLine({ className }) {
5636
+ 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" }) });
5637
+ }
5638
+ function SkeletonLine({ width, end = false, className }) {
5639
+ 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
5640
  }
6584
5641
  var TITLE_CLASSES = TITLE_TEXT;
6585
5642
  var FRAME_CLASSES = cn(
@@ -6843,7 +5900,16 @@ function Table({
6843
5900
  if (allSelected) onSelectionChange?.(selectedRows.filter((r) => !shown.has(r)));
6844
5901
  else onSelectionChange?.([...selectedRows.filter((r) => !shown.has(r)), ...drawnIdx]);
6845
5902
  };
6846
- const toggleRow = (idx) => {
5903
+ const anchor = useRef(null);
5904
+ const toggleRow = (idx, shiftKey2 = false) => {
5905
+ const from = anchor.current === null ? -1 : drawnIdx.indexOf(anchor.current);
5906
+ const to = drawnIdx.indexOf(idx);
5907
+ anchor.current = idx;
5908
+ if (shiftKey2 && from >= 0 && to >= 0 && from !== to) {
5909
+ const run = drawnIdx.slice(Math.min(from, to), Math.max(from, to) + 1);
5910
+ onSelectionChange?.([...selectedRows.filter((r) => !run.includes(r)), ...run]);
5911
+ return;
5912
+ }
6847
5913
  if (selectedSet.has(idx)) onSelectionChange?.(selectedRows.filter((r) => r !== idx));
6848
5914
  else onSelectionChange?.([...selectedRows, idx]);
6849
5915
  };
@@ -6998,7 +6064,7 @@ function Table({
6998
6064
  cardHeading,
6999
6065
  body
7000
6066
  ] });
7001
- const control = pager && !loading && data.length > 0 ? /* @__PURE__ */ jsx(Pagination, { ...pager.props }) : null;
6067
+ const control = pager && data.length > 0 ? /* @__PURE__ */ jsx(Pagination, { ...pager.props }) : null;
7002
6068
  const listRoot = control ? void 0 : setRoot;
7003
6069
  const listClass = control ? void 0 : className;
7004
6070
  const withControl = (list2) => control ? /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-gs-paged": "", className: cn("flex flex-col min-w-0", className), children: [
@@ -7033,15 +6099,23 @@ function Table({
7033
6099
  (same ?? (row.tabIndex >= 0 ? row : controls[0]))?.focus({ preventScroll: true });
7034
6100
  }, [isMobile, rootRef]);
7035
6101
  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" }) });
6102
+ if (loading && data.length === 0) {
6103
+ const skeletonCards = /* @__PURE__ */ jsxs(Fragment, { children: [
6104
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: LOADING_LABEL }),
6105
+ /* @__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: [
6106
+ /* @__PURE__ */ jsx(SkeletonLine, { width: "56%", className: "flex-1" }),
6107
+ /* @__PURE__ */ jsx(SkeletonLine, { width: SKELETON_WIDTHS[(r + c) % SKELETON_WIDTHS.length], end: true, className: "flex-1" })
6108
+ ] }, col.key)) }, r)) })
6109
+ ] });
6110
+ if (title) return titledCards(skeletonCards);
6111
+ return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", className, children: skeletonCards });
7039
6112
  }
7040
6113
  if (data.length === 0 && emptyState) {
7041
6114
  if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", children: emptyState }));
7042
6115
  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
6116
  }
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: [
6117
+ 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("relative 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: [
6118
+ loading && data.length > 0 && /* @__PURE__ */ jsx(RefreshLine, { className: "inset-x-[4px] rounded-full" }),
7045
6119
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select`, children: "Select" }),
7046
6120
  drawn.map(({ row, originalIdx }) => {
7047
6121
  const isSelected = selectedSet.has(originalIdx);
@@ -7085,7 +6159,7 @@ function Table({
7085
6159
  } : void 0,
7086
6160
  children: [
7087
6161
  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` }),
6162
+ /* @__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
6163
  columns.map((col, c) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-ds-2 text-sm", children: [
7090
6164
  /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[var(--ds-text-secondary)] font-medium text-xs uppercase tracking-wider", children: col.header }),
7091
6165
  /* @__PURE__ */ jsx(CardValue, { value: row[col.key], id: lead + c === nameIndex ? `${headingId}-card-name-${idPart(id)}` : void 0 })
@@ -7129,7 +6203,7 @@ function Table({
7129
6203
  return withControl(title ? titledCards(cards) : cards);
7130
6204
  }
7131
6205
  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: [
6206
+ 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
6207
  /* @__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
6208
  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(
7135
6209
  Checkbox,
@@ -7171,21 +6245,18 @@ function Table({
7171
6245
  "inline-flex items-center gap-ds-1 uppercase tracking-wider cursor-pointer select-none",
7172
6246
  "[font:inherit] text-inherit hover:text-[var(--ds-text-primary)]",
7173
6247
  "rounded-[var(--ds-radius-sm)]",
6248
+ // in a right-aligned column the indicator goes before the label, so the label ends where the
6249
+ // column's values end; the sorted column's label in full colour (the look check, 14 October).
6250
+ // Set at the line's top, not its baseline: reversed, the button takes its baseline from the
6251
+ // indicator, which has no text, and stood 2 px above the other headers' labels
6252
+ headerAlign === "right" && "flex-row-reverse",
6253
+ "align-top",
6254
+ isActiveSort && "text-[var(--ds-text-primary)]",
7174
6255
  FOCUS_RING
7175
6256
  ),
7176
6257
  children: [
7177
6258
  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
- )
6259
+ /* @__PURE__ */ jsx(SortIndicator, { sorted: isActiveSort ? sortDir : false })
7189
6260
  ]
7190
6261
  }
7191
6262
  )
@@ -7195,14 +6266,15 @@ function Table({
7195
6266
  );
7196
6267
  })
7197
6268
  ] }) }),
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(
6269
+ /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: loading && data.length === 0 ? (
6270
+ // the rows' shape on a first load (item 16), each cell's bar where its text will be; the rows are hidden
6271
+ // from a screen reader, which hears the status beside the table
6272
+ 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: [
6273
+ 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" }) }),
6274
+ 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") }),
6275
+ 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))
6276
+ ] }, `skeleton-${r}`))
6277
+ ) : data.length === 0 && emptyState ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
7206
6278
  "td",
7207
6279
  {
7208
6280
  colSpan: cellCount,
@@ -7246,7 +6318,7 @@ function Table({
7246
6318
  }
7247
6319
  } : void 0,
7248
6320
  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` }) }),
6321
+ 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` }) }),
7250
6322
  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(
7251
6323
  "button",
7252
6324
  {
@@ -7274,9 +6346,9 @@ function Table({
7274
6346
  const whole2 = !kind && renderedKinds.has(col.key);
7275
6347
  let inner;
7276
6348
  if (truncLines > 0) {
7277
- inner = /* @__PURE__ */ jsx(TruncCell, { lines: truncLines, children: cellContent });
6349
+ inner = /* @__PURE__ */ jsx(CutCell, { lines: truncLines, children: cellContent });
7278
6350
  } 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 });
6351
+ inner = /* @__PURE__ */ jsx(CutEmail, { children: cellContent });
7280
6352
  } else {
7281
6353
  inner = cellContent;
7282
6354
  }
@@ -7335,10 +6407,12 @@ function Table({
7335
6407
  ref: listRoot,
7336
6408
  "data-ds-list": "",
7337
6409
  "data-gs": "table",
7338
- className: cn("w-full min-w-0 flex flex-col", FRAME_CLASSES, FRAME_FOCUS_RING, control && "min-h-0", listClass),
6410
+ className: cn("relative w-full min-w-0 flex flex-col", FRAME_CLASSES, FRAME_FOCUS_RING, control && "min-h-0", listClass),
7339
6411
  onFocus: noteFocus,
7340
6412
  onBlur: forgetFocus,
7341
6413
  children: [
6414
+ loading && data.length > 0 && /* @__PURE__ */ jsx(RefreshLine, { className: "inset-x-0 rounded-t-[inherit]" }),
6415
+ loading && data.length === 0 && /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: LOADING_LABEL }),
7342
6416
  title && /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: true }),
7343
6417
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select-all`, children: "Select all rows shown" }),
7344
6418
  selectable && /* @__PURE__ */ jsx("span", { hidden: true, id: `${headingId}-select`, children: "Select" }),
@@ -7630,7 +6704,12 @@ var NEUTRAL = {
7630
6704
  text: "var(--ds-text-primary)",
7631
6705
  edge: "var(--ds-control-edge-strong)"
7632
6706
  };
7633
- function EventBar({ event, category, compact, onOpen, inert, showTime = true, continuesBefore, continuesAfter, className, style }) {
6707
+
6708
+ // ../../showcase/src/components/Calendar/styles.ts
6709
+ var TODAY_CIRCLE = "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)] font-bold";
6710
+ var CARD_CORNER = "calc(var(--ds-radius-lg) - var(--ds-border-width))";
6711
+ var FLUSH_EDGE = { "data-flush-edge": "" };
6712
+ function EventBar({ event, category, compact, onOpen, inert, showTime = true, continuesBefore, continuesAfter, flushEdge, className, style }) {
7634
6713
  const ref = useRef(null);
7635
6714
  const colours = eventColours(category);
7636
6715
  const words = useDateWords();
@@ -7673,6 +6752,7 @@ function EventBar({ event, category, compact, onOpen, inert, showTime = true, co
7673
6752
  ref,
7674
6753
  type: "button",
7675
6754
  "data-calendar-event": event.id,
6755
+ ...flushEdge ? FLUSH_EDGE : {},
7676
6756
  className: cn(
7677
6757
  "w-full text-left text-xs leading-tight px-ds-1.5 py-ds-0.5",
7678
6758
  look,
@@ -7896,9 +6976,6 @@ function DayList({ day, events, categories, anchor, onOpenEvent, onNewEvent, onC
7896
6976
  document.body
7897
6977
  );
7898
6978
  }
7899
-
7900
- // ../../showcase/src/components/Calendar/styles.ts
7901
- var TODAY_CIRCLE = "bg-[var(--ds-accent-primary)] [background-image:var(--ds-gradient)] text-[var(--ds-text-on-primary)] font-bold";
7902
6979
  var LANES = 3;
7903
6980
  var LANE_H = 20;
7904
6981
  var LANE_GAP = 4;
@@ -7958,6 +7035,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
7958
7035
  if (!activeDay || !isSameDay(day, activeDay)) setActiveDay(day);
7959
7036
  },
7960
7037
  onClick: (e) => choose(day, e.currentTarget),
7038
+ style: lastRow && di === 0 ? { borderBottomLeftRadius: CARD_CORNER } : lastRow && di === week.length - 1 ? { borderBottomRightRadius: CARD_CORNER } : void 0,
7961
7039
  className: cn(
7962
7040
  "relative min-h-[80px] sm:min-h-[100px] border-[var(--ds-border-subtle)]",
7963
7041
  !lastRow && "border-b-[length:var(--ds-divider-width)]",
@@ -7990,6 +7068,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
7990
7068
  onOpen: onOpenEvent,
7991
7069
  continuesBefore: s.continuesBefore,
7992
7070
  continuesAfter: s.continuesAfter,
7071
+ flushEdge: true,
7993
7072
  className: "absolute z-[1] flex items-center",
7994
7073
  style: {
7995
7074
  top: NUMBER_ROW + s.lane * (LANE_H + LANE_GAP),
@@ -8008,6 +7087,7 @@ function MonthView({ date, events, categories, selectedDate, onDayClick, canStar
8008
7087
  "aria-expanded": !!list2 && isSameDay(list2.day, day),
8009
7088
  className: cn("absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)]", FOCUS_RING),
8010
7089
  style: { top: NUMBER_ROW + (LANES - 1) * (LANE_H + LANE_GAP), left: INSET, height: LANE_H },
7090
+ ...FLUSH_EDGE,
8011
7091
  onClick: (e) => {
8012
7092
  e.stopPropagation();
8013
7093
  openList(day, e.currentTarget);
@@ -8085,6 +7165,7 @@ function TimedEvent({ placed, category, onOpen, onFocusEvent }) {
8085
7165
  {
8086
7166
  type: "button",
8087
7167
  "data-calendar-event": event.id,
7168
+ ...FLUSH_EDGE,
8088
7169
  "aria-label": eventName(event, category, words),
8089
7170
  className: cn(
8090
7171
  // a short event keeps its title whole: no padding above and below it
@@ -8132,7 +7213,10 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8132
7213
  const allDayHeight = Math.max(28, 4 + allDay.lanes * (LANE_H2 + LANE_GAP2) + 2);
8133
7214
  const rootRef = useRef(null);
8134
7215
  const scrollerRef = useRef(null);
7216
+ const allDayRef = useRef(null);
8135
7217
  const syncingScroll = useRef(false);
7218
+ useRevealFocus(allDayRef);
7219
+ useRevealFocus(scrollerRef);
8136
7220
  const { ref: edgesRef, measure: measureEdges, fadeProps: edgeFade } = useScrollEdges();
8137
7221
  const [list2, setList] = useState(null);
8138
7222
  const handleScroll = useCallback((e) => {
@@ -8236,9 +7320,9 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8236
7320
  case "End":
8237
7321
  return cellKey(days[days.length - 1], hour);
8238
7322
  case "PageUp":
8239
- return cellKey(addDays2(day, -7), hour);
7323
+ return cellKey(addDays(day, -7), hour);
8240
7324
  case "PageDown":
8241
- return cellKey(addDays2(day, 7), hour);
7325
+ return cellKey(addDays(day, 7), hour);
8242
7326
  default:
8243
7327
  return null;
8244
7328
  }
@@ -8265,7 +7349,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8265
7349
  ] }),
8266
7350
  /* @__PURE__ */ jsxs("div", { className: "flex border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-bg-muted)]/30", children: [
8267
7351
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] text-2xs text-[var(--ds-text-muted)] px-ds-1 py-ds-1 text-right", children: "all-day" }),
8268
- /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", "data-week-scroll": true, children: /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": "All day", "aria-readonly": "true", style: { minWidth: stripWidth }, children: /* @__PURE__ */ jsx("div", { role: "row", className: "grid", style: { gridTemplateColumns: dayGridColumns, height: allDayHeight }, children: days.map((day, i) => {
7352
+ /* @__PURE__ */ jsx("div", { ref: allDayRef, ...edgeFade, className: "flex-1 overflow-x-auto gs-scrollbar-none", style: { ...edgeFade.style, scrollPaddingInline: FADE }, "data-week-scroll": true, children: /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": "All day", "aria-readonly": "true", style: { minWidth: stripWidth }, children: /* @__PURE__ */ jsx("div", { role: "row", className: "grid", style: { gridTemplateColumns: dayGridColumns, height: allDayHeight }, children: days.map((day, i) => {
8269
7353
  const key = cellKey(day, null);
8270
7354
  const more = allDay.more[i];
8271
7355
  return /* @__PURE__ */ jsxs(
@@ -8281,24 +7365,31 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8281
7365
  onClick: () => onDayClick(day),
8282
7366
  className: cn("relative cursor-pointer", FOCUS_INSET, i > 0 && "border-l-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"),
8283
7367
  children: [
8284
- allDay.segments.filter((s) => s.startCol === i).map((s) => /* @__PURE__ */ jsx(
8285
- EventBar,
8286
- {
8287
- event: s.event,
8288
- category: categories[s.event.category ?? ""],
8289
- onOpen: onOpenEvent,
8290
- continuesBefore: s.continuesBefore,
8291
- continuesAfter: s.continuesAfter,
8292
- className: "absolute z-[1] flex items-center",
8293
- style: {
8294
- top: 2 + s.lane * (LANE_H2 + LANE_GAP2),
8295
- height: LANE_H2,
8296
- left: s.continuesBefore ? 0 : 2,
8297
- width: `calc(${s.span} * (100% + var(--ds-divider-width)) - var(--ds-divider-width) - ${s.continuesBefore ? 0 : 2}px - ${s.continuesAfter ? 0 : 2}px)`
8298
- }
8299
- },
8300
- `${s.event.id}-${s.startCol}`
8301
- )),
7368
+ allDay.segments.filter((s) => s.startCol === i).map((s) => {
7369
+ const start = s.continuesBefore ? 0 : 2;
7370
+ const end = s.continuesAfter ? 0 : 2;
7371
+ return /* @__PURE__ */ jsx(
7372
+ EventBar,
7373
+ {
7374
+ event: s.event,
7375
+ category: categories[s.event.category ?? ""],
7376
+ onOpen: onOpenEvent,
7377
+ continuesBefore: s.continuesBefore,
7378
+ continuesAfter: s.continuesAfter,
7379
+ flushEdge: true,
7380
+ className: "absolute z-[1] flex items-center",
7381
+ style: {
7382
+ top: 2 + s.lane * (LANE_H2 + LANE_GAP2),
7383
+ height: LANE_H2,
7384
+ left: start,
7385
+ // across its days from its own cell's box: the first day's cell has no divider on its left, so
7386
+ // its box is a whole column; a later day's box starts after its divider (Sprint 25.27)
7387
+ width: s.startCol === 0 ? `calc(${s.span} * 100% - ${start + end}px)` : `calc(${s.span} * (100% + var(--ds-divider-width)) - var(--ds-divider-width) - ${start + end}px)`
7388
+ }
7389
+ },
7390
+ `${s.event.id}-${s.startCol}`
7391
+ );
7392
+ }),
8302
7393
  more.length > 0 && /* @__PURE__ */ jsxs(
8303
7394
  "button",
8304
7395
  {
@@ -8307,6 +7398,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8307
7398
  "aria-expanded": !!list2 && isSameDay(list2.day, day),
8308
7399
  className: cn("absolute z-[1] text-2xs text-[var(--ds-accent-text,var(--ds-accent-primary))] font-medium cursor-pointer hover:underline px-ds-1.5 rounded-[var(--ds-radius-sm)]", FOCUS_RING),
8309
7400
  style: { top: 2 + (ALL_DAY_LANES - 1) * (LANE_H2 + LANE_GAP2), left: 2, height: LANE_H2 },
7401
+ ...FLUSH_EDGE,
8310
7402
  onClick: (e) => {
8311
7403
  e.stopPropagation();
8312
7404
  setList({ day, anchor: e.currentTarget });
@@ -8324,9 +7416,9 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8324
7416
  );
8325
7417
  }) }) }) })
8326
7418
  ] }),
8327
- /* @__PURE__ */ jsxs("div", { ref: scrollerRef, className: "flex flex-1 min-h-0 overflow-y-auto", "data-week-hours": "", children: [
7419
+ /* @__PURE__ */ jsxs("div", { ref: scrollerRef, className: "flex flex-1 min-h-0 overflow-y-auto", style: { scrollPaddingBlock: FOCUS_ROOM }, "data-week-hours": "", children: [
8328
7420
  /* @__PURE__ */ jsx("div", { className: "w-14 shrink-0 relative border-r-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", style: { height: GRID_HEIGHT }, "aria-hidden": "true", children: HOURS.map((h) => /* @__PURE__ */ jsx("div", { className: "absolute right-0 pr-ds-2 text-2xs text-[var(--ds-text-muted)] -translate-y-1/2 tabular-nums", style: { top: h * HOUR_HEIGHT }, children: h > 0 && formatHourLabel(h) }, h)) }),
8329
- /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 self-start overflow-x-auto overflow-y-hidden gs-scrollbar-none", style: { height: GRID_HEIGHT }, "data-week-scroll": true, children: /* @__PURE__ */ jsxs("div", { className: "relative", style: { height: GRID_HEIGHT, minWidth: stripWidth }, children: [
7421
+ /* @__PURE__ */ jsx("div", { ...edgeFade, className: "flex-1 self-start overflow-x-auto overflow-y-hidden gs-scrollbar-none", style: { ...edgeFade.style, height: GRID_HEIGHT, scrollPaddingInline: FADE }, "data-week-scroll": true, children: /* @__PURE__ */ jsxs("div", { className: "relative", style: { height: GRID_HEIGHT, minWidth: stripWidth }, children: [
8330
7422
  /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": formatWeekRange(date, weekStartsOn), "aria-readonly": "true", className: "absolute inset-0", children: HOURS.map((hour) => /* @__PURE__ */ jsx("div", { role: "row", className: "grid", style: { gridTemplateColumns: dayGridColumns, height: HOUR_HEIGHT }, children: days.map((day, i) => {
8331
7423
  const key = cellKey(day, hour);
8332
7424
  return /* @__PURE__ */ jsx(
@@ -8340,6 +7432,7 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8340
7432
  if (activeKey !== key) setActiveKey(key);
8341
7433
  },
8342
7434
  onClick: chooseHour(day, hour),
7435
+ style: hour === HOURS.length - 1 && i === days.length - 1 ? { borderBottomRightRadius: CARD_CORNER } : void 0,
8343
7436
  className: cn(
8344
7437
  "cursor-pointer border-t-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)] hover:bg-[var(--ds-bg-muted)]/40",
8345
7438
  FOCUS_INSET,
@@ -8481,6 +7574,7 @@ function MonthRow({
8481
7574
  {
8482
7575
  type: "button",
8483
7576
  "aria-label": `${words.monthName(month, "short")} ${year}`,
7577
+ style: month === 11 ? { borderBottomLeftRadius: CARD_CORNER } : void 0,
8484
7578
  className: cn(
8485
7579
  "sticky left-0 z-10 flex items-center justify-center px-ds-2 text-xs font-semibold text-[var(--ds-text-primary)]",
8486
7580
  "bg-[var(--ds-bg-muted)] cursor-pointer hover:text-[var(--ds-accent-ink)] transition-colors",
@@ -9036,9 +8130,9 @@ function Calendar({
9036
8130
  return getMonthGrid(currentDate.getFullYear(), currentDate.getMonth(), weekStartsOn)[0][0];
9037
8131
  }, [currentView, currentDate, weekStartsOn]);
9038
8132
  const drawnTo = useMemo(() => {
9039
- if (currentView === "week") return addDays2(drawnFrom, 7);
8133
+ if (currentView === "week") return addDays(drawnFrom, 7);
9040
8134
  if (currentView === "year") return new Date(drawnFrom.getFullYear() + 1, 0, 1);
9041
- return addDays2(drawnFrom, 42);
8135
+ return addDays(drawnFrom, 42);
9042
8136
  }, [currentView, drawnFrom]);
9043
8137
  const { drawnEvents, unreadableRepeats } = useMemo(() => {
9044
8138
  const out = [];
@@ -9076,7 +8170,7 @@ function Calendar({
9076
8170
  if (currentView === "month") {
9077
8171
  setCurrentDate(addMonths(currentDate, direction));
9078
8172
  } else if (currentView === "week") {
9079
- setCurrentDate(addDays2(currentDate, direction * 7));
8173
+ setCurrentDate(addDays(currentDate, direction * 7));
9080
8174
  } else {
9081
8175
  setCurrentDate(new Date(currentDate.getFullYear() + direction, currentDate.getMonth(), 1));
9082
8176
  }
@@ -9240,7 +8334,7 @@ function Calendar({
9240
8334
  "bg-[var(--ds-bg-muted)]",
9241
8335
  "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]"
9242
8336
  ),
9243
- style: { padding: "var(--ds-space-1) var(--ds-space-1)" },
8337
+ style: { padding: atLeastFocusRoom("var(--ds-space-1)") },
9244
8338
  children: [
9245
8339
  /* @__PURE__ */ jsx(
9246
8340
  "button",
@@ -9458,9 +8552,9 @@ function AvatarGroup({ items, max = 4, size = "sm", ring = "canvas", onOverflowC
9458
8552
  },
9459
8553
  i
9460
8554
  );
9461
- return item.name ? /* @__PURE__ */ jsx(Tooltip, { content: item.name, children: avatar }, i) : avatar;
8555
+ return item.name ? /* @__PURE__ */ jsx(AutoTooltip, { content: item.name, children: avatar }, i) : avatar;
9462
8556
  }),
9463
- counter && (onOverflowClick ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onOverflowClick, "aria-label": `Show ${hidden.length} more`, className: cn("inline-flex rounded-[var(--ds-radius-full)]", FOCUS_RING), children: hiddenNames ? /* @__PURE__ */ jsx(Tooltip, { content: hiddenNames, children: counter }) : counter }) : hiddenNames ? /* @__PURE__ */ jsx(Tooltip, { content: hiddenNames, children: counter }) : counter)
8557
+ counter && (onOverflowClick ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onOverflowClick, "aria-label": `Show ${hidden.length} more`, className: cn("inline-flex rounded-[var(--ds-radius-full)]", FOCUS_RING), children: hiddenNames ? /* @__PURE__ */ jsx(AutoTooltip, { content: hiddenNames, children: counter }) : counter }) : hiddenNames ? /* @__PURE__ */ jsx(AutoTooltip, { content: hiddenNames, children: counter }) : counter)
9464
8558
  ] });
9465
8559
  }
9466
8560
  var defaultIcons = {
@@ -9907,7 +9001,7 @@ function TruncLabel2({ children }) {
9907
9001
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
9908
9002
  const label = /* @__PURE__ */ jsx("span", { ref, className: "truncate", children });
9909
9003
  if (!isTruncated) return label;
9910
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: label });
9004
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: label });
9911
9005
  }
9912
9006
  function foldersHolding(nodes, selected) {
9913
9007
  const ids = [];
@@ -9936,7 +9030,9 @@ function TreeView({
9936
9030
  defaultChecked = [],
9937
9031
  onSelect,
9938
9032
  onCheck,
9939
- className
9033
+ className,
9034
+ "aria-label": ariaLabel,
9035
+ "aria-labelledby": ariaLabelledBy
9940
9036
  }) {
9941
9037
  const [ownSelected, setOwnSelected] = useState(() => new Set(defaultSelected));
9942
9038
  const selected = useMemo(() => selectedProp ? new Set(selectedProp) : ownSelected, [selectedProp, ownSelected]);
@@ -10053,7 +9149,7 @@ function TreeView({
10053
9149
  },
10054
9150
  [flatNodes, baseId, expanded, toggleExpand, checkable, handleCheck, selectable, handleSelect]
10055
9151
  );
10056
- return /* @__PURE__ */ jsx("div", { role: "tree", className: cn("text-sm", className), children: nodes.map((node) => /* @__PURE__ */ jsx(
9152
+ return /* @__PURE__ */ jsx("div", { role: "tree", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className: cn("text-sm", className), children: nodes.map((node) => /* @__PURE__ */ jsx(
10057
9153
  TreeNodeRow,
10058
9154
  {
10059
9155
  node,
@@ -10195,6 +9291,32 @@ function CheckboxIndicator2({ state, onClick }) {
10195
9291
  }
10196
9292
  );
10197
9293
  }
9294
+ function useMediaQuery(query) {
9295
+ const subscribe = useCallback((onChange) => {
9296
+ const mql = window.matchMedia(query);
9297
+ mql.addEventListener("change", onChange);
9298
+ return () => mql.removeEventListener("change", onChange);
9299
+ }, [query]);
9300
+ return useSyncExternalStore(
9301
+ subscribe,
9302
+ () => window.matchMedia(query).matches,
9303
+ () => false
9304
+ );
9305
+ }
9306
+ var ROUND_BUTTON = cn(
9307
+ "flex items-center justify-center w-8 h-8",
9308
+ "rounded-[var(--ds-radius-full)]",
9309
+ "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
9310
+ "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
9311
+ "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
9312
+ FOCUS_RING
9313
+ );
9314
+ var ROTATION_BUTTON = cn(
9315
+ "flex items-center justify-center w-6 h-6 rounded-[var(--ds-radius-full)]",
9316
+ "bg-[color-mix(in_oklab,var(--ds-bg-surface-elevated)_70%,transparent)] [backdrop-filter:blur(6px)] [-webkit-backdrop-filter:blur(6px)]",
9317
+ "text-[var(--ds-text-primary)] transition-colors cursor-pointer hover:bg-[var(--ds-bg-surface-elevated)]",
9318
+ FOCUS_RING
9319
+ );
10198
9320
  function Carousel({
10199
9321
  children,
10200
9322
  autoPlay = false,
@@ -10202,6 +9324,8 @@ function Carousel({
10202
9324
  loop = false,
10203
9325
  showIndicators = true,
10204
9326
  showArrows = true,
9327
+ "aria-label": ariaLabel,
9328
+ "aria-labelledby": ariaLabelledBy,
10205
9329
  className,
10206
9330
  ref
10207
9331
  }) {
@@ -10210,32 +9334,45 @@ function Carousel({
10210
9334
  const [dragOffset, setDragOffset] = useState(0);
10211
9335
  const trackRef = useRef(null);
10212
9336
  const dragStart = useRef(null);
10213
- const pauseRef = useRef(false);
10214
9337
  const count = children.length;
9338
+ const reducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
9339
+ const [stopped, setStopped] = useState(null);
9340
+ const playing = autoPlay && count > 1 && !(stopped ?? reducedMotion);
9341
+ const [hovered, setHovered] = useState(false);
9342
+ const [focusInSlide, setFocusInSlide] = useState(false);
9343
+ const rotating = playing && !hovered && !dragging && !focusInSlide;
9344
+ const [said, setSaid] = useState("");
10215
9345
  const goTo = useCallback(
10216
9346
  (index) => {
10217
- if (loop) {
10218
- setCurrent((index % count + count) % count);
10219
- } else {
10220
- setCurrent(Math.max(0, Math.min(index, count - 1)));
10221
- }
9347
+ const to = loop ? (index % count + count) % count : Math.max(0, Math.min(index, count - 1));
9348
+ setCurrent(to);
9349
+ setSaid(`Slide ${to + 1} of ${count}`);
10222
9350
  },
10223
9351
  [count, loop]
10224
9352
  );
10225
9353
  const next = useCallback(() => goTo(current + 1), [goTo, current]);
10226
9354
  const prev = useCallback(() => goTo(current - 1), [goTo, current]);
10227
9355
  useEffect(() => {
10228
- if (!autoPlay || count <= 1) return;
10229
- const interval = setInterval(() => {
10230
- if (!pauseRef.current) goTo(current + 1);
10231
- }, autoPlayInterval);
9356
+ if (!rotating) return;
9357
+ const interval = setInterval(() => setCurrent((c) => (c + 1) % count), autoPlayInterval);
10232
9358
  return () => clearInterval(interval);
10233
- }, [autoPlay, autoPlayInterval, current, count, goTo]);
9359
+ }, [rotating, autoPlayInterval, count, current]);
9360
+ const onFocus = () => {
9361
+ if (playing && inputModality() !== "pointer") setStopped(true);
9362
+ };
9363
+ const onSlidesBlur = (e) => {
9364
+ if (!e.currentTarget.contains(e.relatedTarget)) setFocusInSlide(false);
9365
+ };
9366
+ const onPointerEnter = (e) => {
9367
+ if (e.pointerType !== "touch") setHovered(true);
9368
+ };
9369
+ const onPointerLeave = (e) => {
9370
+ if (e.pointerType !== "touch") setHovered(false);
9371
+ };
10234
9372
  const handlePointerDown = useCallback((e) => {
10235
9373
  if (e.pointerType === "mouse" && e.button !== 0) return;
10236
9374
  dragStart.current = { x: e.clientX, time: Date.now() };
10237
9375
  setDragging(true);
10238
- pauseRef.current = true;
10239
9376
  e.target.setPointerCapture(e.pointerId);
10240
9377
  }, []);
10241
9378
  const handlePointerMove = useCallback(
@@ -10260,9 +9397,6 @@ function Carousel({
10260
9397
  dragStart.current = null;
10261
9398
  setDragging(false);
10262
9399
  setDragOffset(0);
10263
- setTimeout(() => {
10264
- pauseRef.current = false;
10265
- }, 1e3);
10266
9400
  },
10267
9401
  [next, prev]
10268
9402
  );
@@ -10273,31 +9407,49 @@ function Carousel({
10273
9407
  {
10274
9408
  ref,
10275
9409
  "data-gs-clip": "",
9410
+ role: "group",
9411
+ "aria-roledescription": "carousel",
9412
+ "aria-label": ariaLabel,
9413
+ "aria-labelledby": ariaLabelledBy,
10276
9414
  className: cn(
10277
9415
  "relative overflow-hidden rounded-[var(--ds-radius-md)]",
10278
9416
  "bg-[var(--ds-bg-muted)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10279
9417
  className
10280
9418
  ),
10281
- onMouseEnter: () => {
10282
- pauseRef.current = true;
10283
- },
10284
- onMouseLeave: () => {
10285
- if (!dragging) pauseRef.current = false;
10286
- },
9419
+ onFocus,
9420
+ onPointerEnter,
9421
+ onPointerLeave,
10287
9422
  children: [
9423
+ autoPlay && count > 1 && /* @__PURE__ */ jsx(
9424
+ "button",
9425
+ {
9426
+ type: "button",
9427
+ "data-carousel-rotation": "",
9428
+ onClick: () => setStopped(playing),
9429
+ "aria-label": playing ? "Stop automatic slide show" : "Start automatic slide show",
9430
+ className: cn("absolute left-ds-2 top-ds-2 z-10", ROTATION_BUTTON),
9431
+ children: /* @__PURE__ */ jsx(Icon, { name: playing ? "pause" : "play", size: 12 })
9432
+ }
9433
+ ),
10288
9434
  /* @__PURE__ */ jsx(
10289
9435
  "div",
10290
9436
  {
10291
9437
  ref: trackRef,
10292
- className: cn("flex items-stretch", !dragging && "transition-transform duration-400 ease-[var(--ds-transition-ease)]"),
9438
+ className: cn("flex items-stretch", !dragging && "transition-transform duration-400 ease-[var(--ds-transition-ease)] motion-reduce:transition-none"),
10293
9439
  style: { transform: `translateX(calc(-${current * 100}% + ${dragOffset}px))` },
10294
9440
  onPointerDown: handlePointerDown,
10295
9441
  onPointerMove: handlePointerMove,
10296
9442
  onPointerUp: handlePointerUp,
10297
9443
  onPointerCancel: handlePointerUp,
10298
- 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))
9444
+ onFocus: () => setFocusInSlide(true),
9445
+ onBlur: onSlidesBlur,
9446
+ children: children.map((child, i) => (
9447
+ // each slide named by its place in the set; those out of view are out of reach, for Tab and assistive technology
9448
+ /* @__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)
9449
+ ))
10299
9450
  }
10300
9451
  ),
9452
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: said }),
10301
9453
  showArrows && count > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
10302
9454
  /* @__PURE__ */ jsx(
10303
9455
  "button",
@@ -10306,16 +9458,7 @@ function Carousel({
10306
9458
  onClick: prev,
10307
9459
  disabled: !canPrev,
10308
9460
  "aria-label": "Previous slide",
10309
- className: cn(
10310
- "absolute left-ds-4 top-1/2 -translate-y-1/2 z-10",
10311
- "flex items-center justify-center w-8 h-8",
10312
- "rounded-[var(--ds-radius-full)]",
10313
- "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10314
- "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10315
- "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10316
- FOCUS_RING,
10317
- !canPrev && "opacity-0 pointer-events-none"
10318
- ),
9461
+ className: cn("absolute left-ds-4 top-1/2 -translate-y-1/2 z-10", ROUND_BUTTON, !canPrev && "opacity-0 pointer-events-none"),
10319
9462
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16, className: "rotate-180" })
10320
9463
  }
10321
9464
  ),
@@ -10326,16 +9469,7 @@ function Carousel({
10326
9469
  onClick: next,
10327
9470
  disabled: !canNext,
10328
9471
  "aria-label": "Next slide",
10329
- className: cn(
10330
- "absolute right-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
- !canNext && "opacity-0 pointer-events-none"
10338
- ),
9472
+ className: cn("absolute right-ds-4 top-1/2 -translate-y-1/2 z-10", ROUND_BUTTON, !canNext && "opacity-0 pointer-events-none"),
10339
9473
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
10340
9474
  }
10341
9475
  )
@@ -10346,6 +9480,7 @@ function Carousel({
10346
9480
  type: "button",
10347
9481
  onClick: () => goTo(i),
10348
9482
  "aria-label": `Go to slide ${i + 1}`,
9483
+ "aria-current": i === current || void 0,
10349
9484
  className: cn(
10350
9485
  "w-2 h-2 rounded-[var(--ds-radius-full)] transition-all cursor-pointer",
10351
9486
  FOCUS_RING,
@@ -10423,7 +9558,7 @@ function ActionsRow({ message, onAction }) {
10423
9558
  ),
10424
9559
  children: message.actions.map((a) => {
10425
9560
  const { id, label, icon } = resolveAction(a);
10426
- return /* @__PURE__ */ jsx(Tooltip, { content: label, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", "aria-label": label, onClick: () => run(id), children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 14 }) }) }, id);
9561
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: label, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", "aria-label": label, onClick: () => run(id), children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 14 }) }) }, id);
10427
9562
  })
10428
9563
  }
10429
9564
  );
@@ -10739,15 +9874,15 @@ function ChatPanel({
10739
9874
  ),
10740
9875
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
10741
9876
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-ds-2", children: [
10742
- isTruncated ? /* @__PURE__ */ jsx(Tooltip, { content: title, children: heading }) : heading,
9877
+ isTruncated ? /* @__PURE__ */ jsx(AutoTooltip, { content: title, children: heading }) : heading,
10743
9878
  status && /* @__PURE__ */ jsx(StatusDot, { variant: STATUS_VARIANT[status.tone ?? "success"], label: status.label })
10744
9879
  ] }),
10745
9880
  description && /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-[var(--ds-text-muted)]", children: description })
10746
9881
  ] }),
10747
9882
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-ds-1", children: [
10748
9883
  actions,
10749
- onNewConversation && /* @__PURE__ */ jsx(Tooltip, { content: "New conversation", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "New conversation", onClick: onNewConversation, children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 16 }) }) }),
10750
- onExpandedChange && /* @__PURE__ */ jsx(Tooltip, { content: expanded ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
9884
+ onNewConversation && /* @__PURE__ */ jsx(AutoTooltip, { content: "New conversation", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "New conversation", onClick: onNewConversation, children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 16 }) }) }),
9885
+ onExpandedChange && /* @__PURE__ */ jsx(AutoTooltip, { content: expanded ? "Collapse" : "Expand", children: /* @__PURE__ */ jsx(
10751
9886
  Button,
10752
9887
  {
10753
9888
  type: "button",
@@ -10759,7 +9894,7 @@ function ChatPanel({
10759
9894
  children: /* @__PURE__ */ jsx(Icon, { name: expanded ? "minimize-2" : "maximize-2", size: 16 })
10760
9895
  }
10761
9896
  ) }),
10762
- /* @__PURE__ */ jsx(Tooltip, { content: "Close", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "Close", onClick: onClose, ...{ [OVERLAY_CLOSE_ATTR]: "" }, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 }) }) })
9897
+ /* @__PURE__ */ jsx(AutoTooltip, { content: "Close", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "Close", onClick: onClose, ...{ [OVERLAY_CLOSE_ATTR]: "" }, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 }) }) })
10763
9898
  ] })
10764
9899
  ]
10765
9900
  }
@@ -10775,58 +9910,6 @@ function ChatPanel({
10775
9910
  )
10776
9911
  ] });
10777
9912
  }
10778
- var TOAST_INSET_VARS = {
10779
- "bottom-right": "--ds-toast-inset-bottom-right",
10780
- "bottom-left": "--ds-toast-inset-bottom-left",
10781
- right: "--ds-toast-inset-right",
10782
- left: "--ds-toast-inset-left",
10783
- top: "--ds-toast-inset-top"
10784
- };
10785
- var LAUNCHER_INSET_PRIORITY = 1;
10786
- var PANEL_INSET_PRIORITY = 2;
10787
- var NAVBAR_INSET_PRIORITY = 1;
10788
- var registry = /* @__PURE__ */ new WeakMap();
10789
- var seq = 0;
10790
- function claimsFor(target) {
10791
- let entry = registry.get(target);
10792
- if (!entry) {
10793
- entry = { "bottom-right": [], "bottom-left": [], right: [], left: [], top: [] };
10794
- registry.set(target, entry);
10795
- }
10796
- return entry;
10797
- }
10798
- function winner(claims) {
10799
- let best = null;
10800
- for (const c of claims) {
10801
- if (!best || c.priority > best.priority || c.priority === best.priority && c.seq > best.seq) best = c;
10802
- }
10803
- return best;
10804
- }
10805
- function apply(target, corner) {
10806
- const style = target.style;
10807
- if (!style) return;
10808
- const best = winner(claimsFor(target)[corner]);
10809
- if (best) style.setProperty(TOAST_INSET_VARS[corner], best.inset);
10810
- else style.removeProperty(TOAST_INSET_VARS[corner]);
10811
- }
10812
- function claimCornerInset(corner, inset, priority, target) {
10813
- const claim = { inset, priority, seq: ++seq };
10814
- const list2 = claimsFor(target)[corner];
10815
- list2.push(claim);
10816
- apply(target, corner);
10817
- return () => {
10818
- const i = list2.indexOf(claim);
10819
- if (i >= 0) list2.splice(i, 1);
10820
- apply(target, corner);
10821
- };
10822
- }
10823
- function useCornerInset(corner, inset, priority, target) {
10824
- useEffect(() => {
10825
- if (!corner || !inset) return;
10826
- const el = target ?? document.documentElement;
10827
- return claimCornerInset(corner, inset, priority, el);
10828
- }, [corner, inset, priority, target]);
10829
- }
10830
9913
  function Sheet({ open, onClose: onCloseProp, title, children, footer, dirty, onDiscard, snapPoints = [0.5, 0.92], container, flush, keepMounted = false, id, labelledBy }) {
10831
9914
  const { marked, mark } = useOverlayMark(open);
10832
9915
  const guard = useDirtyGuard(dirty ?? marked, onCloseProp, onDiscard);
@@ -10915,7 +9998,7 @@ function CornerWindow({ open, onClose, edge, align, width, height, offset, ancho
10915
9998
  const containerEl = container instanceof Element ? container : null;
10916
9999
  useCornerInset(
10917
10000
  open && mounted && !expanded ? claim : null,
10918
- vertical === "bottom" ? `calc(${bottom} + ${height} + var(--ds-space-3))` : `calc(${x} + ${width} + var(--ds-space-3))`,
10001
+ vertical === "bottom" ? `calc(${bottom} + ${height} + var(--ds-space-3))` : besideRoom(`calc(${x} + ${width} + var(--ds-space-3))`),
10919
10002
  PANEL_INSET_PRIORITY,
10920
10003
  containerEl
10921
10004
  );
@@ -11052,7 +10135,7 @@ function DockedColumn({ open, onClose, side, width, floating, navbarFloating, si
11052
10135
  });
11053
10136
  useCornerInset(
11054
10137
  open && mounted ? side : null,
11055
- floating ? `calc(${currentWidth} + ${GUTTER} + var(--ds-space-4))` : `calc(${currentWidth} + var(--ds-space-4))`,
10138
+ besideRoom(floating ? `calc(${currentWidth} + ${GUTTER} + var(--ds-space-4))` : `calc(${currentWidth} + var(--ds-space-4))`),
11056
10139
  PANEL_INSET_PRIORITY
11057
10140
  );
11058
10141
  if (!mounted) return null;
@@ -11120,7 +10203,7 @@ function TruncText({ children, className }) {
11120
10203
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
11121
10204
  const text = /* @__PURE__ */ jsx("p", { ref, className: cn("truncate", className), children });
11122
10205
  if (!isTruncated) return text;
11123
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: text });
10206
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: text });
11124
10207
  }
11125
10208
  var StackedList = forwardRef(function StackedList2({
11126
10209
  items,
@@ -11253,9 +10336,9 @@ var StackedList = forwardRef(function StackedList2({
11253
10336
 
11254
10337
  // ../../showcase/src/lib/time-days.ts
11255
10338
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
11256
- var DAY_MS2 = 864e5;
10339
+ var DAY_MS = 864e5;
11257
10340
  var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
11258
- var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS2);
10341
+ var utcDay = (y, m, d) => Math.round(Date.UTC(y, m, d) / DAY_MS);
11259
10342
  function dayOfDate(d) {
11260
10343
  const t = d.getTime();
11261
10344
  if (Number.isNaN(t)) return NaN;
@@ -11264,7 +10347,7 @@ function dayOfDate(d) {
11264
10347
  first.setHours(0, 0, 0, 0);
11265
10348
  if (first.getTime() === t) return day;
11266
10349
  const ms = ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
11267
- return day + ms / DAY_MS2;
10350
+ return day + ms / DAY_MS;
11268
10351
  }
11269
10352
  function dayOf(value) {
11270
10353
  if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
@@ -11274,13 +10357,13 @@ function dayOf(value) {
11274
10357
  if (!m) return dayOfDate(new Date(value));
11275
10358
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
11276
10359
  const day = utcDay(y, mo, d);
11277
- const back = new Date(day * DAY_MS2);
10360
+ const back = new Date(day * DAY_MS);
11278
10361
  return back.getUTCFullYear() === y && back.getUTCMonth() === mo && back.getUTCDate() === d ? day : NaN;
11279
10362
  }
11280
10363
  function dateOfDay(day) {
11281
10364
  const whole2 = Math.floor(day);
11282
- const u = new Date(whole2 * DAY_MS2);
11283
- return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole2) * DAY_MS2));
10365
+ const u = new Date(whole2 * DAY_MS);
10366
+ return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole2) * DAY_MS));
11284
10367
  }
11285
10368
  function isZoned(value) {
11286
10369
  if (value instanceof Date) return !Number.isNaN(value.getTime());
@@ -11343,12 +10426,12 @@ function formatDateRange(start, end, words) {
11343
10426
  if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
11344
10427
  const timed = !isWholeDay(start) || !isWholeDay(until);
11345
10428
  const first = timed ? from : Math.floor(from);
11346
- const last2 = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
11347
- const a = new Date(first * DAY_MS2);
11348
- const b = new Date(last2 * DAY_MS2);
10429
+ const last = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
10430
+ const a = new Date(first * DAY_MS);
10431
+ const b = new Date(last * DAY_MS);
11349
10432
  const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
11350
10433
  const f = dayFormatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
11351
- return first === last2 ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
10434
+ return first === last ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
11352
10435
  }
11353
10436
 
11354
10437
  // ../../showcase/src/lib/time-scale.ts
@@ -11359,7 +10442,7 @@ function periodStart(unit, day, weekStartsOn) {
11359
10442
  const d = Math.floor(day);
11360
10443
  if (unit === "day") return d;
11361
10444
  if (unit === "week") return d - (weekday(d) - weekStartsOn + 7) % 7;
11362
- const u = new Date(d * DAY_MS2);
10445
+ const u = new Date(d * DAY_MS);
11363
10446
  const y = u.getUTCFullYear();
11364
10447
  const m = u.getUTCMonth();
11365
10448
  if (unit === "month") return utcDay(y, m, 1);
@@ -11369,7 +10452,7 @@ function periodStart(unit, day, weekStartsOn) {
11369
10452
  function periodAfter(unit, start) {
11370
10453
  if (unit === "day") return start + 1;
11371
10454
  if (unit === "week") return start + 7;
11372
- const u = new Date(start * DAY_MS2);
10455
+ const u = new Date(start * DAY_MS);
11373
10456
  const y = u.getUTCFullYear();
11374
10457
  const m = u.getUTCMonth();
11375
10458
  if (unit === "month") return utcDay(y, m + 1, 1);
@@ -11377,11 +10460,11 @@ function periodAfter(unit, start) {
11377
10460
  return utcDay(y + 1, 0, 1);
11378
10461
  }
11379
10462
  function format2(day, locale, options) {
11380
- return dayFormatter(locale, options).format(new Date(day * DAY_MS2));
10463
+ return dayFormatter(locale, options).format(new Date(day * DAY_MS));
11381
10464
  }
11382
10465
  var pad22 = (n) => String(n).padStart(2, "0");
11383
10466
  function keyOf(unit, day) {
11384
- const u = new Date(day * DAY_MS2);
10467
+ const u = new Date(day * DAY_MS);
11385
10468
  const y = u.getUTCFullYear();
11386
10469
  const m = u.getUTCMonth();
11387
10470
  if (unit === "year") return String(y);
@@ -11396,7 +10479,7 @@ function labelsOf(unit, role, day, locale) {
11396
10479
  else if (unit === "week") forms = [f({ day: "numeric", month: "short" }), f({ day: "numeric" })];
11397
10480
  else if (unit === "month") {
11398
10481
  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" })];
11399
- } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS2).getUTCMonth() / 3) + 1}`];
10482
+ } else if (unit === "quarter") forms = [`Q${Math.floor(new Date(day * DAY_MS).getUTCMonth() / 3) + 1}`];
11400
10483
  else forms = [f({ year: "numeric" }), f({ year: "2-digit" })];
11401
10484
  return forms;
11402
10485
  }
@@ -11468,8 +10551,8 @@ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, p
11468
10551
  function extendedEnd(scale, width) {
11469
10552
  if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale.endDay;
11470
10553
  const unit = MINOR[scale.zoom];
11471
- const last2 = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
11472
- return periodAfter(unit, periodStart(unit, last2, scale.weekStartsOn));
10554
+ const last = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
10555
+ return periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
11473
10556
  }
11474
10557
  function extendTimeScaleTo(scale, endDay) {
11475
10558
  if (!(endDay > scale.endDay)) return scale;
@@ -11928,8 +11011,8 @@ var TimeViewport = forwardRef(function TimeViewport2({
11928
11011
  if (shadeDays && drawn.pxPerDay >= SHADE_MIN) {
11929
11012
  for (let d = drawn.startDay; d < drawn.endDay; d++) {
11930
11013
  if (!shadeDays(calendarDay(d))) continue;
11931
- const last2 = shaded[shaded.length - 1];
11932
- if (last2 && Math.abs(last2.x + last2.width - drawn.x(d)) < 0.5) last2.width += drawn.pxPerDay;
11014
+ const last = shaded[shaded.length - 1];
11015
+ if (last && Math.abs(last.x + last.width - drawn.x(d)) < 0.5) last.width += drawn.pxPerDay;
11933
11016
  else shaded.push({ key: String(d), x: drawn.x(d), width: drawn.pxPerDay });
11934
11017
  }
11935
11018
  }
@@ -12299,7 +11382,7 @@ var NO_DATES = "no dates";
12299
11382
  var KNOCKOUT = 2;
12300
11383
  var EDGE = "border-[length:1px] border-transparent";
12301
11384
  var EDGE_WIDTH = 1;
12302
- var SELECTED = "border-[var(--ds-accent-ink)]";
11385
+ var SELECTED2 = "border-[var(--ds-accent-ink)]";
12303
11386
  var UNDATED_INSET = 16;
12304
11387
  var NO_GROUPS = [];
12305
11388
  var NO_STATUSES = {};
@@ -12346,7 +11429,7 @@ function NoDates({ cue, selected, className, ...props }) {
12346
11429
  className: cn(
12347
11430
  "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)]",
12348
11431
  FOCUS_RING,
12349
- selected && SELECTED,
11432
+ selected && SELECTED2,
12350
11433
  className
12351
11434
  ),
12352
11435
  style: { insetInlineStart: labelWidth + UNDATED_INSET },
@@ -12373,6 +11456,7 @@ function Gantt({
12373
11456
  describe,
12374
11457
  labelHeader = "Task",
12375
11458
  toolbar,
11459
+ shadeDays,
12376
11460
  "aria-label": ariaLabel,
12377
11461
  className
12378
11462
  }) {
@@ -12602,7 +11686,7 @@ function Gantt({
12602
11686
  ] });
12603
11687
  const shapeBox = shape.kind === "milestone" ? (
12604
11688
  // the diamond draws the target's focus: its outline, which forced colours keep, so the target draws none of its own
12605
- /* @__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 } }) })
11689
+ /* @__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 } }) })
12606
11690
  ) : (
12607
11691
  // TimeBar keeps the title in view beside the pinned column while the bar's start is scrolled away
12608
11692
  /* @__PURE__ */ jsx(
@@ -12615,7 +11699,7 @@ function Gantt({
12615
11699
  start: dates.start,
12616
11700
  end: dates.end,
12617
11701
  minWidth: MIN_BAR,
12618
- className: cn("cursor-pointer text-xs font-medium", EDGE, tone.fill, tone.on, isSelected && SELECTED),
11702
+ className: cn("cursor-pointer text-xs font-medium", EDGE, tone.fill, tone.on, isSelected && SELECTED2),
12619
11703
  style: { top: (ROW_HEIGHT2 - BAR_HEIGHT) / 2, bottom: "auto", height: BAR_HEIGHT },
12620
11704
  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 })
12621
11705
  }
@@ -12697,6 +11781,7 @@ function Gantt({
12697
11781
  hasStatuses && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "min-w-0 flex-1" }),
12698
11782
  legend
12699
11783
  ] }),
11784
+ shadeDays,
12700
11785
  tabStop: false,
12701
11786
  "aria-label": regionLabel,
12702
11787
  className: legendApart ? "h-auto flex-1" : className,
@@ -12854,14 +11939,29 @@ function Launcher({
12854
11939
  const y = offset?.y ?? DEFAULT_OFFSET2;
12855
11940
  const boxSize = launcherSize(size);
12856
11941
  const onOpenChangeRef = useLatest(onOpenChange);
12857
- const corner = isRibbon ? null : side === "right" ? "bottom-right" : "bottom-left";
11942
+ const [buttonEl, setButtonEl] = useState(null);
11943
+ const [ribbonBox, setRibbonBox] = useState(null);
11944
+ useEffect(() => {
11945
+ if (!isRibbon || !buttonEl || typeof ResizeObserver === "undefined") return;
11946
+ const ro = new ResizeObserver(([entry]) => {
11947
+ const el = entry?.target ?? buttonEl;
11948
+ const width = el.offsetWidth;
11949
+ const height = el.offsetHeight;
11950
+ setRibbonBox((b) => width === 0 && height === 0 ? null : b && b.width === width && b.height === height ? b : { width, height });
11951
+ });
11952
+ ro.observe(buttonEl);
11953
+ return () => ro.disconnect();
11954
+ }, [isRibbon, buttonEl]);
11955
+ const cornerOfSide = side === "right" ? "bottom-right" : "bottom-left";
11956
+ const above = (from, height) => `calc(${from} + env(safe-area-inset-bottom) + ${height} + var(--ds-space-3))`;
11957
+ const claims = !isRibbon ? [[cornerOfSide, above(y, boxSize)]] : !ribbonBox ? [] : bottomRibbon ? (align === "end" ? ["bottom-right"] : ["bottom-right", "bottom-left", "bottom-center"]).map((e) => [e, above("0px", `${ribbonBox.height}px`)]) : align === "end" ? [[cornerOfSide, above(y, `${ribbonBox.height}px`)]] : [[side, `calc(env(safe-area-inset-${side}) + ${ribbonBox.width}px + var(--ds-space-3))`]];
11958
+ const register = useAppShell()?.registerLauncher;
11959
+ const padded = isRibbon ? null : cornerOfSide;
11960
+ useEffect(() => mounted && register ? register(padded) : void 0, [mounted, register, padded]);
12858
11961
  const containerEl = container instanceof Element ? container : null;
12859
- useCornerInset(
12860
- mounted ? corner : null,
12861
- corner ? `calc(${y} + ${boxSize} + var(--ds-space-3))` : null,
12862
- LAUNCHER_INSET_PRIORITY,
12863
- containerEl
12864
- );
11962
+ useCornerInset(mounted ? claims[0]?.[0] ?? null : null, claims[0]?.[1] ?? null, LAUNCHER_INSET_PRIORITY, containerEl);
11963
+ useCornerInset(mounted ? claims[1]?.[0] ?? null : null, claims[1]?.[1] ?? null, LAUNCHER_INSET_PRIORITY, containerEl);
11964
+ useCornerInset(mounted ? claims[2]?.[0] ?? null : null, claims[2]?.[1] ?? null, LAUNCHER_INSET_PRIORITY, containerEl);
12865
11965
  useEffect(() => {
12866
11966
  if (container) return;
12867
11967
  bodyLaunchers++;
@@ -12922,6 +12022,7 @@ function Launcher({
12922
12022
  const button = /* @__PURE__ */ jsxs(
12923
12023
  "button",
12924
12024
  {
12025
+ ref: setButtonEl,
12925
12026
  type: "button",
12926
12027
  "aria-label": accessibleName,
12927
12028
  "aria-expanded": open,
@@ -12996,7 +12097,7 @@ function Launcher({
12996
12097
  }
12997
12098
  );
12998
12099
  const content = variant === "bubble" ? /* @__PURE__ */ jsx(
12999
- Tooltip,
12100
+ AutoTooltip,
13000
12101
  {
13001
12102
  container,
13002
12103
  content: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-ds-2", children: [
@@ -13027,19 +12128,19 @@ function trackClasses(orientation) {
13027
12128
  "border-b-[length:var(--ds-nav-h-track-border)] border-[var(--ds-border-default)]"
13028
12129
  );
13029
12130
  }
13030
- function useScrollTrack(activeTab, enabled) {
12131
+ function useScrollTrack(stopTab, enabled) {
13031
12132
  const { ref, elRef, measure, fadeProps } = useScrollEdges({ enabled });
13032
12133
  useLayoutEffect(() => {
13033
12134
  if (!enabled) return;
13034
12135
  const el = elRef.current;
13035
- const tab = el?.querySelector('[role="tab"][aria-selected="true"]');
12136
+ const tab = el?.querySelector('[role="tab"][tabindex="0"]');
13036
12137
  if (!el || !tab) return;
13037
12138
  const start = tab.offsetLeft;
13038
12139
  const end = start + tab.offsetWidth;
13039
12140
  if (start < el.scrollLeft + EDGE_FADE) el.scrollLeft = Math.max(0, start - EDGE_FADE);
13040
12141
  else if (end > el.scrollLeft + el.clientWidth - EDGE_FADE) el.scrollLeft = end - el.clientWidth + EDGE_FADE;
13041
12142
  measure();
13042
- }, [activeTab, enabled, measure, elRef]);
12143
+ }, [stopTab, enabled, measure, elRef]);
13043
12144
  useEffect(() => {
13044
12145
  if (!enabled) return;
13045
12146
  const el = elRef.current;
@@ -13120,7 +12221,7 @@ function getTabsFromDOM(el) {
13120
12221
  disabled: btn.getAttribute("aria-disabled") === "true"
13121
12222
  }));
13122
12223
  }
13123
- function handleTabKeyDown(e, currentId, getTabs, orientation, onTabChange) {
12224
+ function handleTabKeyDown(e, currentId, getTabs, orientation, select) {
13124
12225
  const prev = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
13125
12226
  const next = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
13126
12227
  let targetId;
@@ -13148,7 +12249,7 @@ function handleTabKeyDown(e, currentId, getTabs, orientation, onTabChange) {
13148
12249
  }
13149
12250
  e.preventDefault();
13150
12251
  if (targetId && targetId !== currentId) {
13151
- onTabChange(targetId);
12252
+ select?.(targetId);
13152
12253
  document.getElementById(`ds-tab-${targetId}`)?.focus();
13153
12254
  }
13154
12255
  }
@@ -13165,10 +12266,21 @@ function TabButton({
13165
12266
  truncate,
13166
12267
  onKeyDown,
13167
12268
  className,
13168
- hasPanel
12269
+ hasPanel,
12270
+ isTabStop,
12271
+ setFocusedTab
13169
12272
  }) {
13170
12273
  const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !truncate && orientation !== "horizontal" });
13171
12274
  const isActive2 = activeTab === id;
12275
+ const forget = useCallback(() => setFocusedTab((f) => f === id ? null : f), [setFocusedTab, id]);
12276
+ useEffect(() => forget, [forget]);
12277
+ const focusTracking = {
12278
+ tabIndex: isTabStop ? 0 : -1,
12279
+ onFocus: () => setFocusedTab(id),
12280
+ onBlur: (e) => {
12281
+ if (!e.currentTarget.closest('[role="tablist"]')?.contains(e.relatedTarget)) forget();
12282
+ }
12283
+ };
13172
12284
  const tabId = `ds-tab-${id}`;
13173
12285
  const panelId = hasPanel === false ? void 0 : `ds-tabpanel-${id}`;
13174
12286
  const badgeBg = orientation === "vertical" ? "bg-[var(--ds-nav-v-badge-bg)]" : "bg-[var(--ds-nav-h-badge-bg)]";
@@ -13187,7 +12299,7 @@ function TabButton({
13187
12299
  "aria-selected": isActive2,
13188
12300
  "aria-controls": panelId,
13189
12301
  "aria-disabled": disabled || void 0,
13190
- tabIndex: isActive2 ? 0 : -1,
12302
+ ...focusTracking,
13191
12303
  onClick: disabled ? void 0 : () => onTabChange(id),
13192
12304
  onKeyDown,
13193
12305
  className: cn(
@@ -13211,7 +12323,7 @@ function TabButton({
13211
12323
  ]
13212
12324
  }
13213
12325
  );
13214
- if (isTruncated) return /* @__PURE__ */ jsx(Tooltip, { content: label, children: btn2 });
12326
+ if (isTruncated) return /* @__PURE__ */ jsx(AutoTooltip, { content: label, children: btn2 });
13215
12327
  return btn2;
13216
12328
  }
13217
12329
  const btn = /* @__PURE__ */ jsx(
@@ -13223,7 +12335,7 @@ function TabButton({
13223
12335
  "aria-selected": isActive2,
13224
12336
  "aria-controls": panelId,
13225
12337
  "aria-disabled": disabled || void 0,
13226
- tabIndex: isActive2 ? 0 : -1,
12338
+ ...focusTracking,
13227
12339
  onClick: disabled ? void 0 : () => onTabChange(id),
13228
12340
  onKeyDown,
13229
12341
  className: cn(
@@ -13254,12 +12366,12 @@ function TabButton({
13254
12366
  ] })
13255
12367
  }
13256
12368
  );
13257
- if (isTruncated) return /* @__PURE__ */ jsx(Tooltip, { content: label, children: btn });
12369
+ if (isTruncated) return /* @__PURE__ */ jsx(AutoTooltip, { content: label, children: btn });
13258
12370
  return btn;
13259
12371
  }
13260
12372
  function TabsList({ children, className }) {
13261
- const { orientation, fullWidth, overflow, activeTab } = useTabsCtx();
13262
- const track = useScrollTrack(activeTab, orientation === "horizontal" && overflow === "scroll");
12373
+ const { orientation, fullWidth, overflow, stopTab } = useTabsCtx();
12374
+ const track = useScrollTrack(stopTab, orientation === "horizontal" && overflow === "scroll");
13263
12375
  if (orientation === "vertical") {
13264
12376
  return /* @__PURE__ */ jsx(
13265
12377
  "div",
@@ -13274,7 +12386,7 @@ function TabsList({ children, className }) {
13274
12386
  return /* @__PURE__ */ jsx(TabStrip, { overflow, fullWidth, track, className, children });
13275
12387
  }
13276
12388
  function TabsTrigger({ value, children, count, icon, disabled, truncate, className }) {
13277
- const { activeTab, onTabChange, orientation, fullWidth, overflow } = useTabsCtx();
12389
+ const { activeTab, onTabChange, orientation, fullWidth, overflow, activation, stopTab, setFocusedTab } = useTabsCtx();
13278
12390
  return /* @__PURE__ */ jsx(
13279
12391
  TabButton,
13280
12392
  {
@@ -13288,7 +12400,9 @@ function TabsTrigger({ value, children, count, icon, disabled, truncate, classNa
13288
12400
  activeTab,
13289
12401
  onTabChange,
13290
12402
  fullWidth,
13291
- onKeyDown: (e) => handleTabKeyDown(e, value, () => getTabsFromDOM(e.currentTarget), orientation, onTabChange),
12403
+ isTabStop: value === stopTab,
12404
+ setFocusedTab,
12405
+ onKeyDown: (e) => handleTabKeyDown(e, value, () => getTabsFromDOM(e.currentTarget), orientation, activation === "automatic" ? onTabChange : void 0),
13292
12406
  className
13293
12407
  }
13294
12408
  );
@@ -13315,15 +12429,20 @@ function TabsRoot(props) {
13315
12429
  orientation = "horizontal",
13316
12430
  fullWidth = false,
13317
12431
  overflow = "scroll",
12432
+ activation = "automatic",
13318
12433
  className
13319
12434
  } = props;
13320
12435
  const isMobile = useBreakpoint("md");
13321
12436
  const effectiveOrientation = orientation === "responsive" ? "vertical" : orientation;
13322
12437
  const arrayApi = "tabs" in props && !!props.tabs;
13323
12438
  const arrayOverflow = arrayApi && props.tabs.some((t) => t.truncate) ? "truncate" : overflow;
13324
- const track = useScrollTrack(activeTab, arrayApi && effectiveOrientation === "horizontal" && arrayOverflow === "scroll");
12439
+ const [focusedTab, setFocusedTab] = useState(null);
12440
+ const stopTab = focusedTab ?? activeTab;
12441
+ const track = useScrollTrack(stopTab, arrayApi && effectiveOrientation === "horizontal" && arrayOverflow === "scroll");
13325
12442
  if ("tabs" in props && props.tabs) {
13326
12443
  const { tabs, mobile = "scroll" } = props;
12444
+ const stopIndex = rovingTabStop(tabs, (t) => t.id === stopTab, (t) => !!t.disabled && t.id !== stopTab);
12445
+ const select = activation === "automatic" ? onTabChange : void 0;
13327
12446
  const collapseToDropdown = isMobile && (orientation === "responsive" || mobile === "dropdown");
13328
12447
  if (collapseToDropdown) {
13329
12448
  return (
@@ -13358,14 +12477,14 @@ function TabsRoot(props) {
13358
12477
  children: tab.group
13359
12478
  }
13360
12479
  ),
13361
- /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, onTabChange) })
12480
+ /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, isTabStop: i === stopIndex, setFocusedTab, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, select) })
13362
12481
  ] }, tab.id))
13363
12482
  }
13364
12483
  );
13365
12484
  }
13366
- 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)) });
12485
+ 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)) });
13367
12486
  }
13368
- return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { activeTab, onTabChange, orientation: effectiveOrientation, fullWidth, overflow }, children: /* @__PURE__ */ jsx("div", { className, children: props.children }) });
12487
+ return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { activeTab, onTabChange, orientation: effectiveOrientation, fullWidth, overflow, activation, stopTab, setFocusedTab }, children: /* @__PURE__ */ jsx("div", { className, children: props.children }) });
13369
12488
  }
13370
12489
  var Tabs = Object.assign(TabsRoot, {
13371
12490
  List: TabsList,
@@ -13916,7 +13035,7 @@ var Sidebar = forwardRef(function Sidebar2({
13916
13035
  ]
13917
13036
  }
13918
13037
  );
13919
- return collapsed ? /* @__PURE__ */ jsx(Tooltip, { content: "Expand sidebar", side: "right", delay: 100, container: portalEl ?? void 0, children: collapseButton }) : collapseButton;
13038
+ return collapsed ? /* @__PURE__ */ jsx(AutoTooltip, { content: "Expand sidebar", side: "right", delay: 100, container: portalEl ?? void 0, children: collapseButton }) : collapseButton;
13920
13039
  })(),
13921
13040
  footer && /* @__PURE__ */ jsx(
13922
13041
  "div",
@@ -13971,7 +13090,7 @@ function TruncLabel3({ children, className, side = "right", container, lines = 1
13971
13090
  const clampClass = lines <= 1 ? "truncate" : clampMap[lines] ?? "truncate";
13972
13091
  const label = /* @__PURE__ */ jsx("span", { ref, className: cn(clampClass, className), children });
13973
13092
  if (!isTruncated) return label;
13974
- return /* @__PURE__ */ jsx(Tooltip, { content: children, side, delay: 200, container, children: label });
13093
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, side, delay: 200, container, children: label });
13975
13094
  }
13976
13095
  var isActive = (item) => !!item.active;
13977
13096
  function collectInitialExpanded(items, prefix = "") {
@@ -14196,7 +13315,7 @@ function SidebarNav({
14196
13315
  }
14197
13316
  );
14198
13317
  if (collapsed && isRoot) {
14199
- return /* @__PURE__ */ jsx(Tooltip, { content: item.label, side: "right", delay: 100, container: portalContainer ?? void 0, children: link }, item.label);
13318
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: item.label, side: "right", delay: 100, container: portalContainer ?? void 0, children: link }, item.label);
14200
13319
  }
14201
13320
  return /* @__PURE__ */ jsx("div", { children: link }, item.label);
14202
13321
  }
@@ -14551,13 +13670,16 @@ var variantIcons = {
14551
13670
  warning: { name: "warning", className: "text-[var(--ds-status-warning)]" },
14552
13671
  error: { name: "error", className: "text-[var(--ds-status-error)]" }
14553
13672
  };
13673
+ var RIGHT_ROOM = "max-w-[calc(100%_-_var(--ds-toast-inset-right)_-_var(--ds-space-4))]";
13674
+ var LEFT_ROOM = "max-w-[calc(100%_-_var(--ds-toast-inset-left)_-_var(--ds-space-4))]";
13675
+ var CENTRED = "left-[max(calc(var(--ds-toast-inset-left)_+_180px),calc(var(--ds-toast-inset-left)_+_(100%_-_var(--ds-toast-inset-left)_-_var(--ds-toast-inset-right))_/_2))] -translate-x-1/2 max-w-[max(360px,calc(100%_-_var(--ds-toast-inset-left)_-_var(--ds-toast-inset-right)))]";
14554
13676
  var TOAST_POSITION_CLASSES = {
14555
- "top-right": "top-[var(--ds-toast-inset-top)] right-[var(--ds-toast-inset-right)]",
14556
- "top-left": "top-[var(--ds-toast-inset-top)] left-[var(--ds-toast-inset-left)]",
14557
- "bottom-right": "bottom-[var(--ds-toast-inset-bottom-right)] right-[var(--ds-toast-inset-right)]",
14558
- "bottom-left": "bottom-[var(--ds-toast-inset-bottom-left)] left-[var(--ds-toast-inset-left)]",
14559
- "top-center": "top-[var(--ds-toast-inset-top)] left-1/2 -translate-x-1/2",
14560
- "bottom-center": "bottom-ds-4 left-1/2 -translate-x-1/2"
13677
+ "top-right": `top-[var(--ds-toast-inset-top)] right-[var(--ds-toast-inset-right)] ${RIGHT_ROOM}`,
13678
+ "top-left": `top-[var(--ds-toast-inset-top)] left-[var(--ds-toast-inset-left)] ${LEFT_ROOM}`,
13679
+ "bottom-right": `bottom-[var(--ds-toast-inset-bottom-right)] right-[var(--ds-toast-inset-right)] ${RIGHT_ROOM}`,
13680
+ "bottom-left": `bottom-[var(--ds-toast-inset-bottom-left)] left-[var(--ds-toast-inset-left)] ${LEFT_ROOM}`,
13681
+ "top-center": `top-[var(--ds-toast-inset-top)] ${CENTRED}`,
13682
+ "bottom-center": `bottom-[var(--ds-toast-inset-bottom-center)] ${CENTRED}`
14561
13683
  };
14562
13684
  var finite = (ms) => typeof ms === "number" && Number.isFinite(ms) && ms > 0;
14563
13685
  function Toast({ variant = "default", message, visible, onDismiss, duration = 5e3, pauseOnHover = false, action, actionWindow, position = "bottom-right", mode, container, inline = false, paused = false, onActionLapse, onAction, restartKey = 0 }) {
@@ -15165,6 +14287,50 @@ function NavButton({ item, onClick }) {
15165
14287
  );
15166
14288
  }
15167
14289
 
14290
+ // ../../showcase/src/lib/scroll-padding.ts
14291
+ var registry = /* @__PURE__ */ new WeakMap();
14292
+ function apply(scroller, claims) {
14293
+ const px = Math.max(0, ...claims.sizes.values());
14294
+ if (px) {
14295
+ scroller.style.setProperty("scroll-padding-top", claims.floor ? `max(${claims.floor}, ${px}px)` : `${px}px`);
14296
+ return;
14297
+ }
14298
+ if (claims.inline) scroller.style.setProperty("scroll-padding-top", claims.inline, claims.priority);
14299
+ else scroller.style.removeProperty("scroll-padding-top");
14300
+ }
14301
+ function claimScrollPaddingTop(scroller) {
14302
+ let claims = registry.get(scroller);
14303
+ if (!claims) {
14304
+ const own = getComputedStyle(scroller).getPropertyValue("scroll-padding-top").trim();
14305
+ claims = {
14306
+ inline: scroller.style.getPropertyValue("scroll-padding-top"),
14307
+ priority: scroller.style.getPropertyPriority("scroll-padding-top"),
14308
+ floor: own && own !== "auto" && parseFloat(own) !== 0 ? own : null,
14309
+ sizes: /* @__PURE__ */ new Map(),
14310
+ holders: 0
14311
+ };
14312
+ registry.set(scroller, claims);
14313
+ }
14314
+ const held = claims;
14315
+ held.holders++;
14316
+ const token = /* @__PURE__ */ Symbol("scroll-padding-claim");
14317
+ let released = false;
14318
+ return {
14319
+ set(px) {
14320
+ if (released) return;
14321
+ held.sizes.set(token, Math.max(0, Math.ceil(px)));
14322
+ apply(scroller, held);
14323
+ },
14324
+ release() {
14325
+ if (released) return;
14326
+ released = true;
14327
+ held.sizes.delete(token);
14328
+ apply(scroller, held);
14329
+ if (--held.holders === 0) registry.delete(scroller);
14330
+ }
14331
+ };
14332
+ }
14333
+
15168
14334
  // ../../showcase/src/lib/shell-theme.ts
15169
14335
  var PAGE_HEADER_MARK = /* @__PURE__ */ Symbol.for("gridsmith.PageHeader");
15170
14336
  function resolvePageHeader(theme, props, facts) {
@@ -15462,13 +14628,13 @@ function TruncHeading({ as: Tag3 = "h1", className, children, lines: linesProp }
15462
14628
  const { ref, isTruncated } = useTextOverflow({ lines });
15463
14629
  const heading = /* @__PURE__ */ jsx(Tag3, { ref, className: cn(lines === 2 ? "line-clamp-2" : "truncate", className), children });
15464
14630
  if (!isTruncated) return heading;
15465
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: heading });
14631
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: heading });
15466
14632
  }
15467
14633
  function ClampedText({ children, className }) {
15468
14634
  const { ref, isTruncated } = useTextOverflow({ lines: 2 });
15469
14635
  const el = /* @__PURE__ */ jsx("p", { ref, className: cn("line-clamp-2", className), children });
15470
14636
  if (!isTruncated) return el;
15471
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
14637
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: el });
15472
14638
  }
15473
14639
  function findScrollParent(el) {
15474
14640
  let node = el.parentElement;
@@ -16088,20 +15254,38 @@ function SnappingHeader({
16088
15254
  } : setStickyRef;
16089
15255
  useLayoutEffect(() => {
16090
15256
  const report = container?.setPinnedHeight;
16091
- if (!report) return;
16092
15257
  const el = stickyRef.current;
16093
15258
  if (!(pinned && collapsed) || !el) {
16094
- report(null);
15259
+ report?.(null);
16095
15260
  return;
16096
15261
  }
16097
- const sync = () => report(el.offsetHeight);
15262
+ const doc = el.ownerDocument;
15263
+ const claim = claimScrollPaddingTop(findScrollParent(el) ?? doc.documentElement);
15264
+ let inBar = el.contains(doc.activeElement);
15265
+ const sync = () => {
15266
+ report?.(el.offsetHeight);
15267
+ claim.set(inBar ? 0 : (parseFloat(getComputedStyle(el).top) || 0) + el.offsetHeight + FOCUS_ROOM);
15268
+ };
15269
+ const follow2 = (to) => {
15270
+ const now = to instanceof Node && el.contains(to);
15271
+ if (now !== inBar) {
15272
+ inBar = now;
15273
+ sync();
15274
+ }
15275
+ };
15276
+ const onFocusIn = (e) => follow2(e.target);
15277
+ const onFocusOut = (e) => follow2(e.relatedTarget);
16098
15278
  sync();
16099
- if (typeof ResizeObserver === "undefined") return () => report(null);
16100
- const obs = new ResizeObserver(sync);
16101
- obs.observe(el);
15279
+ doc.addEventListener("focusin", onFocusIn, true);
15280
+ doc.addEventListener("focusout", onFocusOut, true);
15281
+ const obs = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(sync);
15282
+ obs?.observe(el);
16102
15283
  return () => {
16103
- obs.disconnect();
16104
- report(null);
15284
+ obs?.disconnect();
15285
+ doc.removeEventListener("focusin", onFocusIn, true);
15286
+ doc.removeEventListener("focusout", onFocusOut, true);
15287
+ report?.(null);
15288
+ claim.release();
16105
15289
  };
16106
15290
  }, [container?.setPinnedHeight, pinned, collapsed]);
16107
15291
  const sentinelRef = useRef(null);
@@ -16597,7 +15781,7 @@ function TruncSpan({ children, className }) {
16597
15781
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
16598
15782
  const el = /* @__PURE__ */ jsx("span", { ref, className: cn("truncate", className), children });
16599
15783
  if (!isTruncated) return el;
16600
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
15784
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: el });
16601
15785
  }
16602
15786
  function NavMenu({
16603
15787
  label,
@@ -17358,68 +16542,6 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
17358
16542
  container ?? document.body
17359
16543
  );
17360
16544
  }
17361
- function HoverCard({
17362
- content,
17363
- children,
17364
- side = "bottom",
17365
- delay = 300,
17366
- closeDelay = 200,
17367
- className,
17368
- container
17369
- }) {
17370
- const [open, setOpen] = useState(false);
17371
- const triggerRef = useRef(null);
17372
- const floatingRef = useRef(null);
17373
- const openTimeout = useRef(void 0);
17374
- const closeTimeout = useRef(void 0);
17375
- const pos = usePosition(triggerRef, floatingRef, open, { side, offset: 8, flip: true });
17376
- const show = useCallback(() => {
17377
- clearTimeout(closeTimeout.current);
17378
- openTimeout.current = setTimeout(() => setOpen(true), delay);
17379
- }, [delay]);
17380
- const hide = useCallback(() => {
17381
- clearTimeout(openTimeout.current);
17382
- closeTimeout.current = setTimeout(() => setOpen(false), closeDelay);
17383
- }, [closeDelay]);
17384
- return /* @__PURE__ */ jsxs(Fragment, { children: [
17385
- /* @__PURE__ */ jsx(
17386
- Slot,
17387
- {
17388
- ref: triggerRef,
17389
- onMouseEnter: show,
17390
- onMouseLeave: hide,
17391
- onFocus: show,
17392
- onBlur: hide,
17393
- children
17394
- }
17395
- ),
17396
- open && createPortal(
17397
- /* @__PURE__ */ jsx(
17398
- "div",
17399
- {
17400
- ref: floatingRef,
17401
- onMouseEnter: () => {
17402
- clearTimeout(closeTimeout.current);
17403
- },
17404
- onMouseLeave: hide,
17405
- className: cn(
17406
- "fixed z-[var(--ds-z-popover)]",
17407
- "w-72 p-ds-4",
17408
- "bg-[var(--ds-bg-surface-elevated)] text-[var(--ds-text-primary)]",
17409
- GLASS_DROPDOWN,
17410
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
17411
- "rounded-[var(--ds-radius-md)]",
17412
- "animate-in fade-in-0 zoom-in-95",
17413
- className
17414
- ),
17415
- style: { top: pos.top, left: pos.left },
17416
- children: /* @__PURE__ */ jsx(RowsCtx, { value: false, children: content })
17417
- }
17418
- ),
17419
- container ?? document.body
17420
- )
17421
- ] });
17422
- }
17423
16545
  function ContextMenu({ items, children, className, ref, container }) {
17424
16546
  const [open, setOpen] = useState(false);
17425
16547
  const [pos, setPos] = useState({ x: 0, y: 0 });
@@ -17558,13 +16680,13 @@ function TruncTitle({ children }) {
17558
16680
  const { ref, isTruncated } = useTextOverflow({ lines: 1 });
17559
16681
  const el = /* @__PURE__ */ jsx("span", { ref, className: "truncate", children });
17560
16682
  if (!isTruncated) return el;
17561
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
16683
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: el });
17562
16684
  }
17563
16685
  function ClampedDesc({ children, className }) {
17564
16686
  const { ref, isTruncated } = useTextOverflow({ lines: 2 });
17565
16687
  const el = /* @__PURE__ */ jsx("p", { ref, className: cn("line-clamp-2", className), children });
17566
16688
  if (!isTruncated) return el;
17567
- return /* @__PURE__ */ jsx(Tooltip, { content: children, children: el });
16689
+ return /* @__PURE__ */ jsx(AutoTooltip, { content: children, children: el });
17568
16690
  }
17569
16691
  function NotificationCenter({
17570
16692
  notifications,
@@ -17624,7 +16746,8 @@ function NotificationCenter({
17624
16746
  document.removeEventListener("mousedown", onClick);
17625
16747
  };
17626
16748
  }, [open, setOpen, isTopLayer, returnFocus]);
17627
- const grouped = groupByDate(filteredNotifications);
16749
+ const today = useCurrentDay();
16750
+ const grouped = groupByDay(filteredNotifications, today);
17628
16751
  if (variant === "page") {
17629
16752
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full", className), children: [
17630
16753
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-ds-4", children: [
@@ -17674,8 +16797,8 @@ function NotificationCenter({
17674
16797
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17675
16798
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: filter === "unread" ? "No unread notifications" : "No notifications" })
17676
16799
  ] }),
17677
- grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { className: "mb-ds-2", children: [
17678
- /* @__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 }),
16800
+ /* @__PURE__ */ jsx(RowsCtx, { value: true, children: grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { className: "mb-ds-2", children: [
16801
+ 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 }),
17679
16802
  items.map((n) => /* @__PURE__ */ jsxs(
17680
16803
  "div",
17681
16804
  {
@@ -17701,7 +16824,7 @@ function NotificationCenter({
17701
16824
  /* @__PURE__ */ jsx(TruncTitle, { children: n.title })
17702
16825
  ] }),
17703
16826
  n.description && /* @__PURE__ */ jsx(ClampedDesc, { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: n.description }),
17704
- /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: n.timestamp })
16827
+ /* @__PURE__ */ jsx(NotificationTime, { timestamp: n.timestamp })
17705
16828
  ] }),
17706
16829
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-1 shrink-0", children: [
17707
16830
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
@@ -17729,7 +16852,7 @@ function NotificationCenter({
17729
16852
  },
17730
16853
  n.id
17731
16854
  ))
17732
- ] }, label))
16855
+ ] }, label ?? "rows")) })
17733
16856
  ] });
17734
16857
  }
17735
16858
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative inline-flex", className), children: [
@@ -17788,8 +16911,8 @@ function NotificationCenter({
17788
16911
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17789
16912
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: "No notifications" })
17790
16913
  ] }),
17791
- grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { children: [
17792
- /* @__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 }),
16914
+ /* @__PURE__ */ jsx(RowsCtx, { value: true, children: grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { children: [
16915
+ 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 }),
17793
16916
  items.map((n) => /* @__PURE__ */ jsxs(
17794
16917
  "div",
17795
16918
  {
@@ -17815,7 +16938,7 @@ function NotificationCenter({
17815
16938
  /* @__PURE__ */ jsx(TruncTitle, { children: n.title })
17816
16939
  ] }),
17817
16940
  n.description && /* @__PURE__ */ jsx(ClampedDesc, { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: n.description }),
17818
- /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: n.timestamp })
16941
+ /* @__PURE__ */ jsx(NotificationTime, { timestamp: n.timestamp })
17819
16942
  ] }),
17820
16943
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-1 shrink-0", children: [
17821
16944
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
@@ -17843,7 +16966,7 @@ function NotificationCenter({
17843
16966
  },
17844
16967
  n.id
17845
16968
  ))
17846
- ] }, label))
16969
+ ] }, label ?? "rows")) })
17847
16970
  ] })
17848
16971
  ]
17849
16972
  }
@@ -17852,27 +16975,33 @@ function NotificationCenter({
17852
16975
  )
17853
16976
  ] });
17854
16977
  }
17855
- function groupByDate(notifications) {
17856
- const today = /* @__PURE__ */ new Date();
17857
- today.setHours(0, 0, 0, 0);
17858
- const yesterday = new Date(today);
17859
- yesterday.setDate(yesterday.getDate() - 1);
17860
- const groups = {};
17861
- const order = [];
16978
+ function NotificationTime({ timestamp }) {
16979
+ const at = datedMillis(timestamp);
16980
+ 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 }) });
16981
+ }
16982
+ function useCurrentDay() {
16983
+ const today = useToday();
16984
+ const [redraws, redraw] = useReducer((n) => n + 1, 0);
16985
+ const tomorrow = today && new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1).getTime();
16986
+ useEffect(() => {
16987
+ if (tomorrow === null) return;
16988
+ const timer = setTimeout(redraw, tomorrow - Date.now() + 1e3);
16989
+ return () => clearTimeout(timer);
16990
+ }, [tomorrow, redraws]);
16991
+ return today;
16992
+ }
16993
+ var DAY_GROUPS = ["Today", "Yesterday", "Earlier"];
16994
+ function groupByDay(notifications, today) {
16995
+ if (!today) return notifications.length ? [{ label: null, items: notifications }] : [];
16996
+ const todayStarts = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
16997
+ const yesterdayStarts = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1).getTime();
16998
+ const groups = new Map(DAY_GROUPS.map((label) => [label, []]));
17862
16999
  for (const n of notifications) {
17863
- let label;
17864
- if (n.timestamp.toLowerCase().includes("ago") || n.timestamp.toLowerCase().includes("just now")) {
17865
- label = "Today";
17866
- } else {
17867
- label = "Earlier";
17868
- }
17869
- if (!groups[label]) {
17870
- groups[label] = [];
17871
- order.push(label);
17872
- }
17873
- groups[label].push(n);
17000
+ const at = datedMillis(n.timestamp);
17001
+ const label = !Number.isFinite(at) || at < yesterdayStarts ? "Earlier" : at < todayStarts ? "Yesterday" : "Today";
17002
+ groups.get(label).push(n);
17874
17003
  }
17875
- return order.map((label) => ({ label, items: groups[label] }));
17004
+ return DAY_GROUPS.filter((label) => groups.get(label).length > 0).map((label) => ({ label, items: groups.get(label) }));
17876
17005
  }
17877
17006
  function Tour({ steps, open, onClose, onComplete, container }) {
17878
17007
  const [current, setCurrent] = useState(0);
@@ -18267,8 +17396,8 @@ function useFlowSteps(options) {
18267
17396
  go(target, { replace: true });
18268
17397
  }, [key, stored, valid, furthest, href, go, at]);
18269
17398
  useEffect(() => {
18270
- if (key && ready && stored?.current !== step) update(key, (v) => ({ ...v, current: step }));
18271
- }, [key, ready, stored?.current, step]);
17399
+ if (key && ready) update(key, (v) => v.current === step ? v : { ...v, current: step });
17400
+ }, [key, ready, step]);
18272
17401
  useEffect(() => {
18273
17402
  if (!key || !ready) return;
18274
17403
  const store = storeFor(key);
@@ -18672,57 +17801,7 @@ function DangerZone({ title = "Danger zone", description, children, className })
18672
17801
  )
18673
17802
  ] });
18674
17803
  }
18675
- var lineClampClasses = {
18676
- 1: "truncate",
18677
- 2: "line-clamp-2",
18678
- 3: "line-clamp-3",
18679
- 4: "line-clamp-4",
18680
- 5: "line-clamp-5",
18681
- 6: "line-clamp-6"
18682
- };
18683
- function getTruncationClass(lines) {
18684
- return lineClampClasses[lines] || `line-clamp-[${lines}]`;
18685
- }
18686
- var TextOverflow = forwardRef(function TextOverflow2({
18687
- lines = 1,
18688
- tooltip = true,
18689
- tooltipContent,
18690
- expandMode = "tooltip",
18691
- as = "div",
18692
- children,
18693
- className
18694
- }, forwardedRef) {
18695
- const { ref: overflowRef, isTruncated } = useTextOverflow({ lines });
18696
- const truncClass = getTruncationClass(lines);
18697
- const Tag3 = as;
18698
- const content = /* @__PURE__ */ jsx(
18699
- Tag3,
18700
- {
18701
- ref: (node) => {
18702
- overflowRef(node);
18703
- if (typeof forwardedRef === "function") forwardedRef(node);
18704
- else if (forwardedRef) forwardedRef.current = node;
18705
- },
18706
- className: cn(truncClass, "min-w-0", className),
18707
- children
18708
- }
18709
- );
18710
- if (!tooltip || !isTruncated) {
18711
- return content;
18712
- }
18713
- const resolvedTooltipContent = tooltipContent ?? children;
18714
- if (expandMode === "hovercard") {
18715
- return /* @__PURE__ */ jsx(
18716
- HoverCard,
18717
- {
18718
- content: /* @__PURE__ */ jsx("div", { className: "max-w-[320px] text-sm text-[var(--ds-text-primary)]", children: resolvedTooltipContent }),
18719
- children: content
18720
- }
18721
- );
18722
- }
18723
- return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
18724
- });
18725
17804
 
18726
- 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 };
17805
+ 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 };
18727
17806
  //# sourceMappingURL=index.js.map
18728
17807
  //# sourceMappingURL=index.js.map