gridsmith-ui 0.31.1 → 0.32.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 (63) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/dist/charts.d.ts +101 -30
  3. package/dist/charts.js +180 -105
  4. package/dist/charts.js.map +1 -1
  5. package/dist/chat.js +6 -6
  6. package/dist/{chunk-EZ3ZGFGA.js → chunk-2724SJO2.js} +12 -4
  7. package/dist/chunk-2724SJO2.js.map +1 -0
  8. package/dist/{chunk-IZW5XHKA.js → chunk-DO6DLTFK.js} +10 -8
  9. package/dist/chunk-DO6DLTFK.js.map +1 -0
  10. package/dist/{chunk-KSJTMSYA.js → chunk-H732QL56.js} +4 -4
  11. package/dist/{chunk-KSJTMSYA.js.map → chunk-H732QL56.js.map} +1 -1
  12. package/dist/{chunk-DR6WEI3A.js → chunk-HCEHTJ5E.js} +3 -3
  13. package/dist/{chunk-DR6WEI3A.js.map → chunk-HCEHTJ5E.js.map} +1 -1
  14. package/dist/{chunk-2TCCSKL3.js → chunk-HUVZQOU4.js} +5 -5
  15. package/dist/{chunk-2TCCSKL3.js.map → chunk-HUVZQOU4.js.map} +1 -1
  16. package/dist/{chunk-TH272DIC.js → chunk-JHZ22HQP.js} +75 -24
  17. package/dist/chunk-JHZ22HQP.js.map +1 -0
  18. package/dist/{chunk-I6JJ7KKP.js → chunk-NGZJK57C.js} +14 -10
  19. package/dist/chunk-NGZJK57C.js.map +1 -0
  20. package/dist/{chunk-BHTAVJIG.js → chunk-OVHM55JF.js} +5 -5
  21. package/dist/{chunk-BHTAVJIG.js.map → chunk-OVHM55JF.js.map} +1 -1
  22. package/dist/{chunk-NDL2JGQ7.js → chunk-QY2RA2HO.js} +5 -5
  23. package/dist/{chunk-NDL2JGQ7.js.map → chunk-QY2RA2HO.js.map} +1 -1
  24. package/dist/{chunk-W3GY5ROH.js → chunk-R2IAK3J3.js} +3 -3
  25. package/dist/{chunk-W3GY5ROH.js.map → chunk-R2IAK3J3.js.map} +1 -1
  26. package/dist/{chunk-V73LHNRL.js → chunk-RS624TD5.js} +29 -10
  27. package/dist/chunk-RS624TD5.js.map +1 -0
  28. package/dist/{chunk-IOHGE4EW.js → chunk-SIEU4H3H.js} +8 -8
  29. package/dist/chunk-SIEU4H3H.js.map +1 -0
  30. package/dist/{chunk-DBHVJ7Z3.js → chunk-SS3C24IJ.js} +3 -2
  31. package/dist/chunk-SS3C24IJ.js.map +1 -0
  32. package/dist/{chunk-K3NAFQ4S.js → chunk-TMPPBXX3.js} +3 -3
  33. package/dist/{chunk-K3NAFQ4S.js.map → chunk-TMPPBXX3.js.map} +1 -1
  34. package/dist/{chunk-SLGGZ3MU.js → chunk-WSKPESAX.js} +12 -10
  35. package/dist/chunk-WSKPESAX.js.map +1 -0
  36. package/dist/{chunk-HQFTP7Y5.js → chunk-XBVZIN26.js} +4 -4
  37. package/dist/{chunk-HQFTP7Y5.js.map → chunk-XBVZIN26.js.map} +1 -1
  38. package/dist/data-grid.js +9 -9
  39. package/dist/data-grid.js.map +1 -1
  40. package/dist/editor-mentions.js +2 -2
  41. package/dist/editor.js +3 -2
  42. package/dist/editor.js.map +1 -1
  43. package/dist/index.d.ts +44 -6
  44. package/dist/index.js +436 -213
  45. package/dist/index.js.map +1 -1
  46. package/dist/kanban.js +9 -9
  47. package/dist/resizable.d.ts +11 -2
  48. package/dist/resizable.js +96 -22
  49. package/dist/resizable.js.map +1 -1
  50. package/dist/roots.css +2 -2
  51. package/dist/tokens.css +6 -6
  52. package/dist/virtualized.js +9 -8
  53. package/dist/virtualized.js.map +1 -1
  54. package/dist-static/tokens.css +6 -6
  55. package/package.json +1 -1
  56. package/dist/chunk-DBHVJ7Z3.js.map +0 -1
  57. package/dist/chunk-EZ3ZGFGA.js.map +0 -1
  58. package/dist/chunk-I6JJ7KKP.js.map +0 -1
  59. package/dist/chunk-IOHGE4EW.js.map +0 -1
  60. package/dist/chunk-IZW5XHKA.js.map +0 -1
  61. package/dist/chunk-SLGGZ3MU.js.map +0 -1
  62. package/dist/chunk-TH272DIC.js.map +0 -1
  63. package/dist/chunk-V73LHNRL.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,71 +1,216 @@
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-TH272DIC.js';
3
- export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-TH272DIC.js';
4
- import { Skeleton, TITLE_TEXT, SegmentedControl } from './chunk-BHTAVJIG.js';
5
- export { SegmentedControl, Skeleton } from './chunk-BHTAVJIG.js';
2
+ import { useAppShell, REACT_VERSION_PROBLEM, Breadcrumbs, useCrumbFollow, crumbGoes, useCornerInset, NAVBAR_INSET_PRIORITY, useOverlayMark, useDirtyGuard, useScrollbarInset, useBulkBarHost, DrawerFooterCtx, OverlayMarkScope, OverlayCloseCtx, BulkBarHostCtx, BulkBarSpacer, bulkBarRoom, insetLessScrollbar, BULK_BAR_LAYER, DiscardConfirm, OVERLAY_ROOT_ATTR, BulkBarPresenceCtx, BULK_BAR_WINDOW_LAYER, OverlayMarkCtx, useCompact, useElementWidth, DrawerPanel, useCloseSettle, besideRoom, PANEL_INSET_PRIORITY, Drawer, useRememberedWidth, pxOf, ResizeEdge, LAUNCHER_INSET_PRIORITY, TOAST_INSET_VARS, defaultMaxWidth } from './chunk-JHZ22HQP.js';
3
+ export { AppShell, Breadcrumbs, Drawer, SkipLink, useAppShell, useUnsavedChanges } from './chunk-JHZ22HQP.js';
4
+ import { Skeleton, TITLE_TEXT, SegmentedControl } from './chunk-OVHM55JF.js';
5
+ export { SegmentedControl, Skeleton } from './chunk-OVHM55JF.js';
6
6
  import { useScrollEdges, EDGE_FADE } from './chunk-3BRQRKDO.js';
7
- import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-K3NAFQ4S.js';
8
- export { Avatar } from './chunk-K3NAFQ4S.js';
9
- import { Link } from './chunk-NDL2JGQ7.js';
10
- export { CodeBlock, Divider, Link } from './chunk-NDL2JGQ7.js';
11
- import { DropdownMenu } from './chunk-2TCCSKL3.js';
12
- export { DropdownMenu } from './chunk-2TCCSKL3.js';
13
- import { useFillHeight, FillContext } from './chunk-DBHVJ7Z3.js';
14
- import { Badge } from './chunk-SLGGZ3MU.js';
15
- export { Badge } from './chunk-SLGGZ3MU.js';
16
- import { useKeyboardScroll } from './chunk-KSJTMSYA.js';
17
- import { localeCache, startOfDay, addDays, useWeekStart, useDateWords, localeDayFirst, dayKey, moveDay, addMonths, DateSegmentFields, getMonthGrid, isSameMonth, isSameDay, DatePicker, timeSegments, pad2, inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, Checkbox, CardValue, SortIndicator, startOfWeek, endOfWeek, addMonthsClamped, isExcludedDay, CutCell, CutEmail, stepValue, separatorMovesOn, TIME_SEPARATORS, typeDigit, getEventsForDay, layoutWeek, isWeekend, getWeekDays, isBarEvent, layoutPieces, dayPieces, groupEventsByDay, timeToFraction } from './chunk-I6JJ7KKP.js';
18
- export { Checkbox, CheckboxGroup, CheckboxGroupItem, DatePicker, HoverCard, TextOverflow } from './chunk-I6JJ7KKP.js';
19
- import { useWholeListCheck, pagerPart, Pagination, NumberInput } from './chunk-IOHGE4EW.js';
20
- export { NumberInput, Pagination, Popover, usePagination } from './chunk-IOHGE4EW.js';
7
+ import { Avatar, foldForSearch, useActiveOptionInView, shownMentions, matchMentions, mentionOptionId, MentionList, initialsOf } from './chunk-TMPPBXX3.js';
8
+ export { Avatar } from './chunk-TMPPBXX3.js';
9
+ import { Link } from './chunk-QY2RA2HO.js';
10
+ export { CodeBlock, Divider, Link } from './chunk-QY2RA2HO.js';
11
+ import { DropdownMenu } from './chunk-HUVZQOU4.js';
12
+ export { DropdownMenu } from './chunk-HUVZQOU4.js';
13
+ import { useFillHeight, FillContext } from './chunk-SS3C24IJ.js';
14
+ import { Badge } from './chunk-WSKPESAX.js';
15
+ export { Badge } from './chunk-WSKPESAX.js';
16
+ import { useKeyboardScroll } from './chunk-H732QL56.js';
17
+ import { localeCache, startOfDay, addDays, useWeekStart, useDateWords, localeDayFirst, dayKey, moveDay, addMonths, DateSegmentFields, getMonthGrid, isSameMonth, isSameDay, DatePicker, timeSegments, pad2, inferColumnKinds, useCardsSwitch, CARDS_BELOW, kindOfRenderedText, Checkbox, CardValue, SortIndicator, startOfWeek, endOfWeek, addMonthsClamped, isExcludedDay, CutCell, CutEmail, stepValue, separatorMovesOn, TIME_SEPARATORS, typeDigit, getEventsForDay, layoutWeek, isWeekend, getWeekDays, isBarEvent, layoutPieces, dayPieces, groupEventsByDay, timeToFraction } from './chunk-NGZJK57C.js';
18
+ export { Checkbox, CheckboxGroup, CheckboxGroupItem, DatePicker, HoverCard, TextOverflow } from './chunk-NGZJK57C.js';
19
+ import { useWholeListCheck, pagerPart, Pagination, NumberInput } from './chunk-SIEU4H3H.js';
20
+ export { NumberInput, Pagination, Popover, usePagination } from './chunk-SIEU4H3H.js';
21
21
  import { useShellPreferences, PREFERENCE_HOUSE_DEFAULTS, formatRelativeTime, thresholdOf, toMillis, formatFullTime, datedMillis, preferenceActionKey, DAY, HOUR, MINUTE } from './chunk-QOMP4D6V.js';
22
22
  export { PreferencesContext, formatFullTime, formatRelativeTime, preferenceActionKey, usePreferences } from './chunk-QOMP4D6V.js';
23
23
  import { useFormatLocale, useHydrated, useToday, LocaleContext } from './chunk-LJYSVZMI.js';
24
24
  export { useFormatLocale, useHydrated } from './chunk-LJYSVZMI.js';
25
- import { Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, OutsideField, InlineFieldError, Label, AddAnotherRowCtx, Field } from './chunk-IZW5XHKA.js';
26
- export { Dropdown, Field, InlineFieldError, Input, Label, Textarea, Toggle } from './chunk-IZW5XHKA.js';
25
+ import { Input, CHECK_FILL, Dropdown, FilterRowContext, SWITCH_KNOB_ON, SWITCH_KNOB_OFF, SWITCH_KNOB_MOTION, SWITCH_TRACK_ON, SWITCH_TRACK_OFF, useFieldControl, useField, OutsideField, InlineFieldError, Label, AddAnotherRowCtx, Field } from './chunk-DO6DLTFK.js';
26
+ export { Dropdown, Field, InlineFieldError, Input, Label, Textarea, Toggle } from './chunk-DO6DLTFK.js';
27
27
  import { formatLocale, localeChoice } from './chunk-7QLXYRPD.js';
28
28
  import { FormChangeCtx, useReportedChange, useOptionalFormContext, FormCtx } from './chunk-72SDAX54.js';
29
29
  export { useFormContext } from './chunk-72SDAX54.js';
30
- import { CardSurfaceCtx, useScrollsInside, SplitPaneCtx, RowsCtx, PageActionsCtx, SurfaceBoundary, useFramesItself, TableCardCtx } from './chunk-V73LHNRL.js';
31
- import { Button, elementType, ControlSizeContext, triggerType, Spinner } from './chunk-HQFTP7Y5.js';
32
- export { Button, ButtonSizeContext, ControlSizeContext, Spinner } from './chunk-HQFTP7Y5.js';
30
+ import { CardSurfaceCtx, useScrollsInside, useRunsEdgeToEdge, SplitPaneCtx, PanePaddedCtx, RowsCtx, PageActionsCtx, PANE_HEADING_CANCEL, SurfaceBoundary, useFramesItself, TableCardCtx, useWantsInset } from './chunk-RS624TD5.js';
31
+ import { Button, elementType, ControlSizeContext, triggerType, Spinner } from './chunk-XBVZIN26.js';
32
+ export { Button, ButtonSizeContext, ControlSizeContext, Spinner } from './chunk-XBVZIN26.js';
33
33
  import { useBreakpoint, readBreakpointPx } from './chunk-ZHUVLBCC.js';
34
34
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-ZHUVLBCC.js';
35
- import { useInputModality, inputModality } from './chunk-7RKC5C7G.js';
35
+ import { inputModality, useInputModality } from './chunk-7RKC5C7G.js';
36
36
  export { INPUT_MODALITY_ATTR, inputModality, installInputModality, useInputModality } from './chunk-7RKC5C7G.js';
37
- import { useTextOverflow } from './chunk-W3GY5ROH.js';
38
- export { useTextOverflow } from './chunk-W3GY5ROH.js';
39
- import { AutoTooltip, mergeRefs, useRevealFocus } from './chunk-DR6WEI3A.js';
40
- export { Tooltip, useRevealFocus } from './chunk-DR6WEI3A.js';
37
+ import { useTextOverflow } from './chunk-R2IAK3J3.js';
38
+ export { useTextOverflow } from './chunk-R2IAK3J3.js';
39
+ import { AutoTooltip, mergeRefs, useRevealFocus } from './chunk-HCEHTJ5E.js';
40
+ export { Tooltip, useRevealFocus } from './chunk-HCEHTJ5E.js';
41
41
  import { Icon, slotIcon, warnOnce } from './chunk-53F7GVBQ.js';
42
- import { cn, FOCUS_RING, focusAfterSelfRemoval, FOCUSABLE, GLASS_SURFACE, useFocusKeptInList, FOCUS_INSET, useLatest, PEER_FOCUS_RING, GLASS_BACKDROP, OPEN_TO_FOCUS, focusOutOf, FIELD, ZOOM_KEYS, rovingTabStop, FOCUS_ROOM, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberOpener, focusInitial, lockBodyScroll, unlockBodyScroll, OVERLAY_CLOSE_ATTR, OVERLAY_SURFACE, GLASS_OVERLAY, usePopupFocus, usePosition, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, CONTROL_ANCESTOR, GROUP_FOCUS_RING, useGridKeyboard, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, focusAfterRemoval, GLASS_DROPDOWN, ERROR_FOCUS_RING, FIELD_FOCUS_RING, openToFocus, atLeastFocusRoom, useNonModalDialog, useReturnFocus } from './chunk-EZ3ZGFGA.js';
43
- export { useGridKeyboard } from './chunk-EZ3ZGFGA.js';
44
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
42
+ import { cn, FOCUS_RING, focusAfterSelfRemoval, FOCUSABLE, GLASS_SURFACE, useFocusKeptInList, FOCUS_INSET, useLatest, PEER_FOCUS_RING, GLASS_BACKDROP, OPEN_TO_FOCUS, focusOutOf, FIELD, ZOOM_KEYS, rovingTabStop, FOCUS_ROOM, useOverlayLayer, useFocusTrap, useFocusBeforeOpen, rememberOpener, focusInitial, lockBodyScroll, unlockBodyScroll, OVERLAY_CLOSE_ATTR, OVERLAY_SURFACE, GLASS_OVERLAY, usePopupFocus, pointOf, pageAddress, registerOpenerRoot, rememberFocus, usePosition, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, CONTROL_ANCESTOR, GROUP_FOCUS_RING, useGridKeyboard, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, focusAfterRemoval, GLASS_DROPDOWN, ERROR_FOCUS_RING, FIELD_FOCUS_RING, openToFocus, atLeastFocusRoom, useNonModalDialog, useReturnFocus, onMovingOn } from './chunk-2724SJO2.js';
43
+ export { useGridKeyboard } from './chunk-2724SJO2.js';
45
44
  import { forwardRef, useRef, useId, useEffect, useState, createContext, useCallback, useLayoutEffect, useMemo, useContext, isValidElement, memo, useImperativeHandle, useSyncExternalStore, Children, Fragment as Fragment$1, cloneElement, useReducer } from 'react';
46
45
  import { createPortal, flushSync } from 'react-dom';
46
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
47
47
 
48
- function selectAllFor(bar) {
49
- for (let box = bar?.parentElement; bar && box && box !== document.body; box = box.parentElement) {
50
- const all = Array.from(box.querySelectorAll("[data-ds-select-all] input"));
51
- if (all.length) return all.find((el) => bar.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) ?? all[all.length - 1];
48
+ var selectionControlIn = (table) => table.querySelector("[data-ds-select-all] input") ?? table.querySelector("[data-ds-select-card] input");
49
+ function holdsSelection(table) {
50
+ const all = table.querySelector("[data-ds-select-all]");
51
+ return all ? all.getAttribute("data-selection") !== "none" : !!table.querySelector("[data-ds-select-card] input:checked");
52
+ }
53
+ var tablesIn = (box) => Array.from(box.querySelectorAll('[data-gs="table"]')).filter((t) => selectionControlIn(t));
54
+ function selectionAt(point) {
55
+ const from = point.startContainer;
56
+ for (let box = from instanceof HTMLElement ? from : from.parentElement; box && box !== document.body; box = box.parentElement) {
57
+ const tables = tablesIn(box);
58
+ if (!tables.length) continue;
59
+ const pick = (list2) => list2.filter((t) => point.comparePoint(t, 0) < 0).at(-1) ?? list2.find((t) => point.comparePoint(t, 0) > 0);
60
+ const table = pick(tables.filter(holdsSelection)) ?? pick(tables);
61
+ const control = table && selectionControlIn(table);
62
+ return control ? { box, control } : null;
52
63
  }
53
64
  return null;
54
65
  }
66
+ function selectionIn(box, point) {
67
+ const tables = tablesIn(box);
68
+ const at = (t) => point ? point.comparePoint(t, 0) : 0;
69
+ const table = tables.filter((t) => at(t) < 0).at(-1) ?? tables.find((t) => at(t) > 0) ?? tables[0];
70
+ return table ? selectionControlIn(table) : null;
71
+ }
72
+ var CONTROL = "[data-bulk-bar-control]";
55
73
  function BulkActionBar({ selectedCount, actions, onClearSelection, className }) {
56
- const isMobile = useBreakpoint("sm");
57
- if (selectedCount === 0) return null;
74
+ const isPhone = useBreakpoint("sm");
75
+ const host = useContext(BulkBarHostCtx);
76
+ const presence = useContext(BulkBarPresenceCtx);
77
+ const [anchor, setAnchor] = useState(null);
78
+ const [bearing, setBearing] = useState(null);
79
+ const [bar, setBar] = useState(null);
80
+ const [stop, setStop] = useState(0);
81
+ const labels = actions.map((action) => action.label).join("\0");
82
+ const [fit, setFit] = useState(null);
83
+ const tooWide = !!fit && fit.labels === labels && fit.room > 0 && fit.full > fit.room;
84
+ const compact = isPhone || tooWide;
85
+ const shown = selectedCount > 0;
86
+ const target = host ? host.layer : typeof document === "undefined" ? null : document.body;
87
+ const selection = useRef({ control: null, box: null, page: "" });
88
+ const point = useRef(null);
89
+ const movedOn = useRef(true);
90
+ const quitWatch = useRef(null);
91
+ const watching = useRef(null);
92
+ const watch = (control) => {
93
+ quitWatch.current?.();
94
+ movedOn.current = false;
95
+ watching.current = control;
96
+ quitWatch.current = onMovingOn(control, () => {
97
+ movedOn.current = true;
98
+ });
99
+ };
100
+ useLayoutEffect(() => {
101
+ if (shown && !bearing && anchor?.isConnected) setBearing(pointOf(anchor));
102
+ else if (!shown && bearing) setBearing(null);
103
+ }, [shown, bearing, anchor]);
104
+ useLayoutEffect(() => {
105
+ if (!shown || !bearing) return;
106
+ point.current = bearing;
107
+ const found = selectionAt(bearing);
108
+ selection.current = { control: found?.control ?? null, box: found?.box ?? null, page: pageAddress() };
109
+ });
110
+ useEffect(() => () => quitWatch.current?.(), []);
111
+ const onToolbar = useCallback((el) => {
112
+ setBar(el);
113
+ const unregister = registerOpenerRoot(el, () => selection.current.control?.isConnected ? rememberFocus(selection.current.control) : null);
114
+ return () => {
115
+ unregister();
116
+ setBar(null);
117
+ const active = document.activeElement;
118
+ const moved = movedOn.current;
119
+ const watched = watching.current;
120
+ quitWatch.current?.();
121
+ quitWatch.current = null;
122
+ watching.current = null;
123
+ movedOn.current = true;
124
+ if (!(active instanceof HTMLElement) || !(el.contains(active) || active === watched) || moved || inputModality() === "pointer") return;
125
+ const { control, box, page } = selection.current;
126
+ const at = point.current;
127
+ if (pageAddress() !== page) return;
128
+ focusAfterSelfRemoval(active, {
129
+ group: null,
130
+ point: at ?? void 0,
131
+ to: () => control?.isConnected ? control : box?.isConnected ? selectionIn(box, at) : null,
132
+ samePage: true,
133
+ removedOnly: true
134
+ });
135
+ };
136
+ }, []);
137
+ useLayoutEffect(() => {
138
+ if (!bar) return;
139
+ const report = () => {
140
+ if (host) host.setRoom(bar, bar.offsetHeight);
141
+ else presence?.room(bar, bulkBarRoom(bar.offsetHeight));
142
+ };
143
+ report();
144
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(report);
145
+ observer?.observe(bar);
146
+ return () => {
147
+ observer?.disconnect();
148
+ if (host) host.setRoom(bar, 0);
149
+ else presence?.room(bar, 0);
150
+ };
151
+ }, [bar, host, presence]);
152
+ useLayoutEffect(() => {
153
+ const layer = bar?.parentElement;
154
+ if (!bar || !layer) return;
155
+ const measure = () => {
156
+ const style = getComputedStyle(layer);
157
+ const room = layer.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);
158
+ setFit((was) => {
159
+ const full = !compact || !was || was.labels !== labels ? bar.scrollWidth : was.full;
160
+ return was && was.labels === labels && was.full === full && was.room === room ? was : { labels, full, room };
161
+ });
162
+ };
163
+ measure();
164
+ if (typeof ResizeObserver === "undefined") return;
165
+ const observer = new ResizeObserver(measure);
166
+ observer.observe(layer);
167
+ return () => observer.disconnect();
168
+ }, [bar, compact, labels]);
169
+ useEffect(() => {
170
+ if (!bar || inputModality() !== "keyboard") return;
171
+ const frame = requestAnimationFrame(() => {
172
+ const active = document.activeElement;
173
+ if (!(active instanceof HTMLElement) || active === document.body || bar.contains(active)) return;
174
+ if (active.getBoundingClientRect().bottom > bar.getBoundingClientRect().top) active.scrollIntoView({ block: "nearest" });
175
+ });
176
+ return () => cancelAnimationFrame(frame);
177
+ }, [bar]);
178
+ useEffect(() => {
179
+ if (shown) presence?.say(`${selectedCount} selected`);
180
+ }, [presence, shown, selectedCount]);
181
+ if (!shown) return null;
58
182
  const dropdownItems = actions.map((action) => ({
59
183
  label: action.label,
60
184
  icon: action.icon,
61
185
  onClick: action.onClick,
62
186
  variant: action.variant
63
187
  }));
64
- return /* @__PURE__ */ jsxs(
188
+ const count = (compact ? Math.min(actions.length, 1) : actions.length) + 1;
189
+ const current = Math.min(stop, count - 1);
190
+ const controls = () => bar ? Array.from(bar.querySelectorAll(CONTROL)) : [];
191
+ const tab = (i) => ({ "data-bulk-bar-control": "", tabIndex: i === current ? 0 : -1 });
192
+ const onKeyDown = (e) => {
193
+ const list2 = controls();
194
+ const at = list2.indexOf(e.target);
195
+ if (at < 0) return;
196
+ const to = e.key === "ArrowRight" ? (at + 1) % list2.length : e.key === "ArrowLeft" ? (at - 1 + list2.length) % list2.length : e.key === "Home" ? 0 : e.key === "End" ? list2.length - 1 : -1;
197
+ if (to < 0) return;
198
+ e.preventDefault();
199
+ list2[to].focus();
200
+ watch(list2[to]);
201
+ };
202
+ const onFocus = (e) => {
203
+ const at = controls().indexOf(e.target);
204
+ if (at >= 0) setStop(at);
205
+ watch(e.target);
206
+ };
207
+ const toolbar = /* @__PURE__ */ jsxs(
65
208
  "div",
66
209
  {
210
+ ref: onToolbar,
211
+ "data-bulk-bar": "",
67
212
  className: cn(
68
- "flex items-center gap-ds-3 rounded-[var(--ds-radius-md)]",
213
+ "pointer-events-auto max-w-full flex items-center gap-ds-3 rounded-[var(--ds-radius-md)]",
69
214
  "bg-[var(--ds-bg-surface-elevated)] border-[length:var(--ds-border-width)] border-[var(--ds-accent-ink)]",
70
215
  "shadow-[var(--ds-shadow-dropdown),var(--ds-glass-inset)]",
71
216
  "[backdrop-filter:var(--ds-glass-filter)] [-webkit-backdrop-filter:var(--ds-glass-filter)]",
@@ -74,16 +219,18 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
74
219
  style: { padding: "var(--ds-space-3) var(--ds-space-4)" },
75
220
  role: "toolbar",
76
221
  "aria-label": `${selectedCount} items selected`,
222
+ onKeyDown,
223
+ onFocus,
77
224
  children: [
78
225
  /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-[var(--ds-accent-ink)] tabular-nums whitespace-nowrap", children: [
79
226
  selectedCount,
80
227
  " selected"
81
228
  ] }),
82
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-[var(--ds-border-subtle)]" }),
83
- isMobile ? /* @__PURE__ */ jsx(
229
+ actions.length > 0 && /* @__PURE__ */ jsx("div", { className: "h-4 w-px shrink-0 bg-[var(--ds-border-subtle)]" }),
230
+ actions.length > 0 && (compact ? /* @__PURE__ */ jsx(
84
231
  DropdownMenu,
85
232
  {
86
- trigger: /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "more-vertical", size: 14 }), children: "Actions" }),
233
+ trigger: /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", iconLeft: /* @__PURE__ */ jsx(Icon, { name: "more-vertical", size: 14 }), ...tab(0), children: "Actions" }),
87
234
  items: dropdownItems,
88
235
  align: "start"
89
236
  }
@@ -93,28 +240,31 @@ function BulkActionBar({ selectedCount, actions, onClearSelection, className })
93
240
  type: "button",
94
241
  variant: "secondary",
95
242
  size: "sm",
243
+ className: "whitespace-nowrap",
96
244
  onClick: action.onClick,
245
+ ...tab(i),
97
246
  children: action.label
98
247
  },
99
248
  i
100
- )) }),
249
+ )) })),
101
250
  /* @__PURE__ */ jsx(
102
251
  "button",
103
252
  {
104
253
  type: "button",
105
- onClick: (e) => {
106
- const bar = e.currentTarget.closest('[role="toolbar"]');
107
- focusAfterSelfRemoval(e.currentTarget, { item: bar, to: selectAllFor(bar) });
108
- onClearSelection();
109
- },
110
- className: cn("ml-auto text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
254
+ onClick: () => onClearSelection(),
255
+ className: cn("ml-auto shrink-0 text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)] transition-colors cursor-pointer", FOCUS_RING),
111
256
  "aria-label": "Clear selection",
257
+ ...tab(count - 1),
112
258
  children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 16 })
113
259
  }
114
260
  )
115
261
  ]
116
262
  }
117
263
  );
264
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
265
+ !bearing && /* @__PURE__ */ jsx("span", { ref: setAnchor, hidden: true, "data-bulk-bar-anchor": "" }),
266
+ target && createPortal(host ? toolbar : /* @__PURE__ */ jsx("div", { className: BULK_BAR_WINDOW_LAYER, "data-bulk-bar-layer": "", children: toolbar }), target)
267
+ ] });
118
268
  }
119
269
  var CONTROL_HEIGHT = {
120
270
  sm: "h-[var(--ds-control-height-sm)]",
@@ -1734,6 +1884,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
1734
1884
  setBodyEl(el);
1735
1885
  }, [bodyInset]);
1736
1886
  useKeyboardScroll(bodyEl, title, open);
1887
+ const { setLayer: setBarLayer, room: barStack, value: barHost } = useBulkBarHost();
1737
1888
  const onCloseRef = useLatest(onClose);
1738
1889
  const onSubmitRef = useLatest(onSubmit);
1739
1890
  const isTopLayer = useOverlayLayer(open);
@@ -1868,19 +2019,28 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
1868
2019
  }
1869
2020
  ) })
1870
2021
  ] }),
1871
- children && /* @__PURE__ */ jsx(
1872
- "div",
1873
- {
1874
- ref: bodyRef,
1875
- className: cn("min-h-0 flex-[0_1_auto] overflow-y-auto pb-ds-6", FOCUS_INSET),
1876
- style: {
1877
- paddingLeft: variant === "destructive" ? "calc(var(--ds-page-gutter) + 2.5rem + var(--ds-space-3))" : "var(--ds-page-gutter)",
1878
- paddingRight: insetLessScrollbar("var(--ds-page-gutter)")
1879
- },
1880
- children: /* @__PURE__ */ jsx(SurfaceBoundary, { children: /* @__PURE__ */ jsx(DrawerFooterCtx, { value: null, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) }) }) })
1881
- }
1882
- ),
1883
- isMobile && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "flex-1 min-h-0" }),
2022
+ children && // the body's box hosts a bulk-action bar from inside: at its foot, above the footer (Sprint 25.33). On a phone it
2023
+ // fills down to the footer, so that foot is the screen's, not the middle of it (the fix pass of 25.33)
2024
+ /* @__PURE__ */ jsxs("div", { className: cn("relative min-h-0 flex flex-col", isMobile ? "flex-1" : "flex-[0_1_auto]"), children: [
2025
+ /* @__PURE__ */ jsxs(
2026
+ "div",
2027
+ {
2028
+ ref: bodyRef,
2029
+ className: cn("min-h-0 flex-[0_1_auto] overflow-y-auto pb-ds-6", FOCUS_INSET),
2030
+ style: {
2031
+ paddingLeft: variant === "destructive" ? "calc(var(--ds-page-gutter) + 2.5rem + var(--ds-space-3))" : "var(--ds-page-gutter)",
2032
+ paddingRight: insetLessScrollbar("var(--ds-page-gutter)"),
2033
+ ...barStack ? { scrollPaddingBottom: bulkBarRoom(barStack) } : {}
2034
+ },
2035
+ children: [
2036
+ /* @__PURE__ */ jsx(SurfaceBoundary, { children: /* @__PURE__ */ jsx(DrawerFooterCtx, { value: null, children: /* @__PURE__ */ jsx(OverlayMarkScope, { mark, children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children: /* @__PURE__ */ jsx(BulkBarHostCtx, { value: barHost, children }) }) }) }) }),
2037
+ /* @__PURE__ */ jsx(BulkBarSpacer, { room: barStack })
2038
+ ]
2039
+ }
2040
+ ),
2041
+ /* @__PURE__ */ jsx("div", { ref: setBarLayer, "data-bulk-bar-layer": "", className: BULK_BAR_LAYER })
2042
+ ] }),
2043
+ isMobile && !children && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "flex-1 min-h-0" }),
1884
2044
  /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
1885
2045
  footer && /* @__PURE__ */ jsx("div", { className: cn(
1886
2046
  "shrink-0 flex justify-end gap-ds-2 py-ds-4 px-[var(--ds-page-gutter)]",
@@ -2279,101 +2439,104 @@ function FileUpload({ onFiles, files, upload, onCancel, onRetry, onRemove, accep
2279
2439
  const canCancel = !controlled || !!onCancel;
2280
2440
  const canRetry = !controlled || !!onRetry;
2281
2441
  const canRemove = (item) => !!onRemove || !controlled && item.status === "error";
2282
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-ds-3", className), children: [
2283
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-ds-1.5", children: [
2284
- showLabel && /* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: label }),
2285
- /* @__PURE__ */ jsxs(
2286
- "div",
2287
- {
2288
- onDragOver: (e) => {
2289
- e.preventDefault();
2290
- setDragOver(true);
2291
- },
2292
- onDragLeave: () => setDragOver(false),
2293
- onDrop: handleDrop,
2294
- onClick: () => inputRef.current?.click(),
2295
- className: cn(
2296
- "flex flex-col items-center justify-center gap-ds-2 cursor-pointer group",
2297
- "border-2 border-dashed rounded-[var(--ds-radius-md)]",
2298
- "transition-colors",
2299
- // The real file input is visually hidden inside; Tab lands on it and
2300
- // Enter/Space open the picker natively, so the zone shows its focus.
2301
- invalid ? ERROR_HAS_FOCUS_RING : FIELD_HAS_FOCUS_RING,
2302
- dragOver ? `${ACTIVE_RING} bg-[var(--ds-accent-whisper)]` : invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)] hover:border-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-whisper)]"
2303
- ),
2304
- style: { padding: "var(--ds-space-8) var(--ds-space-4)" },
2305
- children: [
2306
- /* @__PURE__ */ jsx("div", { className: cn(
2307
- "flex items-center justify-center w-10 h-10 rounded-[var(--ds-radius-full)] transition-colors",
2308
- "bg-[var(--ds-bg-muted)] group-hover:bg-[var(--ds-accent-subtle)]"
2309
- ), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 20, className: "text-[var(--ds-text-muted)] group-hover:text-[var(--ds-accent-subtle-text)] transition-colors" }) }),
2310
- /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
2311
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: multiple ? "Drop files here or browse" : "Drop a file here or browse" }),
2312
- constraints && /* @__PURE__ */ jsx("p", { id: constraintsId, className: "text-xs text-[var(--ds-text-muted)] mt-ds-1", children: constraints })
2313
- ] }),
2314
- /* @__PURE__ */ jsx(
2315
- "input",
2316
- {
2317
- ref: inputRef,
2318
- id: inputId,
2319
- type: "file",
2320
- accept,
2321
- multiple,
2322
- className: "sr-only",
2323
- "aria-label": control.labelId ? void 0 : control.label,
2324
- "aria-labelledby": control.labelId,
2325
- "aria-describedby": describedBy,
2326
- "aria-invalid": invalid || void 0,
2327
- onChange: (e) => {
2328
- if (e.target.files?.length) addFiles(e.target.files);
2329
- e.target.value = "";
2442
+ return (
2443
+ // a wide control: in a form, its row fills the form rather than keeping a field's reading width (Sprint 25.33)
2444
+ /* @__PURE__ */ jsxs("div", { "data-wide-control": "", className: cn("space-y-ds-3", className), children: [
2445
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-ds-1.5", children: [
2446
+ showLabel && /* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: label }),
2447
+ /* @__PURE__ */ jsxs(
2448
+ "div",
2449
+ {
2450
+ onDragOver: (e) => {
2451
+ e.preventDefault();
2452
+ setDragOver(true);
2453
+ },
2454
+ onDragLeave: () => setDragOver(false),
2455
+ onDrop: handleDrop,
2456
+ onClick: () => inputRef.current?.click(),
2457
+ className: cn(
2458
+ "flex flex-col items-center justify-center gap-ds-2 cursor-pointer group",
2459
+ "border-2 border-dashed rounded-[var(--ds-radius-md)]",
2460
+ "transition-colors",
2461
+ // The real file input is visually hidden inside; Tab lands on it and
2462
+ // Enter/Space open the picker natively, so the zone shows its focus.
2463
+ invalid ? ERROR_HAS_FOCUS_RING : FIELD_HAS_FOCUS_RING,
2464
+ dragOver ? `${ACTIVE_RING} bg-[var(--ds-accent-whisper)]` : invalid ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)] hover:border-[var(--ds-accent-primary)] hover:bg-[var(--ds-accent-whisper)]"
2465
+ ),
2466
+ style: { padding: "var(--ds-space-8) var(--ds-space-4)" },
2467
+ children: [
2468
+ /* @__PURE__ */ jsx("div", { className: cn(
2469
+ "flex items-center justify-center w-10 h-10 rounded-[var(--ds-radius-full)] transition-colors",
2470
+ "bg-[var(--ds-bg-muted)] group-hover:bg-[var(--ds-accent-subtle)]"
2471
+ ), children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: 20, className: "text-[var(--ds-text-muted)] group-hover:text-[var(--ds-accent-subtle-text)] transition-colors" }) }),
2472
+ /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
2473
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: multiple ? "Drop files here or browse" : "Drop a file here or browse" }),
2474
+ constraints && /* @__PURE__ */ jsx("p", { id: constraintsId, className: "text-xs text-[var(--ds-text-muted)] mt-ds-1", children: constraints })
2475
+ ] }),
2476
+ /* @__PURE__ */ jsx(
2477
+ "input",
2478
+ {
2479
+ ref: inputRef,
2480
+ id: inputId,
2481
+ type: "file",
2482
+ accept,
2483
+ multiple,
2484
+ className: "sr-only",
2485
+ "aria-label": control.labelId ? void 0 : control.label,
2486
+ "aria-labelledby": control.labelId,
2487
+ "aria-describedby": describedBy,
2488
+ "aria-invalid": invalid || void 0,
2489
+ onChange: (e) => {
2490
+ if (e.target.files?.length) addFiles(e.target.files);
2491
+ e.target.value = "";
2492
+ }
2330
2493
  }
2331
- }
2332
- )
2333
- ]
2334
- }
2335
- ),
2336
- rejected && /* @__PURE__ */ jsx(InlineFieldError, { id: rejectedId, message: rejected, className: "mt-0" }),
2337
- waiting && /* @__PURE__ */ jsx(InlineFieldError, { id: waitingId, message: waiting, className: "mt-0" })
2338
- ] }),
2339
- shown.length > 0 && /* @__PURE__ */ jsx("ul", { ref: listRef, className: "space-y-ds-2", children: shown.map((item) => {
2340
- const progress = percentOf(item.progress);
2341
- const words = item.status === "queued" ? "Waiting" : item.status === "uploading" ? `${Math.round(progress)}%` : item.status === "done" ? "Uploaded" : item.status === "error" ? "Failed" : null;
2342
- return /* @__PURE__ */ jsxs(
2343
- "li",
2344
- {
2345
- "data-upload-id": item.id,
2346
- "data-upload-status": item.status,
2347
- onFocus: (e) => {
2348
- if (e.target instanceof HTMLButtonElement) lastAction.current = { id: item.id, el: e.target };
2494
+ )
2495
+ ]
2496
+ }
2497
+ ),
2498
+ rejected && /* @__PURE__ */ jsx(InlineFieldError, { id: rejectedId, message: rejected, className: "mt-0" }),
2499
+ waiting && /* @__PURE__ */ jsx(InlineFieldError, { id: waitingId, message: waiting, className: "mt-0" })
2500
+ ] }),
2501
+ shown.length > 0 && /* @__PURE__ */ jsx("ul", { ref: listRef, className: "space-y-ds-2", children: shown.map((item) => {
2502
+ const progress = percentOf(item.progress);
2503
+ const words = item.status === "queued" ? "Waiting" : item.status === "uploading" ? `${Math.round(progress)}%` : item.status === "done" ? "Uploaded" : item.status === "error" ? "Failed" : null;
2504
+ return /* @__PURE__ */ jsxs(
2505
+ "li",
2506
+ {
2507
+ "data-upload-id": item.id,
2508
+ "data-upload-status": item.status,
2509
+ onFocus: (e) => {
2510
+ if (e.target instanceof HTMLButtonElement) lastAction.current = { id: item.id, el: e.target };
2511
+ },
2512
+ className: cn(
2513
+ "flex items-center gap-ds-3 text-sm",
2514
+ "border-[length:var(--ds-border-width)]",
2515
+ item.status === "error" ? "border-[var(--ds-status-error)]" : "border-[var(--ds-border-subtle)]",
2516
+ "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]"
2517
+ ),
2518
+ style: { padding: "var(--ds-space-3)" },
2519
+ children: [
2520
+ /* @__PURE__ */ jsx(Icon, { name: "folder", size: 16, className: "text-[var(--ds-text-muted)] shrink-0" }),
2521
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2522
+ /* @__PURE__ */ jsx(TruncFileName, { className: "text-[var(--ds-text-primary)]", children: item.name }),
2523
+ (item.size != null || words) && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)]", children: [item.size != null ? formatSize(item.size) : null, words].filter(Boolean).join(" \xB7 ") }),
2524
+ item.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: progress, size: "sm", label: `Uploading ${item.name}`, className: "mt-ds-1" }),
2525
+ item.status === "error" && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-xs text-[var(--ds-status-error-text)]", children: item.error ?? "The upload didn't work." })
2526
+ ] }),
2527
+ item.status === "done" && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, className: "text-[var(--ds-status-success)] shrink-0", "aria-hidden": "true" }),
2528
+ running(item) && canCancel && // the visible word starts the name, which says which file (WCAG 2.5.3)
2529
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => cancel(item), "aria-label": `Cancel ${item.name}`, children: "Cancel" }),
2530
+ item.status === "error" && canRetry && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => retry(item), "aria-label": `Retry ${item.name}`, children: "Retry" }),
2531
+ !running(item) && canRemove(item) && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: () => remove(item), "aria-label": `Remove ${item.name}`, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) })
2532
+ ]
2349
2533
  },
2350
- className: cn(
2351
- "flex items-center gap-ds-3 text-sm",
2352
- "border-[length:var(--ds-border-width)]",
2353
- item.status === "error" ? "border-[var(--ds-status-error)]" : "border-[var(--ds-border-subtle)]",
2354
- "rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-surface)]"
2355
- ),
2356
- style: { padding: "var(--ds-space-3)" },
2357
- children: [
2358
- /* @__PURE__ */ jsx(Icon, { name: "folder", size: 16, className: "text-[var(--ds-text-muted)] shrink-0" }),
2359
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2360
- /* @__PURE__ */ jsx(TruncFileName, { className: "text-[var(--ds-text-primary)]", children: item.name }),
2361
- (item.size != null || words) && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ds-text-muted)]", children: [item.size != null ? formatSize(item.size) : null, words].filter(Boolean).join(" \xB7 ") }),
2362
- item.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: progress, size: "sm", label: `Uploading ${item.name}`, className: "mt-ds-1" }),
2363
- item.status === "error" && /* @__PURE__ */ jsx("p", { className: "mt-ds-1 text-xs text-[var(--ds-status-error-text)]", children: item.error ?? "The upload didn't work." })
2364
- ] }),
2365
- item.status === "done" && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, className: "text-[var(--ds-status-success)] shrink-0", "aria-hidden": "true" }),
2366
- running(item) && canCancel && // the visible word starts the name, which says which file (WCAG 2.5.3)
2367
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => cancel(item), "aria-label": `Cancel ${item.name}`, children: "Cancel" }),
2368
- item.status === "error" && canRetry && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => retry(item), "aria-label": `Retry ${item.name}`, children: "Retry" }),
2369
- !running(item) && canRemove(item) && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: () => remove(item), "aria-label": `Remove ${item.name}`, children: /* @__PURE__ */ jsx(Icon, { name: "close", size: 14 }) })
2370
- ]
2371
- },
2372
- item.id
2373
- );
2374
- }) }),
2375
- /* @__PURE__ */ jsx("div", { role: "status", className: "sr-only", children: /* @__PURE__ */ jsx("span", { children: heard.text }, heard.n) })
2376
- ] });
2534
+ item.id
2535
+ );
2536
+ }) }),
2537
+ /* @__PURE__ */ jsx("div", { role: "status", className: "sr-only", children: /* @__PURE__ */ jsx("span", { children: heard.text }, heard.n) })
2538
+ ] })
2539
+ );
2377
2540
  }
2378
2541
  function SliderRange({
2379
2542
  value,
@@ -3064,6 +3227,7 @@ var Tag = forwardRef(function Tag2({
3064
3227
  size = "md",
3065
3228
  color,
3066
3229
  maxWidth,
3230
+ width,
3067
3231
  onRemove,
3068
3232
  onClick,
3069
3233
  selected,
@@ -3098,8 +3262,9 @@ var Tag = forwardRef(function Tag2({
3098
3262
  "aria-disabled": !clickable && disabled || void 0,
3099
3263
  "data-selected": isSelected ? "" : void 0,
3100
3264
  className: cn(
3101
- "inline-flex items-center font-medium max-w-full min-w-0 whitespace-nowrap",
3265
+ "inline-flex items-center font-medium w-fit max-w-full min-w-0 whitespace-nowrap",
3102
3266
  "rounded-[var(--ds-radius-sm)]",
3267
+ width && "justify-center",
3103
3268
  "transition-colors duration-[var(--ds-transition-speed)]",
3104
3269
  sizeStyles[size],
3105
3270
  colorClasses,
@@ -3107,7 +3272,7 @@ var Tag = forwardRef(function Tag2({
3107
3272
  disabled && "opacity-disabled cursor-not-allowed",
3108
3273
  className
3109
3274
  ),
3110
- style: maxWidth ? { maxWidth } : void 0,
3275
+ style: maxWidth || width ? { ...width ? { width } : {}, ...maxWidth ? { maxWidth } : {} } : void 0,
3111
3276
  children: [
3112
3277
  clickable ? /* @__PURE__ */ jsx(
3113
3278
  "button",
@@ -4057,6 +4222,15 @@ var Alert = forwardRef(function Alert2({
4057
4222
  }
4058
4223
  );
4059
4224
  });
4225
+ var FORM_ROWS = cn(
4226
+ "[--gs-form-default:calc(var(--ds-bp-md)_-_2*var(--ds-space-6))]",
4227
+ "[:where(&)>*]:max-w-[var(--gs-row-cap,var(--ds-form-max,var(--gs-form-default)))]",
4228
+ // the row is the wide control itself (a FileUpload with its own label, written straight into the form), or holds one
4229
+ "[&>[data-wide-control]]:[--gs-row-cap:none]",
4230
+ "[&>*:has([data-wide-control])]:[--gs-row-cap:none]",
4231
+ "[&:has([data-wide-control])>[data-form-actions]]:[--gs-row-cap:none]"
4232
+ );
4233
+ var FORM_CAP = "max-w-[var(--gs-form-cap,var(--ds-form-max,var(--gs-form-default)))] has-[[data-wide-control]]:[--gs-form-cap:none]";
4060
4234
  var isThenable = (value) => !!value && typeof value.then === "function";
4061
4235
  function stampFormId(node, formId) {
4062
4236
  return Children.map(node, (child) => {
@@ -4246,8 +4420,9 @@ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, a
4246
4420
  ": ",
4247
4421
  f.message
4248
4422
  ] }, f.id || f.label)) }) }) : null;
4423
+ const callerWidth = /(?:^|[\s:!])max-w-/.test(className ?? "");
4249
4424
  const actionsNode = actions && !drawerFooter ? /* @__PURE__ */ jsx("div", { "data-form-actions": "", className: cn("flex flex-wrap items-center gap-ds-2 pt-ds-2", actionsAlign === "end" ? "justify-end" : "justify-start"), children: actions }) : null;
4250
- return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted, register }, children: /* @__PURE__ */ jsx(FormChangeCtx.Provider, { value: reportChange, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className: cn("flex flex-col gap-ds-5 max-w-[var(--ds-form-max,calc(var(--ds-bp-md)_-_2*var(--ds-space-6)))]", className), style: fullWidth ? { "--ds-form-max": "none" } : void 0, noValidate: true, children: [
4425
+ return /* @__PURE__ */ jsx(FormCtx.Provider, { value: { errors, setError, clearErrors, validationMode, submitted, register }, children: /* @__PURE__ */ jsx(FormChangeCtx.Provider, { value: reportChange, children: /* @__PURE__ */ jsxs("form", { ref: setRefs, id: drawerFooter ? formId : void 0, onSubmit: handleSubmit, onFocusCapture: userActed, onInputCapture: userActed, className: cn("flex flex-col gap-ds-5", FORM_ROWS, FORM_CAP, className), style: fullWidth || callerWidth ? { "--ds-form-max": "none" } : void 0, noValidate: true, children: [
4251
4426
  summaryNode && /* @__PURE__ */ jsx("div", { ref: summaryRef, "data-form-error-summary": "", tabIndex: -1, className: cn("rounded-[var(--ds-radius-md)] focus-visible:*:shadow-none", FOCUS_RING), children: summaryNode }),
4252
4427
  children,
4253
4428
  actionsNode
@@ -4381,7 +4556,7 @@ function TransferList({
4381
4556
  setCheckedSelected(allChecked ? /* @__PURE__ */ new Set() : new Set(enabledIds));
4382
4557
  }, [filteredSelected, checkedSelected]);
4383
4558
  const isStacked = useBreakpoint("sm");
4384
- return /* @__PURE__ */ jsxs("div", { ref, className: cn(isStacked ? "flex flex-col gap-ds-2" : "flex items-stretch gap-ds-3", className), children: [
4559
+ return /* @__PURE__ */ jsxs("div", { ref, "data-wide-control": "", className: cn(isStacked ? "flex flex-col gap-ds-2" : "flex items-stretch gap-ds-3", className), children: [
4385
4560
  /* @__PURE__ */ jsx(
4386
4561
  Panel,
4387
4562
  {
@@ -5757,9 +5932,16 @@ var PIN_FOCUS_END = "[tr:focus-visible>&]:after:right-[3px] [tr:focus-visible>&]
5757
5932
  var PINNED_BACKGROUND = "linear-gradient(var(--gs-row-tint, transparent), var(--gs-row-tint, transparent)), linear-gradient(var(--gs-pinned-surface), var(--gs-pinned-surface)), var(--gs-pinned-canvas)";
5758
5933
  var PINNED_LAYERS = cn(
5759
5934
  "[--gs-pinned-surface:var(--ds-bg-surface)] [--gs-pinned-canvas:var(--ds-bg-canvas)]",
5760
- "supports-[color:oklch(from_red_l_c_h)]:[--gs-pinned-surface:oklch(from_var(--ds-bg-surface)_l_c_h_/_max(alpha,min(1,alpha*1000)))]",
5761
5935
  "supports-[color:oklch(from_red_l_c_h)]:[--gs-pinned-canvas:oklch(from_var(--ds-bg-canvas)_l_c_h_/_1)]"
5762
5936
  );
5937
+ function drawnText(el) {
5938
+ const copy = el.cloneNode(true);
5939
+ copy.querySelectorAll(".sr-only").forEach((n) => n.remove());
5940
+ return copy.textContent ?? "";
5941
+ }
5942
+ var CELL_ACTION = "button, a[href], [role='button'], [role='link']";
5943
+ var CELL_READ_OR_SET = "input:not([type='hidden']), select, textarea, img:not([alt='']), [role='switch'], [role='checkbox'], [role='radio'], [role='slider'], [role='spinbutton'], [role='combobox'], [role='img']:not([aria-hidden='true'])";
5944
+ var iconActionsOnly = (td) => !td.querySelector(CELL_READ_OR_SET) && !!td.querySelector(CELL_ACTION);
5763
5945
  var PIN_SHADOW = "oklch(0 0 0 / 0.18)";
5764
5946
  var ROW_CONTROL = `${FOCUSABLE}, label, [role="button"], [role="checkbox"], [role="switch"], [role="menuitem"], [role="link"]`;
5765
5947
  function clickIsCard(e) {
@@ -6312,7 +6494,7 @@ function Table({
6312
6494
  const endColumns = columns.filter((c) => c.pin === "end").length;
6313
6495
  const endCount = endColumns + trail;
6314
6496
  const nameIndex = columns.length > endColumns ? lead : -1;
6315
- const { ref: scrollRef, elRef: scrollEl, measure: measureScroll, scrollable, edges, fadeProps } = useScrollEdges({
6497
+ const { ref: scrollRef, elRef: scrollEl, measure: measureScroll, scrollable, overflowing, edges, fadeProps } = useScrollEdges({
6316
6498
  enabled: !isMobile,
6317
6499
  noFade: { left: lead + (pins.nameFits ? 1 : 0) > 0, right: endCount > 0 }
6318
6500
  });
@@ -6358,6 +6540,7 @@ function Table({
6358
6540
  }, [isMobile, lead, trail, endCount, nameIndex, scrollEl, columns, drawn.length, visibleKeys.length]);
6359
6541
  const pinnedStart = lead + (pins.nameFits ? 1 : 0);
6360
6542
  const pinStyle = (position, total) => {
6543
+ if (!overflowing) return void 0;
6361
6544
  if (position < pinnedStart && pins.start[position] !== void 0) {
6362
6545
  const inner = position === pinnedStart - 1;
6363
6546
  return { position: "sticky", left: pins.start[position], zIndex: 1, background: PINNED_BACKGROUND, boxShadow: inner && edges.left ? `6px 0 6px -6px ${PIN_SHADOW}` : void 0 };
@@ -6376,17 +6559,22 @@ function Table({
6376
6559
  };
6377
6560
  const fade = fadeProps;
6378
6561
  const [renderedKinds, setRenderedKinds] = useState(() => /* @__PURE__ */ new Map());
6562
+ const [iconOnlyCols, setIconOnlyCols] = useState(() => /* @__PURE__ */ new Set());
6379
6563
  useLayoutEffect(() => {
6380
6564
  const table2 = tableRef.current;
6381
6565
  if (!table2 || isMobile) return;
6382
6566
  const next = /* @__PURE__ */ new Map();
6567
+ const icons = /* @__PURE__ */ new Set();
6383
6568
  for (const col of columns) {
6569
+ const cells = Array.from(table2.querySelectorAll(`:scope > tbody > tr > td[data-col="${CSS.escape(col.key)}"]`)).slice(0, 50);
6570
+ const texts = cells.map(drawnText);
6571
+ if (cells.length && cells.every((td, i) => !texts[i].trim() && iconActionsOnly(td))) icons.add(col.key);
6384
6572
  if (kindOf2(col)) continue;
6385
- const texts = Array.from(table2.querySelectorAll(`:scope > tbody > tr > td[data-col="${CSS.escape(col.key)}"]`)).slice(0, 50).map((td) => td.textContent ?? "");
6386
6573
  const kind = texts.length ? kindOfRenderedText(texts) : void 0;
6387
6574
  if (kind) next.set(col.key, kind);
6388
6575
  }
6389
6576
  setRenderedKinds((prev) => prev.size === next.size && [...next].every(([k, v]) => prev.get(k) === v) ? prev : next);
6577
+ setIconOnlyCols((prev) => prev.size === icons.size && [...icons].every((k) => prev.has(k)) ? prev : icons);
6390
6578
  }, [columns, kindOf2, drawn, isMobile, visibleKeys.length]);
6391
6579
  const [focusKey, setFocusKey] = useState(null);
6392
6580
  const visibleSet = new Set(visibleKeys);
@@ -6676,7 +6864,7 @@ function Table({
6676
6864
  scrollToRow(key);
6677
6865
  }, [drawnStart, drawn.length, visibleKeys.length, scrollToRow]);
6678
6866
  const cardHeading = title ? /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: false }) : null;
6679
- const titledCards = (body) => /* @__PURE__ */ jsxs("div", { ref: listRoot, "data-ds-list": "", "data-gs": "table", className: cn("flex flex-col", listClass), onFocus: noteFocus, onBlur: forgetFocus, children: [
6867
+ const titledCards = (body) => /* @__PURE__ */ jsxs("div", { ref: listRoot, "data-ds-list": "", "data-gs": "table", "data-wide-control": "", className: cn("flex flex-col", listClass), onFocus: noteFocus, onBlur: forgetFocus, children: [
6680
6868
  cardHeading,
6681
6869
  body
6682
6870
  ] });
@@ -6730,11 +6918,11 @@ function Table({
6730
6918
  ] }, col.key)) }, r)) })
6731
6919
  ] });
6732
6920
  if (title) return titledCards(skeletonCards);
6733
- return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", className, children: skeletonCards });
6921
+ return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", "data-wide-control": "", className, children: skeletonCards });
6734
6922
  }
6735
6923
  if (data.length === 0 && emptyState) {
6736
6924
  if (title) return titledCards(/* @__PURE__ */ jsx("div", { className: "py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", children: emptyState }));
6737
- 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 });
6925
+ return /* @__PURE__ */ jsx("div", { ref: setRoot, "data-ds-list": "", "data-gs": "table", "data-wide-control": "", className: cn("py-ds-10 text-center text-sm text-[var(--ds-text-muted)]", className), children: emptyState });
6738
6926
  }
6739
6927
  const cards = /* @__PURE__ */ jsx(TableCardCtx, { value: true, children: /* @__PURE__ */ jsx(RowsCtx, { value: true, children: /* @__PURE__ */ jsxs("div", { ref: setCards, "data-ds-list": title ? void 0 : "", "data-gs": title ? void 0 : "table", className: cn("relative flex flex-col gap-ds-3 overflow-y-auto -m-[4px] p-[4px] scroll-p-[4px]", FOCUS_INSET, title ? "min-h-0" : listClass), onFocus: title ? void 0 : noteFocus, onBlur: title ? void 0 : forgetFocus, children: [
6740
6928
  loading && data.length > 0 && /* @__PURE__ */ jsx(RefreshLine, { className: "inset-x-[4px] rounded-full" }),
@@ -6789,8 +6977,10 @@ function Table({
6789
6977
  }
6790
6978
  } : void 0,
6791
6979
  children: [
6792
- selectable && box && // "Select" and the row's name, as in the table (T15); a parent with some of its rows chosen is mixed
6793
- /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, indeterminate: box.mixed, onChange: (_, { shiftKey: shiftKey2 }) => toggleBox(originalIdx, id, shiftKey2), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-card-name-${idPart(id)}` : `${headingId}-select` }),
6980
+ selectable && box && // "Select" and the row's name, as in the table (T15); a parent with some of its rows chosen is mixed. Marked as
6981
+ // the card's selection box: with no select-all on a phone, the first card's is where a BulkActionBar's
6982
+ // handover finds the selection (the fix pass of 25.33)
6983
+ /* @__PURE__ */ jsx("div", { "data-ds-select-card": "", children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, indeterminate: box.mixed, onChange: (_, { shiftKey: shiftKey2 }) => toggleBox(originalIdx, id, shiftKey2), "aria-labelledby": nameIndex >= 0 ? `${headingId}-select ${headingId}-card-name-${idPart(id)}` : `${headingId}-select` }) }),
6794
6984
  columns.map((col, c) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-ds-2 text-sm", children: [
6795
6985
  /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[var(--ds-text-secondary)] font-medium text-xs uppercase tracking-wider", children: col.header }),
6796
6986
  /* @__PURE__ */ jsx(CardValue, { value: row[col.key], id: lead + c === nameIndex ? `${headingId}-card-name-${idPart(id)}` : void 0 })
@@ -6864,8 +7054,9 @@ function Table({
6864
7054
  // a tree table is a treegrid (item 14): its rows have levels, and the arrow keys move between them
6865
7055
  /* @__PURE__ */ jsxs("table", { ref: tableRef, role: nested ? "treegrid" : void 0, "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: [
6866
7056
  /* @__PURE__ */ jsx("thead", { className: cn("bg-[var(--ds-bg-muted)]", stickyHeader && "sticky top-0 z-10"), onKeyDown: nested ? onHeadKeyDown : void 0, onFocus: nested ? onHeadFocus : void 0, 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: [
6867
- selectable && // data-ds-select-all: the selection's own control, where a BulkActionBar's Clear selection sends focus
6868
- /* @__PURE__ */ jsx("th", { "data-ds-select-all": "", className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), style: pinStyle(0, cellCount), children: /* @__PURE__ */ jsx(
7057
+ selectable && // data-ds-select-all: the selection's own control, where a BulkActionBar's Clear selection sends focus; and
7058
+ // whether the table holds a selection, said as it renders (the box's mixed state is set a moment later)
7059
+ /* @__PURE__ */ jsx("th", { "data-ds-select-all": "", "data-selection": allSelected ? "all" : someSelected ? "some" : "none", 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(
6869
7060
  Checkbox,
6870
7061
  {
6871
7062
  ref: selectAllRef,
@@ -6922,6 +7113,9 @@ function Table({
6922
7113
  ]
6923
7114
  }
6924
7115
  )
7116
+ ) : iconOnlyCols.has(col.key) ? (
7117
+ // a column of icon buttons: its name for a screen reader, its width its buttons' (Sprint 25.33)
7118
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: col.header })
6925
7119
  ) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-ds-1", children: col.header })
6926
7120
  },
6927
7121
  col.key
@@ -7104,6 +7298,7 @@ function Table({
7104
7298
  ref: listRoot,
7105
7299
  "data-ds-list": "",
7106
7300
  "data-gs": "table",
7301
+ "data-wide-control": "",
7107
7302
  className: cn("relative w-full min-w-0 flex flex-col", FRAME_CLASSES, FRAME_FOCUS_RING, control && "min-h-0", listClass),
7108
7303
  onFocus: noteFocus,
7109
7304
  onBlur: forgetFocus,
@@ -9268,38 +9463,42 @@ function ExpandableDescription({ children, className }) {
9268
9463
  function HorizontalLayout({ items, minItemWidth, label, className }) {
9269
9464
  const [row, setRow] = useState(null);
9270
9465
  useKeyboardScroll(row, label, true, false, "x");
9271
- return /* @__PURE__ */ jsx("div", { ref: setRow, "data-timeline-row": "", className: cn("relative flex items-start overflow-x-auto snap-x", FOCUS_INSET, className), children: items.map((item, i) => {
9272
- const isFirst = i === 0;
9273
- const isLast = i === items.length - 1;
9274
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 shrink-0 flex-col items-center snap-start", style: { minWidth: minItemWidth }, children: [
9275
- /* @__PURE__ */ jsxs("div", { className: "flex items-center w-full", children: [
9276
- /* @__PURE__ */ jsx(
9277
- "div",
9278
- {
9279
- className: cn(
9280
- "flex-1 h-[2px] transition-colors",
9281
- isFirst ? "bg-transparent" : "bg-[var(--ds-border-subtle)]"
9282
- )
9283
- }
9284
- ),
9285
- /* @__PURE__ */ jsx(Marker, { item, vertical: false }),
9286
- /* @__PURE__ */ jsx(
9287
- "div",
9288
- {
9289
- className: cn(
9290
- "flex-1 h-[2px] transition-colors",
9291
- isLast ? "bg-transparent" : "bg-[var(--ds-border-subtle)]"
9292
- )
9293
- }
9294
- )
9295
- ] }),
9296
- /* @__PURE__ */ jsxs("div", { className: "text-center mt-ds-2 px-ds-3", children: [
9297
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: item.title }),
9298
- item.description && /* @__PURE__ */ jsx(ExpandableDescription, { className: "text-center", children: item.description }),
9299
- /* @__PURE__ */ jsx(ItemTime, { item })
9300
- ] })
9301
- ] }, i);
9302
- }) });
9466
+ return (
9467
+ // 4 px of room inside its edges, and snapping that keeps it, so the first marker and the row's own keyboard outline
9468
+ // never meet (Sprint 25.33: the owner's answer to Sprint 25.30's look question)
9469
+ /* @__PURE__ */ jsx("div", { ref: setRow, "data-timeline-row": "", className: cn("relative flex items-start overflow-x-auto snap-x p-[4px] scroll-px-[4px]", FOCUS_INSET, className), children: items.map((item, i) => {
9470
+ const isFirst = i === 0;
9471
+ const isLast = i === items.length - 1;
9472
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 shrink-0 flex-col items-center snap-start", style: { minWidth: minItemWidth }, children: [
9473
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center w-full", children: [
9474
+ /* @__PURE__ */ jsx(
9475
+ "div",
9476
+ {
9477
+ className: cn(
9478
+ "flex-1 h-[2px] transition-colors",
9479
+ isFirst ? "bg-transparent" : "bg-[var(--ds-border-subtle)]"
9480
+ )
9481
+ }
9482
+ ),
9483
+ /* @__PURE__ */ jsx(Marker, { item, vertical: false }),
9484
+ /* @__PURE__ */ jsx(
9485
+ "div",
9486
+ {
9487
+ className: cn(
9488
+ "flex-1 h-[2px] transition-colors",
9489
+ isLast ? "bg-transparent" : "bg-[var(--ds-border-subtle)]"
9490
+ )
9491
+ }
9492
+ )
9493
+ ] }),
9494
+ /* @__PURE__ */ jsxs("div", { className: "text-center mt-ds-2 px-ds-3", children: [
9495
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--ds-text-primary)]", children: item.title }),
9496
+ item.description && /* @__PURE__ */ jsx(ExpandableDescription, { className: "text-center", children: item.description }),
9497
+ /* @__PURE__ */ jsx(ItemTime, { item })
9498
+ ] })
9499
+ ] }, i);
9500
+ }) })
9501
+ );
9303
9502
  }
9304
9503
  var exactFormat = (locale, d) => new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(d);
9305
9504
  var exactDayFormat = (locale, d) => new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(d);
@@ -9606,6 +9805,7 @@ function TreeView({
9606
9805
  "aria-label": ariaLabel,
9607
9806
  "aria-labelledby": ariaLabelledBy
9608
9807
  }) {
9808
+ useWantsInset();
9609
9809
  const [ownSelected, setOwnSelected] = useState(() => new Set(defaultSelected));
9610
9810
  const selected = useMemo(() => selectedProp ? new Set(selectedProp) : ownSelected, [selectedProp, ownSelected]);
9611
9811
  const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set([...defaultExpanded, ...foldersHolding(nodes, selected)]));
@@ -9760,7 +9960,7 @@ function TreeNodeRow({
9760
9960
  const hasChildren = !!node.children?.length;
9761
9961
  const isExpanded = expanded.has(node.id);
9762
9962
  const isSelected = selected.has(node.id);
9763
- const holdsSelection = !isSelected && holding.has(node.id);
9963
+ const holdsSelection2 = !isSelected && holding.has(node.id);
9764
9964
  const checkState = checkable ? getCheckState(node) : "unchecked";
9765
9965
  return /* @__PURE__ */ jsxs("div", { role: "treeitem", "aria-expanded": hasChildren ? isExpanded : void 0, "aria-selected": selectable ? isSelected : void 0, children: [
9766
9966
  /* @__PURE__ */ jsxs(
@@ -9778,7 +9978,7 @@ function TreeNodeRow({
9778
9978
  isSelected ? "bg-[var(--ds-tree-active-bg)] text-[var(--ds-tree-active-text)] font-medium border-[var(--ds-accent-ink)]" : "border-transparent text-[var(--ds-text-primary)] hover:bg-[var(--ds-tab-hover-bg)] hover:text-[var(--ds-text-primary)]",
9779
9979
  // A folder holding the selection, as SidebarNav draws a parent of the current page (Sprint 25.16 A18): no
9780
9980
  // fill, so it reads the plain tokens, never the active set (pill's on-primary text would vanish here).
9781
- holdsSelection && "font-medium",
9981
+ holdsSelection2 && "font-medium",
9782
9982
  node.disabled && "opacity-disabled pointer-events-none"
9783
9983
  ),
9784
9984
  style: { paddingLeft: `calc(var(--ds-space-3) * ${depth} + var(--ds-space-2))` },
@@ -9805,7 +10005,7 @@ function TreeNodeRow({
9805
10005
  {
9806
10006
  name: "chevron-right",
9807
10007
  size: 14,
9808
- className: cn(isSelected ? "text-[var(--ds-tree-active-muted)]" : holdsSelection ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)]", "transition-transform", isExpanded && "rotate-90")
10008
+ className: cn(isSelected ? "text-[var(--ds-tree-active-muted)]" : holdsSelection2 ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)]", "transition-transform", isExpanded && "rotate-90")
9809
10009
  }
9810
10010
  )
9811
10011
  }
@@ -9821,7 +10021,7 @@ function TreeNodeRow({
9821
10021
  }
9822
10022
  }
9823
10023
  ),
9824
- node.icon && /* @__PURE__ */ jsx(Icon, { name: node.icon, size: 16, className: cn("shrink-0", isSelected ? "text-[var(--ds-tree-active-text)]" : holdsSelection ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)]") }),
10024
+ node.icon && /* @__PURE__ */ jsx(Icon, { name: node.icon, size: 16, className: cn("shrink-0", isSelected ? "text-[var(--ds-tree-active-text)]" : holdsSelection2 ? "text-[var(--ds-accent-ink)]" : "text-[var(--ds-text-muted)]") }),
9825
10025
  /* @__PURE__ */ jsx(TruncLabel2, { children: node.label })
9826
10026
  ]
9827
10027
  }
@@ -10795,9 +10995,12 @@ var StackedList = forwardRef(function StackedList2({
10795
10995
  pager
10796
10996
  }, ref) {
10797
10997
  useScrollsInside();
10998
+ useRunsEdgeToEdge();
10798
10999
  const isPhone = useBreakpoint("sm");
10799
11000
  const inCard = useContext(CardSurfaceCtx);
10800
- const inPane = !!useContext(SplitPaneCtx);
11001
+ const pane = useContext(SplitPaneCtx);
11002
+ const panePadded = useContext(PanePaddedCtx);
11003
+ const inPane = !!pane && !panePadded;
10801
11004
  const articles = variant === "articles";
10802
11005
  useWholeListCheck("StackedList", items.length, pager);
10803
11006
  const shown = pagerPart(items, pager);
@@ -12056,6 +12259,7 @@ function Gantt({
12056
12259
  className
12057
12260
  }) {
12058
12261
  useScrollsInside();
12262
+ useRunsEdgeToEdge();
12059
12263
  const [zoomState, setZoomState] = useState(defaultZoom);
12060
12264
  const zoom = zoomProp ?? zoomState;
12061
12265
  const changeZoom = (next) => {
@@ -13362,7 +13566,8 @@ function ScrollArea({
13362
13566
  orientation = "vertical",
13363
13567
  maxHeight,
13364
13568
  maxWidth,
13365
- "aria-label": ariaLabel = "Scrollable content"
13569
+ "aria-label": ariaLabel = "Scrollable content",
13570
+ flush = false
13366
13571
  }) {
13367
13572
  const viewportRef = useRef(null);
13368
13573
  const [viewport, setViewport] = useState(null);
@@ -13463,6 +13668,8 @@ function ScrollArea({
13463
13668
  className: cn(
13464
13669
  // the frame's corners, so the inset ring follows them rather than being cut by them (as the Table's region)
13465
13670
  "h-full w-full scroll-p-[4px] rounded-[inherit]",
13671
+ // room for an outline at its edges (FOCUS_ROOM), unless the content insets itself
13672
+ !flush && "p-[4px]",
13466
13673
  FOCUS_INSET,
13467
13674
  orientation === "vertical" && "overflow-y-auto overflow-x-hidden",
13468
13675
  orientation === "horizontal" && "overflow-x-auto overflow-y-hidden",
@@ -13612,7 +13819,7 @@ var Sidebar = forwardRef(function Sidebar2({
13612
13819
  children: header
13613
13820
  }
13614
13821
  ),
13615
- /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children }),
13822
+ /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", flush: true, children }),
13616
13823
  showCollapse && (() => {
13617
13824
  const isSm = navSize === "sm";
13618
13825
  const collapseButton = /* @__PURE__ */ jsxs(
@@ -14520,6 +14727,16 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
14520
14727
  clearTimers.current.add(timer);
14521
14728
  }, 0);
14522
14729
  }, []);
14730
+ const barRooms = useRef(/* @__PURE__ */ new Map());
14731
+ const [barRoom, setBarRoom] = useState(0);
14732
+ const bulkBar = useMemo(() => ({
14733
+ room: (bar, room) => {
14734
+ if (room > 0) barRooms.current.set(bar, room);
14735
+ else barRooms.current.delete(bar);
14736
+ setBarRoom(Math.max(0, ...barRooms.current.values()));
14737
+ },
14738
+ say: (text) => announce({ id: "bulk-bar", message: text, variant: "default", duration: 0, shown: false, version: 0 })
14739
+ }), [announce]);
14523
14740
  useEffect(() => () => {
14524
14741
  if (heardTimer.current) clearTimeout(heardTimer.current);
14525
14742
  heardTimer.current = void 0;
@@ -14707,8 +14924,10 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
14707
14924
  const waiting = list2.length - shown.length;
14708
14925
  const isTop = position.startsWith("top");
14709
14926
  const hasBottomNav = isMobile && bottomNavs > 0;
14927
+ const navFoot = "var(--ds-space-12) + var(--ds-space-2) + env(safe-area-inset-bottom)";
14928
+ const stackBottom = barRoom > 0 && (isMobile || !isTop) ? hasBottomNav ? `calc(${navFoot} + ${barRoom}px)` : isMobile ? `calc(env(safe-area-inset-bottom) + ${barRoom}px)` : `max(var(${TOAST_INSET_VARS[position]}), calc(env(safe-area-inset-bottom) + ${barRoom}px))` : hasBottomNav ? `calc(${navFoot} + var(--ds-space-4))` : void 0;
14710
14929
  return /* @__PURE__ */ jsx(ToastContext.Provider, { value: api, children: /* @__PURE__ */ jsxs(ToastLedgerCtx.Provider, { value: ledger, children: [
14711
- /* @__PURE__ */ jsx(BottomNavPresenceCtx.Provider, { value: registerBottomNav, children }),
14930
+ /* @__PURE__ */ jsx(BottomNavPresenceCtx.Provider, { value: registerBottomNav, children: /* @__PURE__ */ jsx(BulkBarPresenceCtx, { value: bulkBar, children }) }),
14712
14931
  /* @__PURE__ */ jsx("div", { className: "sr-only", "aria-live": "polite", "data-toast-announcer": "polite", children: heard.filter((h) => !h.assertive).map((h) => /* @__PURE__ */ jsx("p", { children: h.text }, h.id)) }),
14713
14932
  /* @__PURE__ */ jsx("div", { className: "sr-only", "aria-live": "assertive", "data-toast-announcer": "assertive", children: heard.filter((h) => h.assertive).map((h) => /* @__PURE__ */ jsx("p", { children: h.text }, h.id)) }),
14714
14933
  shown.length > 0 && createPortal(
@@ -14740,7 +14959,7 @@ function ToastProvider({ children, maxVisible = 3, position = "bottom-right", du
14740
14959
  isTop ? "flex-col" : "flex-col-reverse",
14741
14960
  isMobile ? cn("left-ds-4 right-ds-4", !hasBottomNav && "bottom-ds-4") : TOAST_POSITION_CLASSES[position]
14742
14961
  ),
14743
- style: hasBottomNav ? { bottom: "calc(var(--ds-space-12) + var(--ds-space-2) + var(--ds-space-4) + env(safe-area-inset-bottom))" } : void 0,
14962
+ style: stackBottom ? { bottom: stackBottom } : void 0,
14744
14963
  children: [
14745
14964
  shown.map((t) => /* @__PURE__ */ jsx("div", { "data-toast-id": t.id, className: "pointer-events-auto", children: /* @__PURE__ */ jsx(
14746
14965
  Toast,
@@ -15186,6 +15405,7 @@ var PageContainer = forwardRef(function PageContainer2({
15186
15405
  {
15187
15406
  ref: setRoot,
15188
15407
  "data-page-container": wrappedHeader ? "" : void 0,
15408
+ "data-page-column": "",
15189
15409
  "data-width": dataWidth,
15190
15410
  className: cn(
15191
15411
  "w-full",
@@ -15217,7 +15437,7 @@ var PageContainer = forwardRef(function PageContainer2({
15217
15437
  ...floatingHeader ? { paddingRight: "var(--ds-space-3)", paddingLeft: r.sidebarFloating ? 0 : "var(--ds-space-3)" } : {},
15218
15438
  ...overlapActive ? { marginTop: "calc(-1 * var(--ds-overlap, 0px))", position: "relative", zIndex: 1 } : {}
15219
15439
  } : void 0;
15220
- const column = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement: "inside", containedBreadcrumbs: false, breadcrumbsInsideHeader: false, noBottomMargin: false, overlap: false, setPinnedHeight, reportHeader, requestContentWidth, insidePaddedColumn: true }, children: /* @__PURE__ */ jsx("div", { ref: columnRef, className: cn(columnClass(contentPadding), bottomPaddingClasses[vPad], className), style: { ...columnStyle, ...launcherPadding }, children: capped(rest) }) });
15440
+ const column = /* @__PURE__ */ jsx(PageContainerContext, { value: { contentMaxWidth: maxWidthValue, placement: "inside", containedBreadcrumbs: false, breadcrumbsInsideHeader: false, noBottomMargin: false, overlap: false, setPinnedHeight, reportHeader, requestContentWidth, insidePaddedColumn: true }, children: /* @__PURE__ */ jsx("div", { ref: columnRef, "data-page-column": "", className: cn(columnClass(contentPadding), bottomPaddingClasses[vPad], className), style: { ...columnStyle, ...launcherPadding }, children: capped(rest) }) });
15221
15441
  return /* @__PURE__ */ jsx(PageActionsCtx, { value: pageActions, children: /* @__PURE__ */ jsxs("div", { ref: setRoot, "data-page-container": "", "data-width": dataWidth, className: "w-full", children: [
15222
15442
  placement === "block" ? /* @__PURE__ */ jsx("div", { style: floatingBlockPadding(r.navbarFloating, r.sidebarFloating, overlapActive), children: /* @__PURE__ */ jsxs("div", { className: FLOATING_BLOCK, children: [
15223
15443
  strip,
@@ -18304,7 +18524,10 @@ var SectionHeading = forwardRef(function SectionHeading2({
18304
18524
  }, ref) {
18305
18525
  const isPhone = useBreakpoint("sm");
18306
18526
  const pane = variant === "pane";
18307
- const inDetail = useContext(SplitPaneCtx) === "detail";
18527
+ const paneKind = useContext(SplitPaneCtx);
18528
+ const inDetail = paneKind === "detail";
18529
+ const panePadded = useContext(PanePaddedCtx);
18530
+ const cancel = pane && panePadded && (paneKind === "list" || paneKind === "detail") ? PANE_HEADING_CANCEL[paneKind] : void 0;
18308
18531
  const editable = !!onTitleChange;
18309
18532
  return /* @__PURE__ */ jsxs(
18310
18533
  "div",
@@ -18313,7 +18536,7 @@ var SectionHeading = forwardRef(function SectionHeading2({
18313
18536
  "data-gs": "section-heading",
18314
18537
  "data-variant": variant,
18315
18538
  className: cn(
18316
- pane ? cn("shrink-0 pt-ds-4 pb-ds-3 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", inDetail ? "px-[var(--ds-page-gutter)]" : "px-ds-4") : "mb-[var(--ds-heading-gap)]",
18539
+ pane ? cn("shrink-0 pt-ds-4 pb-ds-3 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]", inDetail ? "px-[var(--ds-page-gutter)]" : "px-ds-4", cancel) : "mb-[var(--ds-heading-gap)]",
18317
18540
  !pane && bordered && "border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] pb-ds-4",
18318
18541
  className
18319
18542
  ),