gridsmith-ui 0.29.1 → 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 (88) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.js +4 -2
  5. package/dist/charts.js.map +1 -1
  6. package/dist/chat.js +4 -4
  7. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  8. package/dist/chunk-7QLXYRPD.js.map +1 -0
  9. package/dist/chunk-F2BNJSON.js +1348 -0
  10. package/dist/chunk-F2BNJSON.js.map +1 -0
  11. package/dist/{chunk-37WOC7F7.js → chunk-GQVBZ4GW.js} +29 -8
  12. package/dist/chunk-GQVBZ4GW.js.map +1 -0
  13. package/dist/{chunk-MFAWJSMJ.js → chunk-INGF2CPW.js} +3 -22
  14. package/dist/chunk-INGF2CPW.js.map +1 -0
  15. package/dist/{chunk-I3MTQ3CS.js → chunk-KTPGMJUK.js} +5 -5
  16. package/dist/{chunk-I3MTQ3CS.js.map → chunk-KTPGMJUK.js.map} +1 -1
  17. package/dist/{chunk-MKFWHWBK.js → chunk-L6HWACK7.js} +7 -7
  18. package/dist/chunk-L6HWACK7.js.map +1 -0
  19. package/dist/chunk-LJYSVZMI.js +25 -0
  20. package/dist/chunk-LJYSVZMI.js.map +1 -0
  21. package/dist/{chunk-FUKSLQ77.js → chunk-NI5542ZM.js} +39 -35
  22. package/dist/chunk-NI5542ZM.js.map +1 -0
  23. package/dist/chunk-NKUCS67T.js +13 -0
  24. package/dist/chunk-NKUCS67T.js.map +1 -0
  25. package/dist/{chunk-RECCCA7Z.js → chunk-NLXQ323J.js} +4 -4
  26. package/dist/{chunk-RECCCA7Z.js.map → chunk-NLXQ323J.js.map} +1 -1
  27. package/dist/{chunk-ZI3OQDET.js → chunk-OIOII5L6.js} +93 -4
  28. package/dist/chunk-OIOII5L6.js.map +1 -0
  29. package/dist/{chunk-RJEUQ7RL.js → chunk-Q36JVJX4.js} +46 -89
  30. package/dist/chunk-Q36JVJX4.js.map +1 -0
  31. package/dist/{chunk-FD4YED7O.js → chunk-RHWTMTU5.js} +3 -3
  32. package/dist/{chunk-FD4YED7O.js.map → chunk-RHWTMTU5.js.map} +1 -1
  33. package/dist/chunk-TYNDXMBV.js +105 -0
  34. package/dist/chunk-TYNDXMBV.js.map +1 -0
  35. package/dist/{chunk-4TUJXDRU.js → chunk-UNS4TGGK.js} +20 -11
  36. package/dist/chunk-UNS4TGGK.js.map +1 -0
  37. package/dist/chunk-Z4MCD73B.js +316 -0
  38. package/dist/chunk-Z4MCD73B.js.map +1 -0
  39. package/dist/data-grid.d.ts +25 -6
  40. package/dist/data-grid.js +177 -55
  41. package/dist/data-grid.js.map +1 -1
  42. package/dist/editor-mentions.js +1 -1
  43. package/dist/editor.js +3 -3
  44. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  45. package/dist/feather-XFFEFLTV.js.map +1 -0
  46. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  47. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  48. package/dist/icons.d.ts +1 -1
  49. package/dist/icons.js +1 -1
  50. package/dist/index.d.ts +138 -39
  51. package/dist/index.js +660 -1568
  52. package/dist/index.js.map +1 -1
  53. package/dist/kanban.d.ts +26 -8
  54. package/dist/kanban.js +200 -76
  55. package/dist/kanban.js.map +1 -1
  56. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  57. package/dist/lucide-JOORDTO6.js.map +1 -0
  58. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  59. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  60. package/dist/resizable.js +10 -9
  61. package/dist/resizable.js.map +1 -1
  62. package/dist/roots.css +12 -8
  63. package/dist/{table-utils-CxnpFHRF.d.ts → table-utils-BXkP-2Hx.d.ts} +3 -1
  64. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  65. package/dist/tabler-YGETLYRU.js.map +1 -0
  66. package/dist/tokens.css +14 -0
  67. package/dist/utils.js.map +1 -1
  68. package/dist/virtualized.js +8 -7
  69. package/dist/virtualized.js.map +1 -1
  70. package/dist-static/tokens.css +14 -0
  71. package/package.json +1 -1
  72. package/dist/chunk-37WOC7F7.js.map +0 -1
  73. package/dist/chunk-4TUJXDRU.js.map +0 -1
  74. package/dist/chunk-FUKSLQ77.js.map +0 -1
  75. package/dist/chunk-MFAWJSMJ.js.map +0 -1
  76. package/dist/chunk-MKFWHWBK.js.map +0 -1
  77. package/dist/chunk-OYNUU346.js.map +0 -1
  78. package/dist/chunk-QM4SJKWV.js +0 -51
  79. package/dist/chunk-QM4SJKWV.js.map +0 -1
  80. package/dist/chunk-QNHVPE2B.js +0 -117
  81. package/dist/chunk-QNHVPE2B.js.map +0 -1
  82. package/dist/chunk-RJEUQ7RL.js.map +0 -1
  83. package/dist/chunk-ZI3OQDET.js.map +0 -1
  84. package/dist/feather-FNBQ74RR.js.map +0 -1
  85. package/dist/heroicons-DODCSYWD.js.map +0 -1
  86. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  87. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  88. 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, useCornerInset, NAVBAR_INSET_PRIORITY, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, besideRoom, PANEL_INSET_PRIORITY, Drawer, useRememberedWidth, pxOf, ResizeEdge, LAUNCHER_INSET_PRIORITY, defaultMaxWidth } from './chunk-4TUJXDRU.js';
3
- export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-4TUJXDRU.js';
4
- import { Skeleton, TITLE_TEXT, useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-MFAWJSMJ.js';
5
- export { Skeleton, useFormatLocale, useHydrated } from './chunk-MFAWJSMJ.js';
2
+ import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useCornerInset, NAVBAR_INSET_PRIORITY, useOverlayMark, useDirtyGuard, useScrollbarInset, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, insetLessScrollbar, DiscardConfirm, OVERLAY_ROOT_ATTR, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, besideRoom, PANEL_INSET_PRIORITY, Drawer, useRememberedWidth, pxOf, ResizeEdge, LAUNCHER_INSET_PRIORITY, defaultMaxWidth } from './chunk-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
7
  import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-6A5RHW2W.js';
8
8
  export { Avatar } from './chunk-6A5RHW2W.js';
9
- import { Link } from './chunk-I3MTQ3CS.js';
10
- export { CodeBlock, Divider, Link } from './chunk-I3MTQ3CS.js';
11
- import { useFillHeight, FillContext, useKeyboardScroll } from './chunk-FD4YED7O.js';
12
- import { Badge } from './chunk-RECCCA7Z.js';
13
- export { Badge } from './chunk-RECCCA7Z.js';
14
- import { inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, CardValue } from './chunk-QNHVPE2B.js';
15
- import { useWholeListCheck, pagerPart, Pagination, Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, OutsideField, NumberInput, InlineFieldError, Label, AddAnotherRowCtx, Field } from './chunk-FUKSLQ77.js';
16
- export { Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, usePagination } from './chunk-FUKSLQ77.js';
17
- import { Button, ControlSizeContext, triggerType, Spinner, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-RJEUQ7RL.js';
18
- export { Button, ButtonSizeContext, ControlSizeContext, PreferencesContext, Spinner, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-RJEUQ7RL.js';
19
- import { elementType, formatLocale, localeChoice } from './chunk-OYNUU346.js';
20
- import { useInputModality } from './chunk-7RKC5C7G.js';
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-ZHUVLBCC.js';
23
- export { BreakpointWidthProvider, useBreakpoint } from './chunk-ZHUVLBCC.js';
24
27
  import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-72SDAX54.js';
25
28
  export { useFormContext } from './chunk-72SDAX54.js';
26
- import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-QM4SJKWV.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, AutoTooltip } from './chunk-37WOC7F7.js';
32
- export { Tooltip, useTextOverflow } from './chunk-37WOC7F7.js';
33
- import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
34
- import { Icon, slotIcon, warnOnce } from './chunk-MKFWHWBK.js';
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, atLeastFocusRoom, FOCUS_ROOM } from './chunk-JTU7OBYJ.js';
38
- import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, isValidElement, useId, memo, useImperativeHandle, useEffect, Children, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
39
- import { createPortal, flushSync } from 'react-dom';
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;
@@ -468,25 +347,6 @@ function Combobox({ value, onChange: onChangeProp, options, placeholder = "Searc
468
347
  return /* @__PURE__ */ jsx(AutoTooltip, { content: selectedLabel, disabled: !labelTruncated || open, children: triggerContent });
469
348
  }
470
349
 
471
- // ../../showcase/src/lib/locale-cache.ts
472
- var caches = /* @__PURE__ */ new Set();
473
- function localeCache(limit) {
474
- const map = /* @__PURE__ */ new Map();
475
- caches.add(map);
476
- return (key, make) => {
477
- const hit = map.get(key);
478
- if (hit !== void 0) {
479
- map.delete(key);
480
- map.set(key, hit);
481
- return hit;
482
- }
483
- const made = make();
484
- map.set(key, made);
485
- if (map.size > limit) map.delete(map.keys().next().value);
486
- return made;
487
- };
488
- }
489
-
490
350
  // ../../showcase/src/lib/time-zones.ts
491
351
  function canonicalTimeZone(zone) {
492
352
  try {
@@ -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;
@@ -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,494 +600,6 @@ 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"
1253
- ];
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 }) {
1313
- const weekStartsOn = useWeekStart(weekStartsOnProp);
1314
- const locale = useFormatLocale();
1315
- const { formatDayLong, formatMonthYear, weekdayNames } = useDateWords();
1316
- const hydrated = useHydrated();
1317
- const today = useToday();
1318
- const onChange = useReportedChange(onChangeProp, value);
1319
- const contextSize = useContext(ControlSizeContext);
1320
- const size = sizeProp ?? contextSize ?? "md";
1321
- const [open, setOpen] = useState(false);
1322
- const [hover, setHover] = useState(null);
1323
- const [viewDate, setViewDate] = useState(() => value ?? range2?.start ?? range2?.end ?? /* @__PURE__ */ new Date());
1324
- const triggerRef = useRef(null);
1325
- const buttonRef = useRef(null);
1326
- const panelRef = useRef(null);
1327
- const gridRef = useRef(null);
1328
- const generatedId = useId();
1329
- const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1330
- 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
- };
1380
- 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);
1406
- };
1407
- document.addEventListener("mousedown", handler);
1408
- document.addEventListener("keydown", keyHandler);
1409
- return () => {
1410
- document.removeEventListener("mousedown", handler);
1411
- document.removeEventListener("keydown", keyHandler);
1412
- };
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
603
  var DEFAULT_DATE_RANGE_PRESETS = [
1729
604
  { label: "Today", range: () => {
1730
605
  const t = startOfDay(/* @__PURE__ */ new Date());
@@ -1732,11 +607,11 @@ var DEFAULT_DATE_RANGE_PRESETS = [
1732
607
  } },
1733
608
  { label: "Last 7 days", range: () => {
1734
609
  const t = startOfDay(/* @__PURE__ */ new Date());
1735
- return { start: addDays2(t, -6), end: t };
610
+ return { start: addDays(t, -6), end: t };
1736
611
  } },
1737
612
  { label: "Last 30 days", range: () => {
1738
613
  const t = startOfDay(/* @__PURE__ */ new Date());
1739
- return { start: addDays2(t, -29), end: t };
614
+ return { start: addDays(t, -29), end: t };
1740
615
  } },
1741
616
  { label: "This month", range: () => {
1742
617
  const t = /* @__PURE__ */ new Date();
@@ -1760,6 +635,7 @@ function DateRangePicker({
1760
635
  container,
1761
636
  min,
1762
637
  max,
638
+ disabledDates,
1763
639
  presets = DEFAULT_DATE_RANGE_PRESETS,
1764
640
  weekStartsOn: weekStartsOnProp,
1765
641
  size: sizeProp,
@@ -1776,13 +652,36 @@ function DateRangePicker({
1776
652
  const [viewDate, setViewDate] = useState(() => value.start ?? /* @__PURE__ */ new Date());
1777
653
  const [hover, setHover] = useState(null);
1778
654
  const triggerRef = useRef(null);
655
+ const buttonRef = useRef(null);
1779
656
  const panelRef = useRef(null);
657
+ const isPhone = useBreakpoint("sm");
658
+ const presetEdges = useScrollEdges({ enabled: isPhone });
659
+ useRevealFocus(presetEdges.elRef, isPhone);
1780
660
  const generatedId = useId();
1781
661
  const control = useFieldControl({ ...wiring, id, error: error || void 0 }, { requiredInDescription: true });
1782
662
  useInputModality();
1783
663
  const triggerId = control.id ?? generatedId;
1784
- const labelledBy = control.labelId ? `${control.labelId} ${triggerId}` : void 0;
664
+ const locale = useFormatLocale();
665
+ const dayFirst = useMemo(() => localeDayFirst(locale), [locale]);
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`;
675
+ };
676
+ fit();
677
+ window.addEventListener("resize", fit);
678
+ return () => {
679
+ window.removeEventListener("resize", fit);
680
+ panel.style.width = "";
681
+ };
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
  }
@@ -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);
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);
3817
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",
@@ -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
  )
@@ -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(AutoTooltip, { 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" }),
@@ -8246,9 +7320,9 @@ function WeekView({ date, events, categories, selectedDate, onDayClick, onOpenEv
8246
7320
  case "End":
8247
7321
  return cellKey(days[days.length - 1], hour);
8248
7322
  case "PageUp":
8249
- return cellKey(addDays2(day, -7), hour);
7323
+ return cellKey(addDays(day, -7), hour);
8250
7324
  case "PageDown":
8251
- return cellKey(addDays2(day, 7), hour);
7325
+ return cellKey(addDays(day, 7), hour);
8252
7326
  default:
8253
7327
  return null;
8254
7328
  }
@@ -9056,9 +8130,9 @@ function Calendar({
9056
8130
  return getMonthGrid(currentDate.getFullYear(), currentDate.getMonth(), weekStartsOn)[0][0];
9057
8131
  }, [currentView, currentDate, weekStartsOn]);
9058
8132
  const drawnTo = useMemo(() => {
9059
- if (currentView === "week") return addDays2(drawnFrom, 7);
8133
+ if (currentView === "week") return addDays(drawnFrom, 7);
9060
8134
  if (currentView === "year") return new Date(drawnFrom.getFullYear() + 1, 0, 1);
9061
- return addDays2(drawnFrom, 42);
8135
+ return addDays(drawnFrom, 42);
9062
8136
  }, [currentView, drawnFrom]);
9063
8137
  const { drawnEvents, unreadableRepeats } = useMemo(() => {
9064
8138
  const out = [];
@@ -9096,7 +8170,7 @@ function Calendar({
9096
8170
  if (currentView === "month") {
9097
8171
  setCurrentDate(addMonths(currentDate, direction));
9098
8172
  } else if (currentView === "week") {
9099
- setCurrentDate(addDays2(currentDate, direction * 7));
8173
+ setCurrentDate(addDays(currentDate, direction * 7));
9100
8174
  } else {
9101
8175
  setCurrentDate(new Date(currentDate.getFullYear() + direction, currentDate.getMonth(), 1));
9102
8176
  }
@@ -9956,7 +9030,9 @@ function TreeView({
9956
9030
  defaultChecked = [],
9957
9031
  onSelect,
9958
9032
  onCheck,
9959
- className
9033
+ className,
9034
+ "aria-label": ariaLabel,
9035
+ "aria-labelledby": ariaLabelledBy
9960
9036
  }) {
9961
9037
  const [ownSelected, setOwnSelected] = useState(() => new Set(defaultSelected));
9962
9038
  const selected = useMemo(() => selectedProp ? new Set(selectedProp) : ownSelected, [selectedProp, ownSelected]);
@@ -10073,7 +9149,7 @@ function TreeView({
10073
9149
  },
10074
9150
  [flatNodes, baseId, expanded, toggleExpand, checkable, handleCheck, selectable, handleSelect]
10075
9151
  );
10076
- 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(
10077
9153
  TreeNodeRow,
10078
9154
  {
10079
9155
  node,
@@ -10215,6 +9291,32 @@ function CheckboxIndicator2({ state, onClick }) {
10215
9291
  }
10216
9292
  );
10217
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
+ );
10218
9320
  function Carousel({
10219
9321
  children,
10220
9322
  autoPlay = false,
@@ -10222,6 +9324,8 @@ function Carousel({
10222
9324
  loop = false,
10223
9325
  showIndicators = true,
10224
9326
  showArrows = true,
9327
+ "aria-label": ariaLabel,
9328
+ "aria-labelledby": ariaLabelledBy,
10225
9329
  className,
10226
9330
  ref
10227
9331
  }) {
@@ -10230,32 +9334,45 @@ function Carousel({
10230
9334
  const [dragOffset, setDragOffset] = useState(0);
10231
9335
  const trackRef = useRef(null);
10232
9336
  const dragStart = useRef(null);
10233
- const pauseRef = useRef(false);
10234
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("");
10235
9345
  const goTo = useCallback(
10236
9346
  (index) => {
10237
- if (loop) {
10238
- setCurrent((index % count + count) % count);
10239
- } else {
10240
- setCurrent(Math.max(0, Math.min(index, count - 1)));
10241
- }
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}`);
10242
9350
  },
10243
9351
  [count, loop]
10244
9352
  );
10245
9353
  const next = useCallback(() => goTo(current + 1), [goTo, current]);
10246
9354
  const prev = useCallback(() => goTo(current - 1), [goTo, current]);
10247
9355
  useEffect(() => {
10248
- if (!autoPlay || count <= 1) return;
10249
- const interval = setInterval(() => {
10250
- if (!pauseRef.current) goTo(current + 1);
10251
- }, autoPlayInterval);
9356
+ if (!rotating) return;
9357
+ const interval = setInterval(() => setCurrent((c) => (c + 1) % count), autoPlayInterval);
10252
9358
  return () => clearInterval(interval);
10253
- }, [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
+ };
10254
9372
  const handlePointerDown = useCallback((e) => {
10255
9373
  if (e.pointerType === "mouse" && e.button !== 0) return;
10256
9374
  dragStart.current = { x: e.clientX, time: Date.now() };
10257
9375
  setDragging(true);
10258
- pauseRef.current = true;
10259
9376
  e.target.setPointerCapture(e.pointerId);
10260
9377
  }, []);
10261
9378
  const handlePointerMove = useCallback(
@@ -10280,9 +9397,6 @@ function Carousel({
10280
9397
  dragStart.current = null;
10281
9398
  setDragging(false);
10282
9399
  setDragOffset(0);
10283
- setTimeout(() => {
10284
- pauseRef.current = false;
10285
- }, 1e3);
10286
9400
  },
10287
9401
  [next, prev]
10288
9402
  );
@@ -10293,31 +9407,49 @@ function Carousel({
10293
9407
  {
10294
9408
  ref,
10295
9409
  "data-gs-clip": "",
9410
+ role: "group",
9411
+ "aria-roledescription": "carousel",
9412
+ "aria-label": ariaLabel,
9413
+ "aria-labelledby": ariaLabelledBy,
10296
9414
  className: cn(
10297
9415
  "relative overflow-hidden rounded-[var(--ds-radius-md)]",
10298
9416
  "bg-[var(--ds-bg-muted)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10299
9417
  className
10300
9418
  ),
10301
- onMouseEnter: () => {
10302
- pauseRef.current = true;
10303
- },
10304
- onMouseLeave: () => {
10305
- if (!dragging) pauseRef.current = false;
10306
- },
9419
+ onFocus,
9420
+ onPointerEnter,
9421
+ onPointerLeave,
10307
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
+ ),
10308
9434
  /* @__PURE__ */ jsx(
10309
9435
  "div",
10310
9436
  {
10311
9437
  ref: trackRef,
10312
- 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"),
10313
9439
  style: { transform: `translateX(calc(-${current * 100}% + ${dragOffset}px))` },
10314
9440
  onPointerDown: handlePointerDown,
10315
9441
  onPointerMove: handlePointerMove,
10316
9442
  onPointerUp: handlePointerUp,
10317
9443
  onPointerCancel: handlePointerUp,
10318
- children: children.map((child, i) => /* @__PURE__ */ jsx("div", { className: "w-full shrink-0 select-none flex flex-col [&>*]:flex-1", "aria-roledescription": "slide", "aria-label": `Slide ${i + 1} of ${count}`, children: child }, i))
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
+ ))
10319
9450
  }
10320
9451
  ),
9452
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: said }),
10321
9453
  showArrows && count > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
10322
9454
  /* @__PURE__ */ jsx(
10323
9455
  "button",
@@ -10326,16 +9458,7 @@ function Carousel({
10326
9458
  onClick: prev,
10327
9459
  disabled: !canPrev,
10328
9460
  "aria-label": "Previous slide",
10329
- className: cn(
10330
- "absolute left-ds-4 top-1/2 -translate-y-1/2 z-10",
10331
- "flex items-center justify-center w-8 h-8",
10332
- "rounded-[var(--ds-radius-full)]",
10333
- "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10334
- "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10335
- "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10336
- FOCUS_RING,
10337
- !canPrev && "opacity-0 pointer-events-none"
10338
- ),
9461
+ className: cn("absolute left-ds-4 top-1/2 -translate-y-1/2 z-10", ROUND_BUTTON, !canPrev && "opacity-0 pointer-events-none"),
10339
9462
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16, className: "rotate-180" })
10340
9463
  }
10341
9464
  ),
@@ -10346,16 +9469,7 @@ function Carousel({
10346
9469
  onClick: next,
10347
9470
  disabled: !canNext,
10348
9471
  "aria-label": "Next slide",
10349
- className: cn(
10350
- "absolute right-ds-4 top-1/2 -translate-y-1/2 z-10",
10351
- "flex items-center justify-center w-8 h-8",
10352
- "rounded-[var(--ds-radius-full)]",
10353
- "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]",
10354
- "text-[var(--ds-text-primary)] shadow-[var(--ds-shadow-sm)]",
10355
- "transition-opacity cursor-pointer hover:bg-[var(--ds-bg-muted)]",
10356
- FOCUS_RING,
10357
- !canNext && "opacity-0 pointer-events-none"
10358
- ),
9472
+ className: cn("absolute right-ds-4 top-1/2 -translate-y-1/2 z-10", ROUND_BUTTON, !canNext && "opacity-0 pointer-events-none"),
10359
9473
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
10360
9474
  }
10361
9475
  )
@@ -10366,6 +9480,7 @@ function Carousel({
10366
9480
  type: "button",
10367
9481
  onClick: () => goTo(i),
10368
9482
  "aria-label": `Go to slide ${i + 1}`,
9483
+ "aria-current": i === current || void 0,
10369
9484
  className: cn(
10370
9485
  "w-2 h-2 rounded-[var(--ds-radius-full)] transition-all cursor-pointer",
10371
9486
  FOCUS_RING,
@@ -11221,9 +10336,9 @@ var StackedList = forwardRef(function StackedList2({
11221
10336
 
11222
10337
  // ../../showcase/src/lib/time-days.ts
11223
10338
  var TIME_ZOOMS = ["day", "week", "month", "quarter"];
11224
- var DAY_MS2 = 864e5;
10339
+ var DAY_MS = 864e5;
11225
10340
  var DATE_ONLY2 = /^(\d{4})-(\d{2})-(\d{2})$/;
11226
- 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);
11227
10342
  function dayOfDate(d) {
11228
10343
  const t = d.getTime();
11229
10344
  if (Number.isNaN(t)) return NaN;
@@ -11232,7 +10347,7 @@ function dayOfDate(d) {
11232
10347
  first.setHours(0, 0, 0, 0);
11233
10348
  if (first.getTime() === t) return day;
11234
10349
  const ms = ((d.getHours() * 60 + d.getMinutes()) * 60 + d.getSeconds()) * 1e3 + d.getMilliseconds();
11235
- return day + ms / DAY_MS2;
10350
+ return day + ms / DAY_MS;
11236
10351
  }
11237
10352
  function dayOf(value) {
11238
10353
  if (typeof value === "number") return Number.isFinite(value) ? value : NaN;
@@ -11242,13 +10357,13 @@ function dayOf(value) {
11242
10357
  if (!m) return dayOfDate(new Date(value));
11243
10358
  const [y, mo, d] = [Number(m[1]), Number(m[2]) - 1, Number(m[3])];
11244
10359
  const day = utcDay(y, mo, d);
11245
- const back = new Date(day * DAY_MS2);
10360
+ const back = new Date(day * DAY_MS);
11246
10361
  return back.getUTCFullYear() === y && back.getUTCMonth() === mo && back.getUTCDate() === d ? day : NaN;
11247
10362
  }
11248
10363
  function dateOfDay(day) {
11249
10364
  const whole2 = Math.floor(day);
11250
- const u = new Date(whole2 * DAY_MS2);
11251
- return new Date(u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate(), 0, 0, 0, Math.round((day - whole2) * DAY_MS2));
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));
11252
10367
  }
11253
10368
  function isZoned(value) {
11254
10369
  if (value instanceof Date) return !Number.isNaN(value.getTime());
@@ -11311,12 +10426,12 @@ function formatDateRange(start, end, words) {
11311
10426
  if (!Number.isFinite(from) || !Number.isFinite(stop)) return "";
11312
10427
  const timed = !isWholeDay(start) || !isWholeDay(until);
11313
10428
  const first = timed ? from : Math.floor(from);
11314
- const last2 = timed ? Math.max(from, stop) : Math.max(first, Math.ceil(stop) - 1);
11315
- const a = new Date(first * DAY_MS2);
11316
- const b = new Date(last2 * DAY_MS2);
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);
11317
10432
  const years = a.getUTCFullYear() !== b.getUTCFullYear() && day.year === void 0;
11318
10433
  const f = dayFormatter(locale, { ...day, ...years ? { year: "numeric" } : {}, ...timed ? time : {} });
11319
- return first === last2 ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
10434
+ return first === last ? f.format(a) : range(f, a, b, to ?? (english(f) ? " to " : void 0));
11320
10435
  }
11321
10436
 
11322
10437
  // ../../showcase/src/lib/time-scale.ts
@@ -11327,7 +10442,7 @@ function periodStart(unit, day, weekStartsOn) {
11327
10442
  const d = Math.floor(day);
11328
10443
  if (unit === "day") return d;
11329
10444
  if (unit === "week") return d - (weekday(d) - weekStartsOn + 7) % 7;
11330
- const u = new Date(d * DAY_MS2);
10445
+ const u = new Date(d * DAY_MS);
11331
10446
  const y = u.getUTCFullYear();
11332
10447
  const m = u.getUTCMonth();
11333
10448
  if (unit === "month") return utcDay(y, m, 1);
@@ -11337,7 +10452,7 @@ function periodStart(unit, day, weekStartsOn) {
11337
10452
  function periodAfter(unit, start) {
11338
10453
  if (unit === "day") return start + 1;
11339
10454
  if (unit === "week") return start + 7;
11340
- const u = new Date(start * DAY_MS2);
10455
+ const u = new Date(start * DAY_MS);
11341
10456
  const y = u.getUTCFullYear();
11342
10457
  const m = u.getUTCMonth();
11343
10458
  if (unit === "month") return utcDay(y, m + 1, 1);
@@ -11345,11 +10460,11 @@ function periodAfter(unit, start) {
11345
10460
  return utcDay(y + 1, 0, 1);
11346
10461
  }
11347
10462
  function format2(day, locale, options) {
11348
- return dayFormatter(locale, options).format(new Date(day * DAY_MS2));
10463
+ return dayFormatter(locale, options).format(new Date(day * DAY_MS));
11349
10464
  }
11350
10465
  var pad22 = (n) => String(n).padStart(2, "0");
11351
10466
  function keyOf(unit, day) {
11352
- const u = new Date(day * DAY_MS2);
10467
+ const u = new Date(day * DAY_MS);
11353
10468
  const y = u.getUTCFullYear();
11354
10469
  const m = u.getUTCMonth();
11355
10470
  if (unit === "year") return String(y);
@@ -11364,7 +10479,7 @@ function labelsOf(unit, role, day, locale) {
11364
10479
  else if (unit === "week") forms = [f({ day: "numeric", month: "short" }), f({ day: "numeric" })];
11365
10480
  else if (unit === "month") {
11366
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" })];
11367
- } 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}`];
11368
10483
  else forms = [f({ year: "numeric" }), f({ year: "2-digit" })];
11369
10484
  return forms;
11370
10485
  }
@@ -11436,8 +10551,8 @@ function scaleOver(from, to, zoom, todayValue, weekStartsOn, locale, pxPerDay, p
11436
10551
  function extendedEnd(scale, width) {
11437
10552
  if (!(width > scale.width) || !(scale.pxPerDay > 0)) return scale.endDay;
11438
10553
  const unit = MINOR[scale.zoom];
11439
- const last2 = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
11440
- return periodAfter(unit, periodStart(unit, last2, scale.weekStartsOn));
10554
+ const last = scale.startDay + Math.ceil(width / scale.pxPerDay) - 1;
10555
+ return periodAfter(unit, periodStart(unit, last, scale.weekStartsOn));
11441
10556
  }
11442
10557
  function extendTimeScaleTo(scale, endDay) {
11443
10558
  if (!(endDay > scale.endDay)) return scale;
@@ -11896,8 +11011,8 @@ var TimeViewport = forwardRef(function TimeViewport2({
11896
11011
  if (shadeDays && drawn.pxPerDay >= SHADE_MIN) {
11897
11012
  for (let d = drawn.startDay; d < drawn.endDay; d++) {
11898
11013
  if (!shadeDays(calendarDay(d))) continue;
11899
- const last2 = shaded[shaded.length - 1];
11900
- if (last2 && Math.abs(last2.x + last2.width - drawn.x(d)) < 0.5) last2.width += drawn.pxPerDay;
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;
11901
11016
  else shaded.push({ key: String(d), x: drawn.x(d), width: drawn.pxPerDay });
11902
11017
  }
11903
11018
  }
@@ -12267,7 +11382,7 @@ var NO_DATES = "no dates";
12267
11382
  var KNOCKOUT = 2;
12268
11383
  var EDGE = "border-[length:1px] border-transparent";
12269
11384
  var EDGE_WIDTH = 1;
12270
- var SELECTED = "border-[var(--ds-accent-ink)]";
11385
+ var SELECTED2 = "border-[var(--ds-accent-ink)]";
12271
11386
  var UNDATED_INSET = 16;
12272
11387
  var NO_GROUPS = [];
12273
11388
  var NO_STATUSES = {};
@@ -12314,7 +11429,7 @@ function NoDates({ cue, selected, className, ...props }) {
12314
11429
  className: cn(
12315
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)]",
12316
11431
  FOCUS_RING,
12317
- selected && SELECTED,
11432
+ selected && SELECTED2,
12318
11433
  className
12319
11434
  ),
12320
11435
  style: { insetInlineStart: labelWidth + UNDATED_INSET },
@@ -12341,6 +11456,7 @@ function Gantt({
12341
11456
  describe,
12342
11457
  labelHeader = "Task",
12343
11458
  toolbar,
11459
+ shadeDays,
12344
11460
  "aria-label": ariaLabel,
12345
11461
  className
12346
11462
  }) {
@@ -12570,7 +11686,7 @@ function Gantt({
12570
11686
  ] });
12571
11687
  const shapeBox = shape.kind === "milestone" ? (
12572
11688
  // the diamond draws the target's focus: its outline, which forced colours keep, so the target draws none of its own
12573
- /* @__PURE__ */ jsx("div", { ...treeitem(row), "aria-selected": isSelected, className: "group absolute grid cursor-pointer place-items-center outline-none", style: { insetInlineStart: (shape.x0 + shape.x1) / 2 - TARGET / 2, top: (ROW_HEIGHT2 - TARGET) / 2, width: TARGET, height: TARGET }, children: /* @__PURE__ */ jsx("span", { "data-gantt-shape": "milestone", "data-item": item.id, className: cn("block rotate-45 rounded-[2px]", GROUP_FOCUS_RING, EDGE, tone.fill, isSelected && SELECTED), style: { width: DIAMOND, height: DIAMOND } }) })
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 } }) })
12574
11690
  ) : (
12575
11691
  // TimeBar keeps the title in view beside the pinned column while the bar's start is scrolled away
12576
11692
  /* @__PURE__ */ jsx(
@@ -12583,7 +11699,7 @@ function Gantt({
12583
11699
  start: dates.start,
12584
11700
  end: dates.end,
12585
11701
  minWidth: MIN_BAR,
12586
- 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),
12587
11703
  style: { top: (ROW_HEIGHT2 - BAR_HEIGHT) / 2, bottom: "auto", height: BAR_HEIGHT },
12588
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 })
12589
11705
  }
@@ -12665,6 +11781,7 @@ function Gantt({
12665
11781
  hasStatuses && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "min-w-0 flex-1" }),
12666
11782
  legend
12667
11783
  ] }),
11784
+ shadeDays,
12668
11785
  tabStop: false,
12669
11786
  "aria-label": regionLabel,
12670
11787
  className: legendApart ? "h-auto flex-1" : className,
@@ -13011,19 +12128,19 @@ function trackClasses(orientation) {
13011
12128
  "border-b-[length:var(--ds-nav-h-track-border)] border-[var(--ds-border-default)]"
13012
12129
  );
13013
12130
  }
13014
- function useScrollTrack(activeTab, enabled) {
12131
+ function useScrollTrack(stopTab, enabled) {
13015
12132
  const { ref, elRef, measure, fadeProps } = useScrollEdges({ enabled });
13016
12133
  useLayoutEffect(() => {
13017
12134
  if (!enabled) return;
13018
12135
  const el = elRef.current;
13019
- const tab = el?.querySelector('[role="tab"][aria-selected="true"]');
12136
+ const tab = el?.querySelector('[role="tab"][tabindex="0"]');
13020
12137
  if (!el || !tab) return;
13021
12138
  const start = tab.offsetLeft;
13022
12139
  const end = start + tab.offsetWidth;
13023
12140
  if (start < el.scrollLeft + EDGE_FADE) el.scrollLeft = Math.max(0, start - EDGE_FADE);
13024
12141
  else if (end > el.scrollLeft + el.clientWidth - EDGE_FADE) el.scrollLeft = end - el.clientWidth + EDGE_FADE;
13025
12142
  measure();
13026
- }, [activeTab, enabled, measure, elRef]);
12143
+ }, [stopTab, enabled, measure, elRef]);
13027
12144
  useEffect(() => {
13028
12145
  if (!enabled) return;
13029
12146
  const el = elRef.current;
@@ -13104,7 +12221,7 @@ function getTabsFromDOM(el) {
13104
12221
  disabled: btn.getAttribute("aria-disabled") === "true"
13105
12222
  }));
13106
12223
  }
13107
- function handleTabKeyDown(e, currentId, getTabs, orientation, onTabChange) {
12224
+ function handleTabKeyDown(e, currentId, getTabs, orientation, select) {
13108
12225
  const prev = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
13109
12226
  const next = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
13110
12227
  let targetId;
@@ -13132,7 +12249,7 @@ function handleTabKeyDown(e, currentId, getTabs, orientation, onTabChange) {
13132
12249
  }
13133
12250
  e.preventDefault();
13134
12251
  if (targetId && targetId !== currentId) {
13135
- onTabChange(targetId);
12252
+ select?.(targetId);
13136
12253
  document.getElementById(`ds-tab-${targetId}`)?.focus();
13137
12254
  }
13138
12255
  }
@@ -13149,10 +12266,21 @@ function TabButton({
13149
12266
  truncate,
13150
12267
  onKeyDown,
13151
12268
  className,
13152
- hasPanel
12269
+ hasPanel,
12270
+ isTabStop,
12271
+ setFocusedTab
13153
12272
  }) {
13154
12273
  const { ref: truncRef, isTruncated } = useTextOverflow({ lines: 1, disabled: !truncate && orientation !== "horizontal" });
13155
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
+ };
13156
12284
  const tabId = `ds-tab-${id}`;
13157
12285
  const panelId = hasPanel === false ? void 0 : `ds-tabpanel-${id}`;
13158
12286
  const badgeBg = orientation === "vertical" ? "bg-[var(--ds-nav-v-badge-bg)]" : "bg-[var(--ds-nav-h-badge-bg)]";
@@ -13171,7 +12299,7 @@ function TabButton({
13171
12299
  "aria-selected": isActive2,
13172
12300
  "aria-controls": panelId,
13173
12301
  "aria-disabled": disabled || void 0,
13174
- tabIndex: isActive2 ? 0 : -1,
12302
+ ...focusTracking,
13175
12303
  onClick: disabled ? void 0 : () => onTabChange(id),
13176
12304
  onKeyDown,
13177
12305
  className: cn(
@@ -13207,7 +12335,7 @@ function TabButton({
13207
12335
  "aria-selected": isActive2,
13208
12336
  "aria-controls": panelId,
13209
12337
  "aria-disabled": disabled || void 0,
13210
- tabIndex: isActive2 ? 0 : -1,
12338
+ ...focusTracking,
13211
12339
  onClick: disabled ? void 0 : () => onTabChange(id),
13212
12340
  onKeyDown,
13213
12341
  className: cn(
@@ -13242,8 +12370,8 @@ function TabButton({
13242
12370
  return btn;
13243
12371
  }
13244
12372
  function TabsList({ children, className }) {
13245
- const { orientation, fullWidth, overflow, activeTab } = useTabsCtx();
13246
- const track = useScrollTrack(activeTab, orientation === "horizontal" && overflow === "scroll");
12373
+ const { orientation, fullWidth, overflow, stopTab } = useTabsCtx();
12374
+ const track = useScrollTrack(stopTab, orientation === "horizontal" && overflow === "scroll");
13247
12375
  if (orientation === "vertical") {
13248
12376
  return /* @__PURE__ */ jsx(
13249
12377
  "div",
@@ -13258,7 +12386,7 @@ function TabsList({ children, className }) {
13258
12386
  return /* @__PURE__ */ jsx(TabStrip, { overflow, fullWidth, track, className, children });
13259
12387
  }
13260
12388
  function TabsTrigger({ value, children, count, icon, disabled, truncate, className }) {
13261
- const { activeTab, onTabChange, orientation, fullWidth, overflow } = useTabsCtx();
12389
+ const { activeTab, onTabChange, orientation, fullWidth, overflow, activation, stopTab, setFocusedTab } = useTabsCtx();
13262
12390
  return /* @__PURE__ */ jsx(
13263
12391
  TabButton,
13264
12392
  {
@@ -13272,7 +12400,9 @@ function TabsTrigger({ value, children, count, icon, disabled, truncate, classNa
13272
12400
  activeTab,
13273
12401
  onTabChange,
13274
12402
  fullWidth,
13275
- 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),
13276
12406
  className
13277
12407
  }
13278
12408
  );
@@ -13299,15 +12429,20 @@ function TabsRoot(props) {
13299
12429
  orientation = "horizontal",
13300
12430
  fullWidth = false,
13301
12431
  overflow = "scroll",
12432
+ activation = "automatic",
13302
12433
  className
13303
12434
  } = props;
13304
12435
  const isMobile = useBreakpoint("md");
13305
12436
  const effectiveOrientation = orientation === "responsive" ? "vertical" : orientation;
13306
12437
  const arrayApi = "tabs" in props && !!props.tabs;
13307
12438
  const arrayOverflow = arrayApi && props.tabs.some((t) => t.truncate) ? "truncate" : overflow;
13308
- 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");
13309
12442
  if ("tabs" in props && props.tabs) {
13310
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;
13311
12446
  const collapseToDropdown = isMobile && (orientation === "responsive" || mobile === "dropdown");
13312
12447
  if (collapseToDropdown) {
13313
12448
  return (
@@ -13342,14 +12477,14 @@ function TabsRoot(props) {
13342
12477
  children: tab.group
13343
12478
  }
13344
12479
  ),
13345
- /* @__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) })
13346
12481
  ] }, tab.id))
13347
12482
  }
13348
12483
  );
13349
12484
  }
13350
- return /* @__PURE__ */ jsx(TabStrip, { overflow: arrayOverflow, fullWidth, track, className, children: tabs.map((tab) => /* @__PURE__ */ jsx(TabButton, { hasPanel: false, ...tab, orientation: effectiveOrientation, activeTab, onTabChange, fullWidth, onKeyDown: (e) => handleTabKeyDown(e, tab.id, () => tabs, effectiveOrientation, onTabChange) }, tab.id)) });
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)) });
13351
12486
  }
13352
- 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 }) });
13353
12488
  }
13354
12489
  var Tabs = Object.assign(TabsRoot, {
13355
12490
  List: TabsList,
@@ -15152,6 +14287,50 @@ function NavButton({ item, onClick }) {
15152
14287
  );
15153
14288
  }
15154
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
+
15155
14334
  // ../../showcase/src/lib/shell-theme.ts
15156
14335
  var PAGE_HEADER_MARK = /* @__PURE__ */ Symbol.for("gridsmith.PageHeader");
15157
14336
  function resolvePageHeader(theme, props, facts) {
@@ -16075,20 +15254,38 @@ function SnappingHeader({
16075
15254
  } : setStickyRef;
16076
15255
  useLayoutEffect(() => {
16077
15256
  const report = container?.setPinnedHeight;
16078
- if (!report) return;
16079
15257
  const el = stickyRef.current;
16080
15258
  if (!(pinned && collapsed) || !el) {
16081
- report(null);
15259
+ report?.(null);
16082
15260
  return;
16083
15261
  }
16084
- 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);
16085
15278
  sync();
16086
- if (typeof ResizeObserver === "undefined") return () => report(null);
16087
- const obs = new ResizeObserver(sync);
16088
- 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);
16089
15283
  return () => {
16090
- obs.disconnect();
16091
- report(null);
15284
+ obs?.disconnect();
15285
+ doc.removeEventListener("focusin", onFocusIn, true);
15286
+ doc.removeEventListener("focusout", onFocusOut, true);
15287
+ report?.(null);
15288
+ claim.release();
16092
15289
  };
16093
15290
  }, [container?.setPinnedHeight, pinned, collapsed]);
16094
15291
  const sentinelRef = useRef(null);
@@ -17345,68 +16542,6 @@ function CommandPalette({ open, onOpenChange, commands, placeholder = "Type a co
17345
16542
  container ?? document.body
17346
16543
  );
17347
16544
  }
17348
- function HoverCard({
17349
- content,
17350
- children,
17351
- side = "bottom",
17352
- delay = 300,
17353
- closeDelay = 200,
17354
- className,
17355
- container
17356
- }) {
17357
- const [open, setOpen] = useState(false);
17358
- const triggerRef = useRef(null);
17359
- const floatingRef = useRef(null);
17360
- const openTimeout = useRef(void 0);
17361
- const closeTimeout = useRef(void 0);
17362
- const pos = usePosition(triggerRef, floatingRef, open, { side, offset: 8, flip: true });
17363
- const show = useCallback(() => {
17364
- clearTimeout(closeTimeout.current);
17365
- openTimeout.current = setTimeout(() => setOpen(true), delay);
17366
- }, [delay]);
17367
- const hide = useCallback(() => {
17368
- clearTimeout(openTimeout.current);
17369
- closeTimeout.current = setTimeout(() => setOpen(false), closeDelay);
17370
- }, [closeDelay]);
17371
- return /* @__PURE__ */ jsxs(Fragment, { children: [
17372
- /* @__PURE__ */ jsx(
17373
- Slot,
17374
- {
17375
- ref: triggerRef,
17376
- onMouseEnter: show,
17377
- onMouseLeave: hide,
17378
- onFocus: show,
17379
- onBlur: hide,
17380
- children
17381
- }
17382
- ),
17383
- open && createPortal(
17384
- /* @__PURE__ */ jsx(
17385
- "div",
17386
- {
17387
- ref: floatingRef,
17388
- onMouseEnter: () => {
17389
- clearTimeout(closeTimeout.current);
17390
- },
17391
- onMouseLeave: hide,
17392
- className: cn(
17393
- "fixed z-[var(--ds-z-popover)]",
17394
- "w-72 p-ds-4",
17395
- "bg-[var(--ds-bg-surface-elevated)] text-[var(--ds-text-primary)]",
17396
- GLASS_DROPDOWN,
17397
- "border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]",
17398
- "rounded-[var(--ds-radius-md)]",
17399
- "animate-in fade-in-0 zoom-in-95",
17400
- className
17401
- ),
17402
- style: { top: pos.top, left: pos.left },
17403
- children: /* @__PURE__ */ jsx(RowsCtx, { value: false, children: content })
17404
- }
17405
- ),
17406
- container ?? document.body
17407
- )
17408
- ] });
17409
- }
17410
16545
  function ContextMenu({ items, children, className, ref, container }) {
17411
16546
  const [open, setOpen] = useState(false);
17412
16547
  const [pos, setPos] = useState({ x: 0, y: 0 });
@@ -17611,7 +16746,8 @@ function NotificationCenter({
17611
16746
  document.removeEventListener("mousedown", onClick);
17612
16747
  };
17613
16748
  }, [open, setOpen, isTopLayer, returnFocus]);
17614
- const grouped = groupByDate(filteredNotifications);
16749
+ const today = useCurrentDay();
16750
+ const grouped = groupByDay(filteredNotifications, today);
17615
16751
  if (variant === "page") {
17616
16752
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full", className), children: [
17617
16753
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-ds-4", children: [
@@ -17661,8 +16797,8 @@ function NotificationCenter({
17661
16797
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17662
16798
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: filter === "unread" ? "No unread notifications" : "No notifications" })
17663
16799
  ] }),
17664
- grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { className: "mb-ds-2", children: [
17665
- /* @__PURE__ */ jsx("p", { className: "px-ds-1 pt-ds-3 pb-ds-1 text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)]", children: label }),
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 }),
17666
16802
  items.map((n) => /* @__PURE__ */ jsxs(
17667
16803
  "div",
17668
16804
  {
@@ -17688,7 +16824,7 @@ function NotificationCenter({
17688
16824
  /* @__PURE__ */ jsx(TruncTitle, { children: n.title })
17689
16825
  ] }),
17690
16826
  n.description && /* @__PURE__ */ jsx(ClampedDesc, { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: n.description }),
17691
- /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: n.timestamp })
16827
+ /* @__PURE__ */ jsx(NotificationTime, { timestamp: n.timestamp })
17692
16828
  ] }),
17693
16829
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-1 shrink-0", children: [
17694
16830
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
@@ -17716,7 +16852,7 @@ function NotificationCenter({
17716
16852
  },
17717
16853
  n.id
17718
16854
  ))
17719
- ] }, label))
16855
+ ] }, label ?? "rows")) })
17720
16856
  ] });
17721
16857
  }
17722
16858
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative inline-flex", className), children: [
@@ -17775,8 +16911,8 @@ function NotificationCenter({
17775
16911
  /* @__PURE__ */ jsx(Icon, { name: "bell", size: 32, className: "opacity-30 mb-ds-2" }),
17776
16912
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: "No notifications" })
17777
16913
  ] }),
17778
- grouped.map(({ label, items }) => /* @__PURE__ */ jsxs("div", { children: [
17779
- /* @__PURE__ */ jsx("p", { className: "px-ds-4 pt-ds-3 pb-ds-1 text-2xs font-medium uppercase tracking-wider text-[var(--ds-text-muted)]", children: label }),
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 }),
17780
16916
  items.map((n) => /* @__PURE__ */ jsxs(
17781
16917
  "div",
17782
16918
  {
@@ -17802,7 +16938,7 @@ function NotificationCenter({
17802
16938
  /* @__PURE__ */ jsx(TruncTitle, { children: n.title })
17803
16939
  ] }),
17804
16940
  n.description && /* @__PURE__ */ jsx(ClampedDesc, { className: "text-xs text-[var(--ds-text-muted)] mt-ds-0.5", children: n.description }),
17805
- /* @__PURE__ */ jsx("p", { className: "text-2xs text-[var(--ds-text-muted)] mt-ds-1 tabular-nums", children: n.timestamp })
16941
+ /* @__PURE__ */ jsx(NotificationTime, { timestamp: n.timestamp })
17806
16942
  ] }),
17807
16943
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-ds-1 shrink-0", children: [
17808
16944
  !n.read && onMarkRead && /* @__PURE__ */ jsx(
@@ -17830,7 +16966,7 @@ function NotificationCenter({
17830
16966
  },
17831
16967
  n.id
17832
16968
  ))
17833
- ] }, label))
16969
+ ] }, label ?? "rows")) })
17834
16970
  ] })
17835
16971
  ]
17836
16972
  }
@@ -17839,27 +16975,33 @@ function NotificationCenter({
17839
16975
  )
17840
16976
  ] });
17841
16977
  }
17842
- function groupByDate(notifications) {
17843
- const today = /* @__PURE__ */ new Date();
17844
- today.setHours(0, 0, 0, 0);
17845
- const yesterday = new Date(today);
17846
- yesterday.setDate(yesterday.getDate() - 1);
17847
- const groups = {};
17848
- const order = [];
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, []]));
17849
16999
  for (const n of notifications) {
17850
- let label;
17851
- if (n.timestamp.toLowerCase().includes("ago") || n.timestamp.toLowerCase().includes("just now")) {
17852
- label = "Today";
17853
- } else {
17854
- label = "Earlier";
17855
- }
17856
- if (!groups[label]) {
17857
- groups[label] = [];
17858
- order.push(label);
17859
- }
17860
- groups[label].push(n);
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);
17861
17003
  }
17862
- 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) }));
17863
17005
  }
17864
17006
  function Tour({ steps, open, onClose, onComplete, container }) {
17865
17007
  const [current, setCurrent] = useState(0);
@@ -18254,8 +17396,8 @@ function useFlowSteps(options) {
18254
17396
  go(target, { replace: true });
18255
17397
  }, [key, stored, valid, furthest, href, go, at]);
18256
17398
  useEffect(() => {
18257
- if (key && ready && stored?.current !== step) update(key, (v) => ({ ...v, current: step }));
18258
- }, [key, ready, stored?.current, step]);
17399
+ if (key && ready) update(key, (v) => v.current === step ? v : { ...v, current: step });
17400
+ }, [key, ready, step]);
18259
17401
  useEffect(() => {
18260
17402
  if (!key || !ready) return;
18261
17403
  const store = storeFor(key);
@@ -18659,57 +17801,7 @@ function DangerZone({ title = "Danger zone", description, children, className })
18659
17801
  )
18660
17802
  ] });
18661
17803
  }
18662
- var lineClampClasses = {
18663
- 1: "truncate",
18664
- 2: "line-clamp-2",
18665
- 3: "line-clamp-3",
18666
- 4: "line-clamp-4",
18667
- 5: "line-clamp-5",
18668
- 6: "line-clamp-6"
18669
- };
18670
- function getTruncationClass(lines) {
18671
- return lineClampClasses[lines] || `line-clamp-[${lines}]`;
18672
- }
18673
- var TextOverflow = forwardRef(function TextOverflow2({
18674
- lines = 1,
18675
- tooltip = true,
18676
- tooltipContent,
18677
- expandMode = "tooltip",
18678
- as = "div",
18679
- children,
18680
- className
18681
- }, forwardedRef) {
18682
- const { ref: overflowRef, isTruncated } = useTextOverflow({ lines });
18683
- const truncClass = getTruncationClass(lines);
18684
- const Tag3 = as;
18685
- const content = /* @__PURE__ */ jsx(
18686
- Tag3,
18687
- {
18688
- ref: (node) => {
18689
- overflowRef(node);
18690
- if (typeof forwardedRef === "function") forwardedRef(node);
18691
- else if (forwardedRef) forwardedRef.current = node;
18692
- },
18693
- className: cn(truncClass, "min-w-0", className),
18694
- children
18695
- }
18696
- );
18697
- if (!tooltip || !isTruncated) {
18698
- return content;
18699
- }
18700
- const resolvedTooltipContent = tooltipContent ?? children;
18701
- if (expandMode === "hovercard") {
18702
- return /* @__PURE__ */ jsx(
18703
- HoverCard,
18704
- {
18705
- content: /* @__PURE__ */ jsx("div", { className: "max-w-[320px] text-sm text-[var(--ds-text-primary)]", children: resolvedTooltipContent }),
18706
- children: content
18707
- }
18708
- );
18709
- }
18710
- return /* @__PURE__ */ jsx(AutoTooltip, { content: resolvedTooltipContent, children: content });
18711
- });
18712
17804
 
18713
- export { Accordion, ActionPanel, ActionPanelGroup, AddAnother, Alert, AsidePanel, AvatarGroup, BottomNav, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DangerZone, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, DropdownMenu, EmptyState, EntityCell, FileUpload, FlowSaved, FormWrapper, Gantt, HoverCard, Kbd, Launcher, LocaleProvider, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, PageActions, PageContainer, PageHeader, PinInput, Progress, RadioGroup, Rating, RecurrencePicker, RecurrenceScopeDialog, RelativeTime, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sheet, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, TIME_ZOOMS, Table, TableToolbar, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TagInput, TextOverflow, TimeBar, TimePicker, TimeRow, TimeScaleHeader, TimeViewport, Timeline, TimezoneSelect, Toast, ToastProvider, Tour, TransferList, TreeView, browserTimeZone, canonicalTimeZone, countEdited, dateOfDay, dayOf, deleteOccurrence, describeRecurrence, endDayOf, fitLabel, fitLabels, formatDateRange, nthOf, occurrenceOn, occurrences, parseRRule, readFlowProgress, readRecurrence, recurrencePresets, sameRecurrence, saveOccurrence, timeExtent, timeZoneOffset, toRRule, useBrowserTimeZone, useContentWidth, useDestructiveAction, useFittedLabels, useFlowSteps, useRelativeTimeFormat, useSidebar, useTextMeasure, useTimeScale, useTimeViewport, useTimeViewportLayout, useToast, useToastOptional };
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 };
18714
17806
  //# sourceMappingURL=index.js.map
18715
17807
  //# sourceMappingURL=index.js.map