@box-kite/react 2.2.4 → 2.2.6

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 (167) hide show
  1. package/.claude/rules/box-kite-rules.md +11 -1
  2. package/.claude/skills/box-kite/SKILL.md +11 -1
  3. package/.claude/skills/box-kite/references/components.md +83 -3
  4. package/.cursor/rules/box-kite.mdc +11 -1
  5. package/AGENTS.md +11 -1
  6. package/BOX_AI_CONTEXT.md +371 -25
  7. package/BOX_KITE_AI_CONTEXT.md +371 -25
  8. package/behavior.cjs +1 -1
  9. package/behavior.mjs +57 -57
  10. package/box.cjs +1 -1
  11. package/box.d.ts +5 -0
  12. package/box.mjs +15 -15
  13. package/catalog.cjs +1 -1
  14. package/catalog.mjs +906 -0
  15. package/client.cjs +1 -1
  16. package/client.mjs +94 -80
  17. package/components/calendar.cjs +1 -0
  18. package/components/calendar.d.ts +77 -0
  19. package/components/calendar.mjs +483 -0
  20. package/components/dashboard.cjs +1 -1
  21. package/components/dashboard.mjs +1 -1
  22. package/components/dataGrid/components/dataGridBooleanValue.d.ts +11 -0
  23. package/components/dataGrid/components/dataGridDateParts.d.ts +40 -0
  24. package/components/dataGrid/components/dataGridDateValue.d.ts +14 -0
  25. package/components/dataGrid/components/dataGridList.d.ts +12 -0
  26. package/components/dataGrid/components/dataGridListAggregate.d.ts +7 -0
  27. package/components/dataGrid/components/dataGridListFilter.d.ts +7 -0
  28. package/components/dataGrid/components/dataGridListFilterSheet.d.ts +11 -0
  29. package/components/dataGrid/components/dataGridListFooter.d.ts +13 -0
  30. package/components/dataGrid/components/dataGridListGroup.d.ts +13 -0
  31. package/components/dataGrid/components/dataGridListItem.d.ts +21 -0
  32. package/components/dataGrid/components/dataGridListLayout.d.ts +20 -0
  33. package/components/dataGrid/components/dataGridListRecord.d.ts +11 -0
  34. package/components/dataGrid/components/dataGridListSelectionBar.d.ts +11 -0
  35. package/components/dataGrid/components/dataGridListSheet.d.ts +30 -0
  36. package/components/dataGrid/components/dataGridListSortSheet.d.ts +10 -0
  37. package/components/dataGrid/components/dataGridListToolbar.d.ts +10 -0
  38. package/components/dataGrid/components/dataGridListValue.d.ts +12 -0
  39. package/components/dataGrid/contracts/dataGridContract.d.ts +70 -11
  40. package/components/dataGrid/models/columnModel.d.ts +3 -1
  41. package/components/dataGrid/models/editModel.d.ts +3 -1
  42. package/components/dataGrid/models/gridModel.d.ts +3 -0
  43. package/components/dataGrid/models/listModel.d.ts +129 -0
  44. package/components/dataGrid/models/localeText.d.ts +36 -0
  45. package/components/dataGrid/models/viewportModel.d.ts +0 -2
  46. package/components/dataGrid/useDateParts.d.ts +17 -0
  47. package/components/dataGrid/useListLayout.d.ts +13 -0
  48. package/components/dataGrid.cjs +3 -3
  49. package/components/dataGrid.mjs +1251 -854
  50. package/components/dateField.cjs +1 -0
  51. package/components/dateField.d.ts +78 -0
  52. package/components/dateField.mjs +313 -0
  53. package/components/datePicker.cjs +1 -0
  54. package/components/datePicker.d.ts +120 -0
  55. package/components/datePicker.mjs +529 -0
  56. package/components/dateTime.cjs +1 -0
  57. package/components/dateTime.d.ts +23 -0
  58. package/components/dateTime.mjs +34 -0
  59. package/components/dialog.cjs +1 -1
  60. package/components/dialog.mjs +29 -29
  61. package/components/list/listItemParts.d.ts +9 -0
  62. package/components/list.cjs +1 -0
  63. package/components/list.d.ts +118 -0
  64. package/components/list.mjs +391 -0
  65. package/components/menu.cjs +1 -1
  66. package/components/menu.mjs +25 -25
  67. package/components/overlay.cjs +1 -1
  68. package/components/overlay.mjs +12 -12
  69. package/components/semantics.d.ts +29 -29
  70. package/components/slider.cjs +1 -1
  71. package/components/slider.mjs +1 -1
  72. package/components/sortableList.cjs +1 -0
  73. package/components/sortableList.d.ts +49 -0
  74. package/components/sortableList.mjs +384 -0
  75. package/components/swipeItem.cjs +1 -0
  76. package/components/swipeItem.d.ts +52 -0
  77. package/components/swipeItem.mjs +321 -0
  78. package/components/tabs.cjs +1 -1
  79. package/components/tabs.mjs +1 -1
  80. package/components/timeZoneSelect.cjs +1 -0
  81. package/components/timeZoneSelect.d.ts +52 -0
  82. package/components/timeZoneSelect.mjs +74 -0
  83. package/components/toaster.cjs +1 -1
  84. package/components/toaster.mjs +24 -24
  85. package/components/virtualList.cjs +1 -0
  86. package/components/virtualList.d.ts +32 -0
  87. package/components/virtualList.mjs +188 -0
  88. package/dataGridDateParts.cjs +1 -0
  89. package/dataGridDateParts.mjs +223 -0
  90. package/dataGridList.cjs +1 -0
  91. package/dataGridList.mjs +1314 -0
  92. package/dataGridListLayout.cjs +1 -0
  93. package/dataGridListLayout.mjs +49 -0
  94. package/date-environment.cjs +1 -0
  95. package/date-environment.mjs +29 -0
  96. package/date-input.cjs +1 -0
  97. package/date-input.mjs +346 -0
  98. package/date-math.cjs +1 -0
  99. package/date-math.mjs +317 -0
  100. package/date.cjs +1 -0
  101. package/date.d.ts +40 -0
  102. package/date.mjs +4 -0
  103. package/dates.cjs +1 -0
  104. package/dates.mjs +689 -0
  105. package/docs/components.md +10 -1
  106. package/icons/closeIcon.d.ts +2 -0
  107. package/icons/filterIcon.d.ts +2 -0
  108. package/layer.cjs +1 -1
  109. package/layer.mjs +1 -1
  110. package/mcp/src/data.d.ts +2 -0
  111. package/package.json +178 -22
  112. package/placement.cjs +1 -1
  113. package/placement.mjs +10 -10
  114. package/platform.cjs +1 -1
  115. package/platform.mjs +4 -1
  116. package/react/a11y/useControllableState.d.ts +2 -1
  117. package/react/boxComponent.d.ts +1 -1
  118. package/react/componentsContext.d.ts +4 -0
  119. package/react/componentsProvider.d.ts +10 -0
  120. package/react/date/dateChange.d.ts +18 -0
  121. package/react/date/dateEnvironment.d.ts +34 -0
  122. package/react/resolveStyles.d.ts +2 -1
  123. package/react/theme/theme.d.ts +6 -1
  124. package/react-shared.cjs +1 -1
  125. package/react-shared.mjs +6 -6
  126. package/registry/blocks/dashboard-shell/dashboard-shell.d.ts +3 -1
  127. package/registry/designs/store/address-form.d.ts +11 -0
  128. package/registry/designs/store/cart-items.d.ts +10 -0
  129. package/registry/designs/store/cart-page.d.ts +4 -0
  130. package/registry/designs/store/checkout-page.d.ts +7 -0
  131. package/registry/designs/store/credit-card-form.d.ts +10 -0
  132. package/registry/designs/store/form-field.d.ts +19 -0
  133. package/registry/designs/store/order-summary.d.ts +18 -0
  134. package/registry/designs/store/product-art.d.ts +26 -0
  135. package/registry/designs/store/product-card.d.ts +11 -0
  136. package/registry/designs/store/product-gallery.d.ts +11 -0
  137. package/registry/designs/store/product-grid.d.ts +10 -0
  138. package/registry/designs/store/product-page.d.ts +5 -0
  139. package/registry/designs/store/quantity-stepper.d.ts +13 -0
  140. package/registry/designs/store/shipping-method.d.ts +14 -0
  141. package/registry/designs/store/store-data.d.ts +53 -0
  142. package/registry/designs/store/store-design.d.ts +63 -0
  143. package/registry/designs/store/store-shell.d.ts +12 -0
  144. package/registry/designs/store/storefront-page.d.ts +4 -0
  145. package/registry/designs/store/variant-picker.d.ts +15 -0
  146. package/rsc.cjs +1 -1
  147. package/rsc.d.ts +2 -0
  148. package/rsc.mjs +1 -1
  149. package/scrolling.cjs +1 -0
  150. package/scrolling.mjs +22 -0
  151. package/utils/date/calendar.d.ts +44 -0
  152. package/utils/date/calendarModel.d.ts +46 -0
  153. package/utils/date/dateTime.d.ts +144 -0
  154. package/utils/date/fieldModel.d.ts +74 -0
  155. package/utils/date/format.d.ts +39 -0
  156. package/utils/date/iso.d.ts +40 -0
  157. package/utils/date/locale.d.ts +17 -0
  158. package/utils/date/parse.d.ts +20 -0
  159. package/utils/date/settings.d.ts +26 -0
  160. package/utils/date/value.d.ts +43 -0
  161. package/utils/date/zoneOptions.d.ts +19 -0
  162. package/utils/date/zones.d.ts +32 -0
  163. package/utils/scroll/scrollUtils.d.ts +6 -0
  164. package/utils/sortable/sortableUtils.d.ts +25 -0
  165. package/utils/swipe/swipeUtils.d.ts +53 -0
  166. package/utils/virtual/measuredWindow.d.ts +28 -0
  167. package/utils/virtual/rowSearch.d.ts +11 -0
@@ -1064,6 +1064,36 @@ Box.components({
1064
1064
  });
1065
1065
  ```
1066
1066
 
1067
+ `extends` is resolved over everything registered so far, chains included, so overriding the base later still reaches the component built on it.
1068
+
1069
+ **A prop on the element wins in every theme.** `<Box component="card" bgColor="red-500">` replaces the card's `bgColor` and also its `theme.dark.bgColor` (at the same level: `hover.bgColor` replaces the themed hover). Everything else the card styles for dark stays. To keep a different dark value, write it: `theme={{ dark: { bgColor: 'red-900' } }}`.
1070
+
1071
+ ### Box.createComponents() — A Design for One Subtree
1072
+
1073
+ `Box.components()` styles the whole app: every call merges into one registry, so two designs cannot share a page through it. `Box.createComponents()` takes the same shape and registers nothing. It returns a design, and `<Box.Components value>`, or `components` on `Box.Theme`, applies it to everything inside.
1074
+
1075
+ ```tsx
1076
+ const aurora = Box.createComponents({ button: { styles: { borderRadius: 999 } }, card: { styles: { p: 6 } } });
1077
+ const harbor = Box.createComponents({ button: { styles: { borderRadius: 0 } } }, { base: 'builtin' });
1078
+
1079
+ <Box.Theme use="local" components={aurora}> {/* light/dark + design in one wrapper */}
1080
+ <Button>Pill</Button>
1081
+ </Box.Theme>
1082
+ <Box.Components value={harbor}>
1083
+ <Button>Square</Button>
1084
+ </Box.Components>
1085
+ ```
1086
+
1087
+ - **`base`** is what the design is merged over:
1088
+ - `'global'` (the default) is the app's own `Box.components()`, followed as it changes.
1089
+ - `'builtin'` ignores the app's registrations.
1090
+ - Another registry chains onto that design.
1091
+ - **Props still beat the design**, the way they beat `Box.components()`.
1092
+ - **A design is identified by its content**, so building one inline does not grow the cache. Module scope still reads best.
1093
+ - **`<Box.Components>` renders no element.** `value={undefined}` hands a subtree back to the app's design.
1094
+ - **A Server Component cannot read the provider.** A client component under it can.
1095
+ - **Types for two designs that both name a component**: `interface ComponentsTypes extends MergeComponentsAndVariants<[typeof aurora, typeof harbor]> {}` (from `@box-kite/core/types`). Extending two `ExtractComponentsAndVariants` that disagree on `button` is an interface conflict.
1096
+
1067
1097
  ---
1068
1098
 
1069
1099
  ## Extension System
@@ -1891,6 +1921,170 @@ on something at the panel's edge needs, since the clip is permanent.
1891
1921
 
1892
1922
  ---
1893
1923
 
1924
+ ## List (`components/list`)
1925
+
1926
+ ```tsx
1927
+ import List from '@box-kite/react/components/list';
1928
+
1929
+ <List>
1930
+ {people.map((person) => (
1931
+ <List.Item key={person.id} chevron>
1932
+ <List.Media variant="avatar">{person.initials}</List.Media>
1933
+ <List.Content>
1934
+ <List.Row>
1935
+ <List.Title lines={1}>
1936
+ <List.Action props={{ href: `/people/${person.id}` }}>{person.name}</List.Action>
1937
+ </List.Title>
1938
+ <List.Meta>{person.lastSeen}</List.Meta>
1939
+ </List.Row>
1940
+ <List.Description lines={2}>{person.bio}</List.Description>
1941
+ </List.Content>
1942
+ </List.Item>
1943
+ ))}
1944
+ </List>;
1945
+ ```
1946
+
1947
+ A list of things to read and pick from — messages, orders, people, settings. **An item is a flex row of
1948
+ three slots**, and the content is a column of rows:
1949
+
1950
+ | Part | Element | What it is |
1951
+ | ------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------------------- |
1952
+ | `List` | `<ul role="list">` | `variant`: dividers by default, `plain`, `card` (one framed block), `separated` (a frame per item); `tag="ol"` |
1953
+ | `List.Item` | `<li>` | A flex row. `size` `sm`/`md`/`lg` (44/56/72px), `wrap`, `selected` (a tint), `chevron` |
1954
+ | `List.Media` | `<div>` | Leading: `variant` `icon` (a tinted square), `avatar` (a circle that clips), `image` (a thumbnail) |
1955
+ | `List.Content` | `<div>` | The middle, taking the room; a column |
1956
+ | `List.Row` | `<div>` | Parts side by side, the last at the end. `secondary` (smaller, muted), `wrap` |
1957
+ | `List.Overline` | `<span>` | A small line above the title. `lines` |
1958
+ | `List.Title` | `<span>` | The name, holding the `List.Action`. `lines` `1` (ellipsis), `2`/`3` (clamp); unset, it wraps |
1959
+ | `List.Description` | `<span>` | Muted supporting text. `lines` |
1960
+ | `List.Meta` | `<span>` | A small value that never wraps: a time, a count |
1961
+ | `List.End` | `<div>` | Trailing: a value over its label, aligned to the end in tabular figures |
1962
+ | `List.Actions` | `<div>` | Controls of the item's own, lifted above the action |
1963
+ | `List.Action` | `<a>` / `<label>` / `<button>` | The primary action, covering the item: `href` → link, `htmlFor` → label, otherwise a button |
1964
+ | `List.Fields` | `<dl>` | Labelled values, as many to a line as fit at 8rem |
1965
+ | `List.Field` | `<div>` + `<dt>` + `<dd>` | `label` and the value |
1966
+ | `List.Group` | `<li>` + `<ul>` | A section: `label`, `level` (makes the label a heading), `sticky` |
1967
+ | `List.Chevron` | `<span aria-hidden>` | The chevron `chevron` draws, for an item something else renders |
1968
+
1969
+ **One action covers the item.** `List.Action`'s `::after` stretches over the whole `<li>`, so a press
1970
+ anywhere on the item is a press on it and the focus ring draws round the item — while its accessible
1971
+ name is the title alone, not every value in the row. A pick-list is a label: put the checkbox in
1972
+ `List.Actions` and give the action its `htmlFor`.
1973
+
1974
+ ```tsx
1975
+ <List.Item selected={picked}>
1976
+ <List.Actions>
1977
+ <Checkbox id="pick-ada" checked={picked} onChange={toggle} />
1978
+ </List.Actions>
1979
+ <List.Title>
1980
+ <List.Action props={{ htmlFor: 'pick-ada' }}>Ada Lovelace</List.Action>
1981
+ </List.Title>
1982
+ </List.Item>
1983
+ ```
1984
+
1985
+ Hover, pressed, current (`aria-current` on the action) and disabled are read off the action with
1986
+ `:has()`, so an item with no action is plain text and needs no prop to say so.
1987
+
1988
+ **It renders on a server**: no hook, no context, no effect. That is why the list's look reaches its
1989
+ items as custom properties rather than through React — and they are yours to set with `vars`:
1990
+
1991
+ | Variable | Default | What it is |
1992
+ | ---------------------- | --------------------- | ------------------------------------------------------------- |
1993
+ | `--list-divider` | `currentColor` at 12% | The line between items; `plain` and `separated` make it clear |
1994
+ | `--list-divider-inset` | `0px` | Where the line starts — `4.5rem` lines it up with the text |
1995
+ | `--list-item-frame` | `none` | The border round each item, which `separated` sets |
1996
+ | `--list-item-radius` | `0` | Each item's corners, which its tint follows |
1997
+ | `--list-sticky-top` | `0` | How far down a sticky group label stops — an app's header |
1998
+
1999
+ **The style tree is paint only** — `list` (the three variants), `list.item` (the divider colour, the
2000
+ states, `selected`), `list.media`, `list.muted`, `list.action` (the focus ring) and `list.label` —
2001
+ because every engine entry carries it. The spacing is each part's default props, so a prop on the part
2002
+ beats it (`<List.Item py={1}>`) and `Box.components` cannot move it. The hairlines and tints are mixed
2003
+ from `currentColor`, so they follow any theme with no rule for it.
2004
+
2005
+ It is a `<ul role="list">` — the role spelled out because Safari stops reporting a list whose items have
2006
+ lost their markers — and **not a composite widget**: each action is a tab stop, Enter follows or presses
2007
+ it, and the arrow keys stay the page's. Arrow-key selection among options is a listbox: `Combobox` or
2008
+ `Select`.
2009
+
2010
+ ### Swipe actions, reorder and a long list
2011
+
2012
+ Three client-only entries on top of `List`, so `List` itself stays server-safe. Each item they draw is a
2013
+ `List.Item` in everything but name — same parts, same sizes, same `list.item` paint.
2014
+
2015
+ ```tsx
2016
+ import SwipeItem from '@box-kite/react/components/swipeItem';
2017
+ import SortableList from '@box-kite/react/components/sortableList';
2018
+ import VirtualList from '@box-kite/react/components/virtualList';
2019
+
2020
+ <SwipeItem
2021
+ fullSwipe
2022
+ startActions={
2023
+ <SwipeItem.Action variant="accent" onClick={pin}>
2024
+ Pin
2025
+ </SwipeItem.Action>
2026
+ }
2027
+ endActions={
2028
+ <SwipeItem.Action variant="danger" onClick={remove}>
2029
+ Delete
2030
+ </SwipeItem.Action>
2031
+ }
2032
+ >
2033
+ <List.Content>…</List.Content>
2034
+ </SwipeItem>;
2035
+
2036
+ <SortableList onOrderChange={(order) => setIds(order)} onOrderCommit={save}>
2037
+ {ids.map((id) => (
2038
+ <SortableList.Item key={id} value={id} label={names[id]}>
2039
+ <List.Content>…</List.Content>
2040
+ <SortableList.Handle />
2041
+ </SortableList.Item>
2042
+ ))}
2043
+ </SortableList>;
2044
+
2045
+ <VirtualList items={messages} itemKey={(message) => message.id} onRangeChange={loadMore}>
2046
+ {(message) => <List.Item>…</List.Item>}
2047
+ </VirtualList>;
2048
+ ```
2049
+
2050
+ | Component | Props |
2051
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2052
+ | `SwipeItem` | `startActions`, `endActions` (the first of each is outermost), `fullSwipe`, `open`/`defaultOpen`/`onOpenChange`, and `List.Item`'s `size`/`wrap`/`selected`/`chevron` |
2053
+ | `SwipeItem.Action` | A `<button>`: `variant` `accent`/`warning`/`danger` (unset is neutral), `icon`, `onClick`, `closeOnSelect` (default `true`) |
2054
+ | `SortableList` | Everything `List` takes, `onOrderChange(order, { reason, value, from, to })`, `onOrderCommit` (the drop — the one to save) |
2055
+ | `SortableList.Item` | Everything `List.Item` takes, `value` (required, unique), `label` (what the handle and announcements call it), `disabled` |
2056
+ | `SortableList.Handle` | The `<button>` a drag starts from; six dots unless given children; named "Move" + the item's `label` |
2057
+ | `VirtualList` | Everything `List` takes, `items`, `itemKey`, `children` (a function returning one `<li>`), `estimateSize` (57), `overscan` (6), `initialCount` (20), `onRangeChange`, `scrollerRef` |
2058
+
2059
+ **`SwipeItem`'s row slides and a tray opens in the space it leaves** — nothing is under the row, so it
2060
+ needs no background. Past half open opens, short of it closes, a flick goes the way it was going, and
2061
+ `fullSwipe` carried most of the way across performs the outermost action (it clicks it, so its `onClick`
2062
+ is the one place the command lives). A press on an open item closes it instead of following the action.
2063
+ The actions are real buttons in the tab order and focus reaching one opens its tray; Escape closes it.
2064
+ A swipe is a path gesture (WCAG 2.5.1), so put the same commands somewhere a press reaches too.
2065
+
2066
+ **`SortableList`: the app owns the order.** During a drag nothing is reordered — the item follows the
2067
+ pointer, its neighbours step aside as inline translates — and `onOrderChange` fires once, on the drop,
2068
+ which then glides into place (the Web Animations API, from where it was drawn). The keyboard is a grab,
2069
+ DashboardGrid's model: Enter/Space picks up, ArrowUp/ArrowDown/Home/End move (reporting each step),
2070
+ Enter drops, Escape puts back. A press on a handle that does not drag also picks the item up, and a
2071
+ press on another item then drops it there — the single-pointer way WCAG 2.5.7 asks for. Render the
2072
+ items in the order you hold; a refused move is just not applied.
2073
+
2074
+ **`VirtualList` measures each item it draws** and keeps the height by `itemKey`, so mixed heights work;
2075
+ an undrawn item counts as the mean of those drawn (`estimateSize` before any is), and the list holds its
2076
+ height while it scrolls, since a scrollbar drag loses a move whenever it changes. The scroller is the nearest ancestor that scrolls, else the
2077
+ page; `scrollerRef` overrides. Each item gets `aria-setsize`/`aria-posinset`, the item focus is in
2078
+ stays rendered, and an item above the screen that turns out taller than estimated scrolls the list by
2079
+ the difference so nothing on screen moves. Groups and sticky labels need the whole list and do not
2080
+ window; find-in-page cannot see an unrendered item.
2081
+
2082
+ Styling: `swipeItem.row`/`swipeItem.tray` (`dragging` variants), `swipeItem.action` (the three colour
2083
+ variants, `armed`), `sortableList.handle` (`grabbed`), and on `list.item` the `lifted` (dragged),
2084
+ `grabbed` (keyboard) and `settling` variants. `List.Chevron` is the item's chevron as a part.
2085
+
2086
+ ---
2087
+
1894
2088
  ## Slider & Progress
1895
2089
 
1896
2090
  ```tsx
@@ -1995,6 +2189,94 @@ Box.components({
1995
2189
 
1996
2190
  ---
1997
2191
 
2192
+ ## Dates and times (`@box-kite/react/date` + the date components)
2193
+
2194
+ ```tsx
2195
+ import { format, toUtc, toLocal, toZone, toPlainDate, parse, add, today } from '@box-kite/react/date';
2196
+ import DateField from '@box-kite/react/components/dateField';
2197
+ import Calendar from '@box-kite/react/components/calendar';
2198
+ import DatePicker, { DateRangePicker } from '@box-kite/react/components/datePicker';
2199
+ import TimeZoneSelect from '@box-kite/react/components/timeZoneSelect';
2200
+ import DateTime, { DateProvider } from '@box-kite/react/components/dateTime';
2201
+
2202
+ toUtc('2026-10-19T14:30', { from: 'Europe/Chisinau' }); // '2026-10-19T11:30:00Z'
2203
+ toLocal('2026-10-19T11:30:00Z'); // the reader's wall clock, with its offset
2204
+ format('2026-10-19', { locale: 'ro-MD', dateStyle: 'long' }); // '19 octombrie 2026'
2205
+ parse('03/04/2026', { locale: 'en-GB' }); // '2026-04-03'
2206
+
2207
+ <DateField label="Due" name="due" />;
2208
+ <DatePicker type="datetime" zone="utc" label="Publish at" onValueChange={(value, { utc, local }) => schedule(utc)} />;
2209
+ <DateRangePicker label="Stay" startName="from" endName="to" />;
2210
+ <DateTime value={order.createdAt} />;
2211
+ ```
2212
+
2213
+ **Every value is an ISO string, and its shape is its kind** — never a `Date`, which is always a moment in
2214
+ the runtime's zone:
2215
+
2216
+ | Kind | Written as | Means |
2217
+ | ---------- | ------------------------------------------ | --------------------------------------------------------- |
2218
+ | `date` | `2026-10-19` | A day: no time, no zone, the same everywhere. |
2219
+ | `time` | `14:30:00` (`.sss` when there are any) | A time of day. |
2220
+ | `datetime` | `2026-10-19T14:30:00` | A wall clock with no zone — an alarm, a timetable. |
2221
+ | `instant` | `2026-10-19T14:30:00+03:00`, or `…Z` (UTC) | One moment, written as one zone sees it. Anything stored. |
2222
+
2223
+ A zone is `'local'` (the reader), `'utc'`, `'system'` (the runtime), an IANA name or a fixed offset.
2224
+ **`'local'` on a server** is `configureDates({ timeZone })` or the nearest `<DateProvider timeZone>`;
2225
+ `systemZone()` is always the server's own. The browser writes its zone into a cookie with
2226
+ `document.cookie = zoneCookie()`, and a server reads it back with `readZoneCookie(request.headers.get('cookie'))`.
2227
+
2228
+ | Function (`@box-kite/react/date`) | What it does |
2229
+ | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
2230
+ | `toUtc(v, { from })` / `toLocal(v)` / `toZone(v, zone)` | The moment in UTC / on the reader's clock / on another zone's, with offset. |
2231
+ | `toPlainDate` / `toPlainTime` / `toPlainDateTime` / `toInstant` | `(v, { zone })` — the day, time or wall clock a value shows in `zone`. |
2232
+ | `toDate` / `fromDate` / `toEpoch` | The bridge to an API that insists on a `Date`. |
2233
+ | `combine(date, time, { zone })` | A date and a time: a date-time, or with `zone` the instant. |
2234
+ | `today(zone)` / `now(zone)` / `currentTime(zone)` | In the reader's zone unless told. |
2235
+ | `format(v, { locale, zone, dateStyle, timeStyle, … })` | `Intl` options, never format tokens. `''` for a non-value. |
2236
+ | `formatRange` / `formatRelative` / `zoneName` | `Oct 19 – 23, 2026`, `3 minutes ago`, `EEST`. |
2237
+ | `parse(text, { locale, kind, zone })` | ISO, `Date`, epoch, HTTP date, or text in the locale's order. |
2238
+ | `add` / `subtract` / `startOf` / `endOf` / `difference` | Calendar units on the wall clock, clock units on the instant. |
2239
+ | `compare` / `isBefore` / `isAfter` / `isEqual` / `isSame` / `clamp` | Across kinds and zones. |
2240
+ | `details(v, { zone })` | Every reading at once: `utc`, `zoned`, `local`, `offset`, `date`, `epoch`. |
2241
+ | `weekInfo` / `hourCycleOf` / `monthNames` / `weekdayNames` | The locale, from `Intl`. |
2242
+ | `timeZones` / `offsetOf` / `isValidZone` / `normalizeZone` | The runtime's zones. |
2243
+ | `configureDates` / `localZone` / `systemZone` / `zoneCookie` / `readZoneCookie` | Who the reader is. |
2244
+
2245
+ **DST** follows Temporal: a wall-clock time a zone skips or repeats takes `disambiguation` —
2246
+ `'compatible'` (default: the later reading of a skipped time, the earlier of a repeated one), `'earlier'`,
2247
+ `'later'` or `'reject'` (`null`).
2248
+
2249
+ **The controls take and give the same string, and `onValueChange(value, details)` hands over the readings:**
2250
+ `details` is `{ reason, event, utc, local, zoned, date, epoch, zone, offset }`, all `null` while the value
2251
+ is. `reason` is `'keyboard'`, `'paste'`, `'select'`, `'preset'`, `'clear'` or `'reset'`.
2252
+
2253
+ | Component (`components/…`) | What it is |
2254
+ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2255
+ | `DateField` (`dateField`) | Segments (`role="spinbutton"`) in the locale's order. `type`, `zone`, `granularity`, `hourCycle`, `min`/`max`, `isDateUnavailable`, `name`, `required`, `label`. |
2256
+ | `Calendar` (`calendar`) | APG's grid keys. `mode="range"` → `{ start, end }`, `months`, `weekNumbers`, `min`/`max`, `isDateUnavailable`, `firstDayOfWeek`. |
2257
+ | `DatePicker` (`datePicker`) | A `DateField` with a calendar (`date`), a list of times every `step` minutes (`time`) or both (`datetime`) in the top layer; `presets`, `clearable`, `closeOnSelect`. |
2258
+ | `DateRangePicker` (`datePicker`) | Two fields over a range calendar; `startName`/`endName`, `months` (2), `presets` of `{ start, end }`. |
2259
+ | `TimeZoneSelect` | A `Combobox` over every zone, searchable by place, region or offset; the value is the IANA name. |
2260
+ | `DateTime` (`dateTime`) | `<time datetime>` in the reader's zone and language; `format`, `relative`, `zone`, `locale`. Also exports `DateProvider`. |
2261
+
2262
+ **A date-time's `zone`** is whose wall clock it is typed on: `'local'` (default), `'utc'`, an IANA name, or
2263
+ `'none'` (a wall clock with no zone, value `2026-10-19T14:30:00`). The value is the instant on that clock
2264
+ (`…+03:00`, or `…Z` for UTC), and a zone that is not the reader's is named after the time.
2265
+
2266
+ **Four things to know.** **Nothing typed is ever parsed**: `03/04/2026` is two days depending on who typed
2267
+ it, so each segment is a spin button and only a paste is read as text. **No hydration error, ever**: a
2268
+ server and a hydrating browser both render in the fallback (`UTC`/`en-US`, or a `DateProvider`'s), and the
2269
+ render after hydration switches to the reader — so pass the cookie's zone to a `DateProvider` for a first
2270
+ paint that is already right. **`name` posts the canonical string** (and `required` is a real input the
2271
+ browser validates), so a server reads `2026-10-19` whatever the reader's locale. And in a **DataGrid**, a
2272
+ column takes `editor: 'date' | 'time' | 'datetime'` (inferred from an ISO string or a `Date`),
2273
+ `dateFormat: 'date' | 'time' | 'datetime' | 'relative' | Intl options` and `filterable: { type: 'date' }`
2274
+ (`on`/`before`/`after`/`between`, each day a `DatePicker`); those parts are a chunk the grid fetches only when it shows a date.
2275
+ Styling: `dateField` (extends `textbox`; `segment`, `literal`, `zone`, `label`; `compact`, `invalid`,
2276
+ `disabled`), `calendar` (`nav`, `title`, `weekday`, `weekNumber`, `cell` with the range band, `day` with
2277
+ `today`/`selected`/`outside`/`blocked`/`unavailable`, `zoomItem`) and `datePicker` (`trigger`, `clear`,
2278
+ `presets`, `preset`, `times`, `time`, `part`); the panel is a `popover`.
2279
+
1998
2280
  ## Toaster (`toast()` + `<Toaster>`)
1999
2281
 
2000
2282
  ```tsx
@@ -2396,30 +2678,33 @@ named ones carry the reason.
2396
2678
  | `footer` | `boolean \| { label? }` | `false` | A pinned row of grand totals over every column with an `aggregate`. `label` replaces the default `Total` |
2397
2679
  | `export` | `boolean \| ExportConfig` | `false` | Export buttons in the top bar: `{ csv?, xlsx?, fileName? }`. The writers load on the first press |
2398
2680
  | `tabNavigation` | `'none' \| 'cells'` | `'none'` | What Tab does. `'none'` is APG's single tab stop; `'cells'` is the spreadsheet reading |
2681
+ | `layout` | `'auto' \| 'table' \| 'list'` | `'auto'` | A table, or a list once the grid's own width is under `list.breakpoint` (see "On a phone") |
2682
+ | `list` | `ListConfig` | — | `{ breakpoint?, fields?, batchSize?, recordView? }` — the list layout's settings |
2399
2683
  | `rangeSelection` | `boolean` | `false` | A drag or Shift+arrow marks a block of cells, and `Ctrl+C`/`Ctrl+V` copy and paste it as TSV (see below) |
2400
2684
 
2401
2685
  ### ColumnType
2402
2686
 
2403
- | Prop | Type | Default | Description |
2404
- | ------------------------ | --------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
2405
- | `key` | `Key` | required | Column identifier (maps to TRow property) |
2406
- | `header` | `string` | — | Header text |
2407
- | `width` | `number` | `200` | Base width in px |
2408
- | `align` | `'start' \| 'end' \| 'center' \| 'left' \| 'right'` | `'left'` | Cell alignment. `start`/`end` follow the reading order; `left`/`right` stay on the screen side they name |
2409
- | `pin` | `'START' \| 'END'` (also `'LEFT'`/`'RIGHT'`) | — | Pin to an edge of the **inline axis** (sticky on scroll), so it holds under either `dir`. `LEFT`/`RIGHT` are the older spelling of the same two |
2410
- | `columns` | `ColumnType[]` | — | Nested columns (grouped header) |
2411
- | `Cell` | `({ cell }) => ReactNode` | — | Custom renderer. `cell`: `{ value, row, column, grid }`. It redraws when its row changes or the grid does — not when another row is selected |
2412
- | `sortable` / `resizable` | `boolean` | inherits | Override grid-level setting |
2413
- | `flexible` | `boolean` | `true` | Participate in flex width distribution |
2414
- | `filterable` | `boolean \| FilterConfig` | — | `true` (text), `{ type: 'number', min?, max? }`, `{ type: 'multiselect', options? }` |
2415
- | `contextMenu` | `boolean \| ContextMenuConfig` | inherits | Override grid-level context menu. `false` hides entirely. `{ sort?, pin?, group? }` controls sections |
2416
- | `aggregate` | `AggregateName \| (values, rows) => value` | — | Total this column over each group row, and over the footer. See below |
2417
- | `AggregateCell` | `({ cell }) => ReactNode` | — | Renders the aggregate. `cell`: `{ value, column, rows, scope }`, `scope` being `'group'` or `'footer'` |
2418
- | `editable` | `boolean \| (row) => boolean` | inherits | Whether this column’s cells can be edited — every row, or the rows a predicate says |
2419
- | `editor` | `CellEditorType \| CellEditorConfig` | inferred | `text`/`number`/`checkbox`/`select`. Without one it is read off the value in the cell |
2420
- | `EditCell` | `({ cell }) => ReactNode` | — | An editor of your own, bound to `cell.draft` with `cell.commitEdit()`/`cell.cancelEdit()` |
2421
- | `exportValue` | `(row: TRow) => value` | the field | What this column writes to an exported file — an export runs no React, so a `Cell` renderer needs it |
2422
- | `exportFormat` | `string` | — | The number format its cells wear in a workbook, `'#,##0.00'` or `'yyyy-mm-dd'` |
2687
+ | Prop | Type | Default | Description |
2688
+ | ------------------------ | ------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
2689
+ | `key` | `Key` | required | Column identifier (maps to TRow property) |
2690
+ | `header` | `string` | — | Header text |
2691
+ | `width` | `number` | `200` | Base width in px |
2692
+ | `align` | `'start' \| 'end' \| 'center' \| 'left' \| 'right'` | `'left'` | Cell alignment. `start`/`end` follow the reading order; `left`/`right` stay on the screen side they name |
2693
+ | `pin` | `'START' \| 'END'` (also `'LEFT'`/`'RIGHT'`) | — | Pin to an edge of the **inline axis** (sticky on scroll), so it holds under either `dir`. `LEFT`/`RIGHT` are the older spelling of the same two |
2694
+ | `columns` | `ColumnType[]` | — | Nested columns (grouped header) |
2695
+ | `Cell` | `({ cell }) => ReactNode` | — | Custom renderer. `cell`: `{ value, row, column, grid }`. It redraws when its row changes or the grid does — not when another row is selected |
2696
+ | `sortable` / `resizable` | `boolean` | inherits | Override grid-level setting |
2697
+ | `flexible` | `boolean` | `true` | Participate in flex width distribution |
2698
+ | `filterable` | `boolean \| FilterConfig` | — | `true` (text), `{ type: 'number', min?, max? }`, `{ type: 'multiselect', options? }` |
2699
+ | `contextMenu` | `boolean \| ContextMenuConfig` | inherits | Override grid-level context menu. `false` hides entirely. `{ sort?, pin?, group? }` controls sections |
2700
+ | `aggregate` | `AggregateName \| (values, rows) => value` | — | Total this column over each group row, and over the footer. See below |
2701
+ | `AggregateCell` | `({ cell }) => ReactNode` | — | Renders the aggregate. `cell`: `{ value, column, rows, scope }`, `scope` being `'group'` or `'footer'` |
2702
+ | `editable` | `boolean \| (row) => boolean` | inherits | Whether this column’s cells can be edited — every row, or the rows a predicate says |
2703
+ | `editor` | `CellEditorType \| CellEditorConfig` | inferred | `text`/`number`/`checkbox`/`select`. Without one it is read off the value in the cell |
2704
+ | `EditCell` | `({ cell }) => ReactNode` | — | An editor of your own, bound to `cell.draft` with `cell.commitEdit()`/`cell.cancelEdit()` |
2705
+ | `exportValue` | `(row: TRow) => value` | the field | What this column writes to an exported file — an export runs no React, so a `Cell` renderer needs it |
2706
+ | `list` | `'title' \| 'kicker' \| 'subtitle' \| 'end' \| 'field' \| 'detail' \| 'hidden'` | inferred | Where the column goes in a list item, on a phone |
2707
+ | `exportFormat` | `string` | — | The number format its cells wear in a workbook, `'#,##0.00'` or `'yyyy-mm-dd'` |
2423
2708
 
2424
2709
  ### Aggregation
2425
2710
 
@@ -2599,11 +2884,13 @@ the same function — and an answer to an edit the user has since abandoned is d
2599
2884
  ```
2600
2885
 
2601
2886
  - **Which editor a cell opens is read off the value in it** — a number gets `number`, a boolean
2602
- `checkbox`, everything else `text` — unless `column.editor` names one (`text`/`number`/`checkbox`/
2603
- `select`) or configures it (`{ type, options?, placeholder?, step?, min?, max? }`; `options` may be a
2604
- function of the row). `column.EditCell` is a control of your own, bound to `cell.draft` with
2605
- `cell.setDraft(v)`, `cell.commitEdit()` and `cell.cancelEdit()` — the same three calls the built-in four
2606
- make.
2887
+ `checkbox` (drawn as a check or a cross, read out as `localeText.booleanTrue`/`booleanFalse`), an ISO
2888
+ date, time or date-time a `DateField` (an edit keeps the shape the cell held: `…Z` stays UTC, a `Date`
2889
+ a `Date`), everything else `text` — unless `column.editor` names one of the seven
2890
+ (`text`/`number`/`checkbox`/`select`/`date`/`time`/`datetime`) or configures it
2891
+ (`{ type, options?, placeholder?, step?, min?, max?, zone? }`; `options` may be a function of the row).
2892
+ `column.EditCell` is a control of your own, bound to `cell.draft` with `cell.setDraft(v)`,
2893
+ `cell.commitEdit()` and `cell.cancelEdit()` — the same three calls the built-in seven make.
2607
2894
  - **Pointer:** a press makes a cell the current one — it takes the ring, whether the pointer or the
2608
2895
  keyboard put it there — and a **double press opens the editor** on an editable cell. A cell that cannot
2609
2896
  be edited takes both as focus and nothing else, and a double press landing on a widget inside a cell (a
@@ -2715,6 +3002,65 @@ only gives it a bigger target.
2715
3002
  landed, so `range.values()` reads what the grid now shows. Everything it accepted lands in the same
2716
3003
  stream a typed value does: `onCellEditsChange(edits, { reason: 'paste' })`.
2717
3004
 
3005
+ ### On a phone: the list layout
3006
+
3007
+ Below `def.list.breakpoint` (576px, 36rem) of **the grid's own width** — measured with a `ResizeObserver`, not read off the viewport — the grid draws each row as a list item instead of a table row. Same rows, filters, sort, selection, grouping, tree and edits; a different drawing. `layout: 'table'` or `layout: 'list'` chooses one for good.
3008
+
3009
+ ```tsx
3010
+ <DataGrid
3011
+ data={orders}
3012
+ def={{
3013
+ title: 'Orders',
3014
+ globalFilter: true,
3015
+ rowSelection: true,
3016
+ columns: [
3017
+ { key: 'customer', header: 'Customer', list: 'title' },
3018
+ { key: 'id', header: 'Order', list: 'kicker' },
3019
+ { key: 'total', header: 'Total', align: 'end', list: 'end', filterable: { type: 'number' } },
3020
+ { key: 'status', header: 'Status', Cell: StatusBadge, filterable: { type: 'multiselect' } },
3021
+ { key: 'date', header: 'Date' },
3022
+ { key: 'notes', header: 'Notes', list: 'detail' },
3023
+ ],
3024
+ }}
3025
+ />
3026
+ ```
3027
+
3028
+ | `column.list` | Where the column goes |
3029
+ | ------------- | ------------------------------------------------------------------------------------------------ |
3030
+ | `'title'` | The item's name, 16px semibold, and the button that opens the record view |
3031
+ | `'kicker'` | A small muted line above the title — an id, a date |
3032
+ | `'subtitle'` | A line under the title; several are joined with `·` |
3033
+ | `'end'` | The one value opposite the title, with its column's name under it — a total, a status |
3034
+ | `'field'` | A labelled value in the two-column `<dl>` under the title |
3035
+ | `'detail'` | Only the record view |
3036
+ | `'hidden'` | Nowhere in the list layout |
3037
+ | (unset) | First column → title, first end-aligned → end, next `def.list.fields` (4) → field, rest → detail |
3038
+
3039
+ | `def.list` | Default | Description |
3040
+ | ------------ | ------- | -------------------------------------------------------------------------------------------------- |
3041
+ | `breakpoint` | `576` | The grid's own width, in px, below which `layout: 'auto'` draws the list |
3042
+ | `fields` | `4` | How many unassigned columns become fields before the rest wait for the record view |
3043
+ | `batchSize` | `20` | How many items one **Show more** adds. A paginated grid draws its page and a compact pager instead |
3044
+ | `recordView` | `true` | Whether pressing an item opens the record view |
3045
+
3046
+ What the list does, in the order a reader meets it:
3047
+
3048
+ - **Toolbar**: the title and row count, a full-width search (`globalFilter`), then pills — **Sort** and **Filters** open bottom sheets, **Select** (with `rowSelection`) turns selecting on. The filters in force sit under them as chips, each removable on its own.
3049
+ - **Sort sheet**: one radio group of the sortable columns and one of the direction, applied as they are picked.
3050
+ - **Filter sheet**: a section per filterable column — a full-width field, the phone's own operator picker for a number, chips for eight options or fewer and a searchable checklist past that. A filter applies as it is set, and the main button reads **Show N rows**.
3051
+ - **Item**: a press opens the record view; while selecting it ticks the row instead, a checkbox leads every item and a bar pinned to the bottom of the viewport counts the selection, with **Select all** and **Clear selection**. A tree row carries its chevron before the title, a `rowDetail` row a **Details** toggle, a group is a section header with its count and aggregates.
3052
+ - **Record view**: every column of one row in a bottom sheet, with a pager (`3 of 40`) that follows the list's own sort and filters — the arrow buttons, Left/Right, or a sideways swipe. An editable column has an **Edit** button that opens the column's editor through the same `def.onCellEdit`. Closing hands focus to the item of the row on screen.
3053
+ - **Footer**: the `def.footer` totals as a summary block, then **Show more** (`20 of 340`) — a button, never an endless scroll, so the totals and the page below stay reachable — or the compact pager when the host paginates.
3054
+
3055
+ Four things to know:
3056
+
3057
+ 1. **It is a list, not a restyled grid.** A `<ul>` of `<li>`s, each value in a `<dl>`, `aria-posinset`/`aria-setsize` on each item; the grid's APG keyboard is the table's alone. Do not reach for `role="grid"` in a list item.
3058
+ 2. **It is a chunk of its own.** The sheets and the record view load the first time a grid is narrow (and on import, on a phone-width screen); until then the grid goes on drawing its table. In a test, `await loadListLayout()` from `components/dataGrid/useListLayout` before rendering.
3059
+ 3. **Unmeasured is a table.** A server render cannot know the width, so `layout: 'auto'` prerenders the table and a phone switches before its first paint after hydration. Force `layout: 'list'` where the server knows better.
3060
+ 4. **The selection bar sticks to the viewport**, so no ancestor of the grid may be a scroll container (`overflow: hidden`/`auto`): `overflow: clip` clips the same corners without being one. The grid itself switches to `clip` in the list layout.
3061
+
3062
+ Styling: every row and group header is a `List.Item` (see List), so the `list` tree styles them — the dividers, the hover and pressed tints, `selected`, the muted text. The grid's own is `datagrid.list` > `toolbar` > (`count`, `search`, `button` with an `active` variant, `chip`), `toggle` (a tree's chevron, the Details toggle), `detail`, `button`, `selectionBar`, `sheet` > (`line`, `input`, `chip`, `button`, `primary`). Both trees carry paint only; the geometry is in the list's own chunk.
3063
+
2718
3064
  ### ContextMenuConfig
2719
3065
 
2720
3066
  | Prop | Type | Default | Description |
package/behavior.cjs CHANGED
@@ -1 +1 @@
1
- const e=require("./platform.cjs"),t=require("./effects.cjs"),n=require("./keyboard.cjs");let r=require("react");function i(e){let n=(0,r.useRef)(e);return t.n(()=>{n.current=e}),n}function a(e){let t=i(e);return(0,r.useCallback)((...e)=>t.current?.(...e),[t])}function o(e){let{value:t,defaultValue:n,onChange:o}=e,s=t!==void 0,[c,l]=(0,r.useState)(n),u=s?t:c,d=i(u),f=a(o);return[u,(0,r.useCallback)((e,t)=>{let n=typeof e==`function`?e(d.current):e;Object.is(n,d.current)||(l(n),f(n,t))},[f,d])]}var s=[];function c(e,t){let n=t.elements();return e.elements().some(e=>n.some(t=>e!==t&&e.contains(t)))}function l(){let e=s.filter(e=>!s.some(t=>t!==e&&c(e,t)));return e[e.length-1]}function u(n){let{enabled:r=!0,onDismiss:o,inside:c,escapeKey:u=!0,outsidePointer:d=!0}=n,f=i(c),p=a(o);t.n(()=>{if(!r)return;let t={elements:()=>(f.current??[]).map(t=>e.s(t)).filter(e=>e!==null)};s.push(t);let n=new AbortController,i=(e,t)=>document.addEventListener(e,t,{signal:n.signal});return u&&i(`keydown`,e=>{e.key!==`Escape`||e.defaultPrevented||l()===t&&p(`escape`,e)}),d&&i(`pointerdown`,t=>{e.l(t,f.current??[])||p(`outside-pointer`,t)}),()=>{n.abort();let e=s.indexOf(t);e!==-1&&s.splice(e,1)}},[r,u,d,p,f])}function d(){let t=e.t()?.activeElement;return t&&t!==document.body?t:null}function f(t={}){let{enabled:n=!0,returnTo:i,preventScroll:o}=t,[s,c]=(0,r.useState)(null),[l,u]=(0,r.useState)(!1);n!==l&&(u(n),n&&c(d()));let f=a(()=>{(e.c(i)??s)?.focus({preventScroll:o})});return(0,r.useEffect)(()=>{if(n)return()=>{let e=document.activeElement;(!e||e===document.body||!document.contains(e))&&f()}},[n,f]),{returnFocus:f}}var p=10;function m(e,t){return t<0?-1:Math.max(0,Math.min(e,t))}function h(e,t,n,r,i){if(n===0)return-1;let a=e;for(let e=0;e<n;e++){if(a+=t,a<0||a>=n){if(!r)return-1;a=(a%n+n)%n}if(!i?.(a))return a}return-1}function g(e,t,n){return h(e===1?-1:t,e,t,!1,n)}function _(t){let{count:a,columns:s,columnIndexOf:c,pageSize:l=p,defaultActiveColumn:u=0,onActiveCellChange:d,orientation:f=`vertical`,loop:_=!0,activeIndex:v,defaultActiveIndex:y=0,onActiveIndexChange:b,isDisabled:x,textOf:S,focusItems:C=!0,onSelect:w}=t,[T,E]=o({value:v,defaultValue:y,onChange:b}),D=Math.min(T,a-1),O=s!==void 0,k=(0,r.useCallback)(e=>typeof s==`function`?s(e):s??0,[s]),A=(0,r.useCallback)((e,t)=>{let n=k(e);if(!c)return m(t,n-1);if(n<=0)return-1;let r=0;for(;r+1<n&&c(e,r+1)<=t;)r++;return r},[c,k]),[j,M]=(0,r.useState)(u),N=O&&D>=0?A(D,j):-1,P=(0,r.useRef)([]),F=(0,r.useRef)(new Map),I=(0,r.useRef)(new Map),L=(0,r.useRef)(new Map),R=(0,r.useRef)({query:``,at:0}),z=(0,r.useCallback)(e=>{P.current[e]?.focus()},[]),B=(0,r.useRef)(!1),V=(0,r.useCallback)((e,t)=>{B.current=!0,I.current.get(`${e}:${t}`)?.focus(),B.current=!1},[]),H=(0,r.useCallback)(()=>O?I.current.get(`${D}:${N}`)??null:P.current[D]??null,[N,D,O]),U=i({row:D,column:N}),W=i(j),G=i(d),K=i(k),q=i(A),J=i(c),Y=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=Math.max(0,t),o=r<0?-1:q.current(r,i),s=U.current;return r===s.row&&o===s.column?s:(E(r,n),M(i),G.current?.({row:r,column:o},n),{row:r,column:o})},[U,q,G,a,E]),X=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=r<0?-1:m(Math.max(0,t),K.current(r)-1);return Y(r,i<0?0:J.current?.(r,i)??i,n)},[J,a,Y,K]),Z=(0,r.useCallback)((e,t)=>{let n=`${e}:${t}`;return L.current.has(n)||L.current.set(n,e=>{e?I.current.set(n,e):(I.current.delete(n),L.current.delete(n))}),{ref:L.current.get(n),tabIndex:e===D&&t===N?0:-1,onFocus:()=>{B.current||X(e,t,{reason:`focus`})}}},[N,D,X]),Q=(0,r.useCallback)(e=>(F.current.has(e)||F.current.set(e,t=>{P.current[e]=t}),{ref:F.current.get(e),tabIndex:C?e===D?0:-1:void 0,onFocus:C?()=>E(e,{reason:`focus`}):void 0}),[D,C,E]),$=(0,r.useCallback)(t=>{let{row:n,column:r}=U.current,i=(e,n)=>{t.preventDefault();let r=X(e,n,{reason:`keyboard`,event:t});C&&V(r.row,r.column)},o=e=>{t.preventDefault();let n=Y(e,W.current,{reason:`keyboard`,event:t});C&&V(n.row,n.column)},s=2**53-1,c=()=>e.u(t.currentTarget)?-1:1;switch(t.key){case`ArrowRight`:return i(n,r+c());case`ArrowLeft`:return i(n,r-c());case`ArrowDown`:return o(n+1);case`ArrowUp`:return o(n-1);case`Home`:return t.ctrlKey||t.metaKey?i(0,0):i(n,0);case`End`:return t.ctrlKey||t.metaKey?i(a-1,s):i(n,s);case`PageDown`:return o(n+l);case`PageUp`:return o(n-l)}},[U,a,W,V,C,l,Y,X]);return{activeIndex:D,activeColumn:N,setActiveIndex:E,setActiveCell:X,onKeyDown:(0,r.useCallback)(t=>{if(t.defaultPrevented)return;if(O){$(t);return}let r=f!==`horizontal`,i=f!==`vertical`,o=R.current,s=n.t.isOpen(o,t.timeStamp),c=(e,n)=>{e!==-1&&e!==D&&(t.preventDefault(),E(e,{reason:n,event:t}),C&&z(e))};if(i&&(t.key===`ArrowRight`||t.key===`ArrowLeft`)){let n=t.key===`ArrowRight`!==e.u(t.currentTarget);c(h(D,n?1:-1,a,_,x),`keyboard`);return}if(t.key===`ArrowDown`&&r){c(h(D,1,a,_,x),`keyboard`);return}if(t.key===`ArrowUp`&&r){c(h(D,-1,a,_,x),`keyboard`);return}if(t.key===`Home`){c(g(1,a,x),`keyboard`);return}if(t.key===`End`){c(g(-1,a,x),`keyboard`);return}if(t.key===`Enter`||t.key===` `&&!(s&&S)){if(!w||D===-1)return;t.preventDefault(),w(D,t);return}if(S&&n.t.isPrintable(t)){let e=n.t.push(o,t.key,t.timeStamp);c(n.t.target(e,D,a,S,x),`typeahead`)}},[D,a,z,C,x,O,_,$,w,f,E,S]),itemProps:Q,cellProps:Z,activeItem:H}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return _}});
1
+ const e=require("./platform.cjs"),t=require("./effects.cjs"),n=require("./keyboard.cjs");let r=require("react");function i(e){let n=(0,r.useRef)(e);return t.n(()=>{n.current=e}),n}function a(e){let t=i(e);return(0,r.useCallback)((...e)=>t.current?.(...e),[t])}function o(e){let{value:t,defaultValue:n,onChange:o}=e,s=t!==void 0,[c,l]=(0,r.useState)(n),u=s?t:c,d=i(u),f=a(o);return[u,(0,r.useCallback)((e,t)=>{let n=typeof e==`function`?e(d.current):e;Object.is(n,d.current)||(l(n),f(n,t))},[f,d])]}var s=[];function c(e,t){let n=t.elements();return e.elements().some(e=>n.some(t=>e!==t&&e.contains(t)))}function l(){let e=s.filter(e=>!s.some(t=>t!==e&&c(e,t)));return e[e.length-1]}function u(n){let{enabled:r=!0,onDismiss:o,inside:c,escapeKey:u=!0,outsidePointer:d=!0}=n,f=i(c),p=a(o);t.n(()=>{if(!r)return;let t={elements:()=>(f.current??[]).map(t=>e.c(t)).filter(e=>e!==null)};s.push(t);let n=new AbortController,i=(e,t)=>document.addEventListener(e,t,{signal:n.signal});return u&&i(`keydown`,e=>{e.key!==`Escape`||e.defaultPrevented||l()===t&&p(`escape`,e)}),d&&i(`pointerdown`,t=>{e.u(t,f.current??[])||p(`outside-pointer`,t)}),()=>{n.abort();let e=s.indexOf(t);e!==-1&&s.splice(e,1)}},[r,u,d,p,f])}function d(){let t=e.t()?.activeElement;return t&&t!==document.body?t:null}function f(t={}){let{enabled:n=!0,returnTo:i,preventScroll:o}=t,[s,c]=(0,r.useState)(null),[l,u]=(0,r.useState)(!1);n!==l&&(u(n),n&&c(d()));let f=a(()=>{(e.l(i)??s)?.focus({preventScroll:o})});return(0,r.useEffect)(()=>{if(n)return()=>{let e=document.activeElement;(!e||e===document.body||!document.contains(e))&&f()}},[n,f]),{returnFocus:f}}var p=10;function m(e,t){return t<0?-1:Math.max(0,Math.min(e,t))}function h(e,t,n,r,i){if(n===0)return-1;let a=e;for(let e=0;e<n;e++){if(a+=t,a<0||a>=n){if(!r)return-1;a=(a%n+n)%n}if(!i?.(a))return a}return-1}function g(e,t,n){return h(e===1?-1:t,e,t,!1,n)}function _(t){let{count:a,columns:s,columnIndexOf:c,pageSize:l=p,defaultActiveColumn:u=0,onActiveCellChange:d,orientation:f=`vertical`,loop:_=!0,activeIndex:v,defaultActiveIndex:y=0,onActiveIndexChange:b,isDisabled:x,textOf:S,focusItems:C=!0,onSelect:w}=t,[T,E]=o({value:v,defaultValue:y,onChange:b}),D=Math.min(T,a-1),O=s!==void 0,k=(0,r.useCallback)(e=>typeof s==`function`?s(e):s??0,[s]),A=(0,r.useCallback)((e,t)=>{let n=k(e);if(!c)return m(t,n-1);if(n<=0)return-1;let r=0;for(;r+1<n&&c(e,r+1)<=t;)r++;return r},[c,k]),[j,M]=(0,r.useState)(u),N=O&&D>=0?A(D,j):-1,P=(0,r.useRef)([]),F=(0,r.useRef)(new Map),I=(0,r.useRef)(new Map),L=(0,r.useRef)(new Map),R=(0,r.useRef)({query:``,at:0}),z=(0,r.useCallback)(e=>{P.current[e]?.focus()},[]),B=(0,r.useRef)(!1),V=(0,r.useCallback)((e,t)=>{B.current=!0,I.current.get(`${e}:${t}`)?.focus(),B.current=!1},[]),H=(0,r.useCallback)(()=>O?I.current.get(`${D}:${N}`)??null:P.current[D]??null,[N,D,O]),U=i({row:D,column:N}),W=i(j),G=i(d),K=i(k),q=i(A),J=i(c),Y=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=Math.max(0,t),o=r<0?-1:q.current(r,i),s=U.current;return r===s.row&&o===s.column?s:(E(r,n),M(i),G.current?.({row:r,column:o},n),{row:r,column:o})},[U,q,G,a,E]),X=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=r<0?-1:m(Math.max(0,t),K.current(r)-1);return Y(r,i<0?0:J.current?.(r,i)??i,n)},[J,a,Y,K]),Z=(0,r.useCallback)((e,t)=>{let n=`${e}:${t}`;return L.current.has(n)||L.current.set(n,e=>{e?I.current.set(n,e):(I.current.delete(n),L.current.delete(n))}),{ref:L.current.get(n),tabIndex:e===D&&t===N?0:-1,onFocus:()=>{B.current||X(e,t,{reason:`focus`})}}},[N,D,X]),Q=(0,r.useCallback)(e=>(F.current.has(e)||F.current.set(e,t=>{P.current[e]=t}),{ref:F.current.get(e),tabIndex:C?e===D?0:-1:void 0,onFocus:C?()=>E(e,{reason:`focus`}):void 0}),[D,C,E]),$=(0,r.useCallback)(t=>{let{row:n,column:r}=U.current,i=(e,n)=>{t.preventDefault();let r=X(e,n,{reason:`keyboard`,event:t});C&&V(r.row,r.column)},o=e=>{t.preventDefault();let n=Y(e,W.current,{reason:`keyboard`,event:t});C&&V(n.row,n.column)},s=2**53-1,c=()=>e.d(t.currentTarget)?-1:1;switch(t.key){case`ArrowRight`:return i(n,r+c());case`ArrowLeft`:return i(n,r-c());case`ArrowDown`:return o(n+1);case`ArrowUp`:return o(n-1);case`Home`:return t.ctrlKey||t.metaKey?i(0,0):i(n,0);case`End`:return t.ctrlKey||t.metaKey?i(a-1,s):i(n,s);case`PageDown`:return o(n+l);case`PageUp`:return o(n-l)}},[U,a,W,V,C,l,Y,X]);return{activeIndex:D,activeColumn:N,setActiveIndex:E,setActiveCell:X,onKeyDown:(0,r.useCallback)(t=>{if(t.defaultPrevented)return;if(O){$(t);return}let r=f!==`horizontal`,i=f!==`vertical`,o=R.current,s=n.t.isOpen(o,t.timeStamp),c=(e,n)=>{e!==-1&&e!==D&&(t.preventDefault(),E(e,{reason:n,event:t}),C&&z(e))};if(i&&(t.key===`ArrowRight`||t.key===`ArrowLeft`)){let n=t.key===`ArrowRight`!==e.d(t.currentTarget);c(h(D,n?1:-1,a,_,x),`keyboard`);return}if(t.key===`ArrowDown`&&r){c(h(D,1,a,_,x),`keyboard`);return}if(t.key===`ArrowUp`&&r){c(h(D,-1,a,_,x),`keyboard`);return}if(t.key===`Home`){c(g(1,a,x),`keyboard`);return}if(t.key===`End`){c(g(-1,a,x),`keyboard`);return}if(t.key===`Enter`||t.key===` `&&!(s&&S)){if(!w||D===-1)return;t.preventDefault(),w(D,t);return}if(S&&n.t.isPrintable(t)){let e=n.t.push(o,t.key,t.timeStamp);c(n.t.target(e,D,a,S,x),`typeahead`)}},[D,a,z,C,x,O,_,$,w,f,E,S]),itemProps:Q,cellProps:Z,activeItem:H}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return _}});