@box-kite/react 2.3.1 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/box-kite-rules.md +58 -94
- package/.claude/skills/box-kite/SKILL.md +69 -104
- package/.claude/skills/box-kite/references/components.md +8 -1
- package/.claude/skills/box-kite/references/patterns.md +1 -1
- package/.claude/skills/box-kite/references/rules.md +115 -0
- package/.claude/skills/box-kite/references/styling.md +5 -3
- package/.cursor/rules/box-kite.mdc +60 -96
- package/AGENTS.md +61 -97
- package/BOX_AI_CONTEXT.md +111 -40
- package/BOX_KITE_AI_CONTEXT.md +111 -40
- package/README.md +200 -721
- package/box.cjs +1 -1
- package/box.d.ts +5 -0
- package/box.mjs +5 -5
- package/catalog.cjs +1 -1
- package/catalog.mjs +53 -18
- package/client.cjs +1 -1
- package/client.mjs +85 -85
- package/components/accordion.cjs +1 -1
- package/components/accordion.mjs +1 -1
- package/components/button.cjs +1 -1
- package/components/button.mjs +1 -1
- package/components/calendar.cjs +1 -1
- package/components/calendar.mjs +1 -1
- package/components/chart.cjs +1 -1
- package/components/chart.mjs +3 -3
- package/components/checkbox.cjs +1 -1
- package/components/checkbox.mjs +1 -1
- package/components/combobox/comboboxListbox.d.ts +2 -1
- package/components/combobox.cjs +1 -1
- package/components/combobox.d.ts +1 -1
- package/components/combobox.mjs +200 -189
- package/components/dashboard.cjs +1 -1
- package/components/dashboard.mjs +1 -1
- package/components/dataGrid/contracts/dataGridContract.d.ts +2 -1
- package/components/dataGrid/useListLayout.d.ts +1 -1
- package/components/dataGrid.cjs +3 -3
- package/components/dataGrid.mjs +453 -453
- package/components/dateField.cjs +1 -1
- package/components/dateField.mjs +16 -16
- package/components/datePicker.cjs +1 -1
- package/components/datePicker.mjs +1 -1
- package/components/dateTime.cjs +1 -1
- package/components/dateTime.mjs +1 -1
- package/components/dialog.cjs +1 -1
- package/components/dialog.mjs +1 -1
- package/components/flex.cjs +1 -1
- package/components/flex.mjs +1 -1
- package/components/flow.cjs +1 -0
- package/components/flow.d.ts +56 -0
- package/components/flow.mjs +98 -0
- package/components/form.cjs +1 -1
- package/components/form.mjs +1 -1
- package/components/grid.cjs +1 -1
- package/components/grid.mjs +1 -1
- package/components/icon.cjs +1 -1
- package/components/icon.mjs +1 -1
- package/components/list.cjs +1 -1
- package/components/list.mjs +1 -1
- package/components/menu.cjs +1 -1
- package/components/menu.mjs +1 -1
- package/components/popover.cjs +1 -1
- package/components/popover.mjs +1 -1
- package/components/radioButton.cjs +1 -1
- package/components/radioButton.mjs +1 -1
- package/components/radioGroup.cjs +1 -1
- package/components/radioGroup.mjs +2 -1
- package/components/semantics.cjs +1 -1
- package/components/semantics.d.ts +32 -30
- package/components/semantics.mjs +3 -3
- package/components/skeleton.cjs +1 -1
- package/components/skeleton.d.ts +1 -1
- package/components/skeleton.mjs +3 -1
- package/components/slider.cjs +1 -1
- package/components/slider.mjs +1 -1
- package/components/sortableList.cjs +1 -1
- package/components/sortableList.mjs +1 -1
- package/components/svg.cjs +1 -1
- package/components/svg.mjs +14 -14
- package/components/swipeItem.cjs +1 -1
- package/components/swipeItem.mjs +1 -1
- package/components/tabs.cjs +1 -1
- package/components/tabs.mjs +1 -1
- package/components/textarea.cjs +1 -1
- package/components/textarea.d.ts +1 -1
- package/components/textarea.mjs +1 -1
- package/components/textbox.cjs +1 -1
- package/components/textbox.d.ts +1 -1
- package/components/textbox.mjs +1 -1
- package/components/timeZoneSelect.d.ts +1 -1
- package/components/toaster.cjs +1 -1
- package/components/toaster.mjs +14 -15
- package/components/tooltip.cjs +1 -1
- package/components/tooltip.mjs +1 -1
- package/components/virtualList.cjs +1 -1
- package/components/virtualList.mjs +1 -1
- package/components/visuallyHidden.cjs +1 -1
- package/components/visuallyHidden.mjs +1 -1
- package/docs/a11y.md +1 -2
- package/docs/components.md +1 -0
- package/docs/index.md +1 -1
- package/docs/props.md +1 -1
- package/effects.cjs +1 -1
- package/effects.mjs +2 -2
- package/identity.cjs +1 -1
- package/identity.mjs +5 -7
- package/package.json +50 -36
- package/react/boxClassNames.d.ts +2 -2
- package/react/effects.d.ts +5 -6
- package/react/identity/useIdentifier.d.ts +0 -3
- package/react-shared.cjs +1 -1
- package/react-shared.mjs +42 -3
- package/rsc.cjs +1 -1
- package/rsc.d.ts +1 -0
- package/rsc.mjs +2 -2
- package/utils/catalog/catalogContracts.d.ts +0 -10
- package/utils/flow/flowPalette.d.ts +17 -0
- package/utils/flow/flowUtils.d.ts +47 -0
package/BOX_AI_CONTEXT.md
CHANGED
|
@@ -660,8 +660,15 @@ const wobble = Box.spring({ stiffness: 120, damping: 8 });
|
|
|
660
660
|
the same commit, with nothing to configure. `Tooltip`, the `Dropdown` popup and the DataGrid column
|
|
661
661
|
menu are all built on it, and each expresses its exit as a `closed` variant in its component styles — plus `up`/`closedUp` on `dropdown.items`, since a popup that opened upward has to collapse upward.
|
|
662
662
|
- **`Box.configure({ transition })`** changes what the base class transitions for the whole engine:
|
|
663
|
-
a group name,
|
|
663
|
+
a group name, a comma list of groups and property names (`'colors, opacity'`), or `false` to declare
|
|
664
|
+
nothing at all and leave transitions entirely to the props. Anything else transitions `all` and warns.
|
|
664
665
|
Call it before the first render.
|
|
666
|
+
- **An inline phrasing element is `inline-block`**, where every other Box is `block`: `a`, `span`, `label`, `time`, `mark`, `abbr`, `code`, `kbd`, `samp`, `var`, `em`,
|
|
667
|
+
`strong`, `small`, `b`, `i`, `u`, `s`, `sub`, `sup`, `q`, `cite`, `dfn`, `data`, `output`, `bdi`, `bdo`. It is
|
|
668
|
+
selected on the rendered element at the base class's specificity, so `<Link>`, `<Box tag="a">` and a
|
|
669
|
+
`useClassNames` class on a router's `<a>` all stay in their sentence, margin and padding still apply, and a
|
|
670
|
+
`display` prop or component still wins. Media (`img`, `svg`, `video`, `canvas`, `iframe`) stays block. An `inline-block` never breaks across lines, so a
|
|
671
|
+
prose link or `<Mark>` that has to wrap with the text sets `display="inline"`.
|
|
665
672
|
|
|
666
673
|
---
|
|
667
674
|
|
|
@@ -1263,7 +1270,7 @@ import Textbox from '@box-kite/react/components/textbox';
|
|
|
1263
1270
|
<Flex d="column" gap={2} md={{ d: 'row', gap: 4 }}>{children}</Flex>
|
|
1264
1271
|
|
|
1265
1272
|
// Grid
|
|
1266
|
-
<Grid
|
|
1273
|
+
<Grid gridTemplateColumns={3} gap={4}>{items.map(i => <Box key={i.id}>{i.content}</Box>)}</Grid>
|
|
1267
1274
|
|
|
1268
1275
|
// Card
|
|
1269
1276
|
<Box p={4} bgColor="white" borderRadius={8} shadow="medium">{content}</Box>
|
|
@@ -1320,6 +1327,11 @@ undefined and rendering it costs nothing, so write the line either way. Pass `{
|
|
|
1320
1327
|
second argument for an element inside an `<svg>`. `<Icon>` is this hook plus that flag — reach for
|
|
1321
1328
|
`Icon` for an icon and this only for everything else.
|
|
1322
1329
|
|
|
1330
|
+
The class carries the base reset, which is `display: block` except on an `<a>`, `<span>`, `<label>` and the
|
|
1331
|
+
other phrasing elements, which are `inline-block`: a `NavLink` styled this way stays in its sentence.
|
|
1332
|
+
`{ base: false }` leaves the reset out — for a library's own root (`<ReactFlow>`, `<MiniMap>`, a `BaseEdge`)
|
|
1333
|
+
whose margin, display and transitions are its own.
|
|
1334
|
+
|
|
1323
1335
|
### Group and Peer
|
|
1324
1336
|
|
|
1325
1337
|
```tsx
|
|
@@ -1456,6 +1468,19 @@ Full reference: `docs/a11y-primitives.md`.
|
|
|
1456
1468
|
|
|
1457
1469
|
---
|
|
1458
1470
|
|
|
1471
|
+
## Field ids, external labels and the invalid state
|
|
1472
|
+
|
|
1473
|
+
- **A top-level `id` lands on the control, once.** `<Combobox id="owner">` puts it on the `<input role="combobox">`
|
|
1474
|
+
and nowhere else, as `Textbox`, `Textarea`, `Select` (its button), `Checkbox`, `Switch`, `RadioButton` and
|
|
1475
|
+
`TimeZoneSelect` do, so `<label htmlFor="owner">` names the control. `DateField`, `DatePicker`, `Slider` and
|
|
1476
|
+
`RadioGroup` are groups no `<label>` can name: the `id` goes on the group, and `labelledBy` points it at an
|
|
1477
|
+
external label.
|
|
1478
|
+
- **`aria-invalid` draws the error border on every field.** `props={{ 'aria-invalid': true }}` turns the frame of a
|
|
1479
|
+
`Textbox`, `Textarea`, `Select` or `Combobox` `red-500`, through hover and focus, in both themes. A combobox's
|
|
1480
|
+
attribute is on its input and its border on the shell, so the shell matches `:has([aria-invalid="true"])` —
|
|
1481
|
+
restyle it as `combobox: { styles: { has: { '[aria-invalid="true"]': { … } } } }`, the others with
|
|
1482
|
+
`ariaAttr: { invalid: { … } }`. `DateField` and `DatePicker` take an `invalid` prop that does both.
|
|
1483
|
+
|
|
1459
1484
|
## Sizes: compact, normal, large
|
|
1460
1485
|
|
|
1461
1486
|
Every built-in component is drawn at one of three sizes, and a size is two CSS variables —
|
|
@@ -1521,16 +1546,16 @@ the association needs no `htmlFor`/`id` pair and the whole row is a click target
|
|
|
1521
1546
|
nothing wraps the input — the markup is what it always was. `labelProps` styles that `<label>`;
|
|
1522
1547
|
every other Box prop styles the control itself.
|
|
1523
1548
|
|
|
1524
|
-
| Component | Prop | What it does
|
|
1525
|
-
| ------------ | ------------------------ |
|
|
1526
|
-
| all three | `label` / `labelProps` | The wrapping `<label>` and its styles.
|
|
1527
|
-
| `Checkbox` | `indeterminate` | Sets the DOM property **and** `aria-checked="mixed"`, which is what is announced.
|
|
1528
|
-
| `Switch` | — | `role="switch"` over the same input; Space **and** Enter toggle, Enter does not submit.
|
|
1529
|
-
| `RadioGroup` | `label` | `role="radiogroup"` named by it — a set of radios with nothing over it is a flat list.
|
|
1530
|
-
| `RadioGroup` | `name` | The field every item submits under. Generated when left out.
|
|
1531
|
-
| `RadioGroup` | `value` / `defaultValue` | The selected item's value (a string — the platform's own model for a radio).
|
|
1532
|
-
| `RadioGroup` | `onValueChange` | `(value, { reason, event })` — `click` or `keyboard`. `onChange` is the deprecated spelling.
|
|
1533
|
-
| `RadioGroup` | `orientation` | `vertical` (default) or `horizontal
|
|
1549
|
+
| Component | Prop | What it does |
|
|
1550
|
+
| ------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1551
|
+
| all three | `label` / `labelProps` | The wrapping `<label>` and its styles. |
|
|
1552
|
+
| `Checkbox` | `indeterminate` | Sets the DOM property **and** `aria-checked="mixed"`, which is what is announced. |
|
|
1553
|
+
| `Switch` | — | `role="switch"` over the same input; Space **and** Enter toggle, Enter does not submit. |
|
|
1554
|
+
| `RadioGroup` | `label` | `role="radiogroup"` named by it — a set of radios with nothing over it is a flat list. |
|
|
1555
|
+
| `RadioGroup` | `name` | The field every item submits under. Generated when left out. |
|
|
1556
|
+
| `RadioGroup` | `value` / `defaultValue` | The selected item's value (a string — the platform's own model for a radio). |
|
|
1557
|
+
| `RadioGroup` | `onValueChange` | `(value, { reason, event })` — `click` or `keyboard`. `onChange` is the deprecated spelling. |
|
|
1558
|
+
| `RadioGroup` | `orientation` | `vertical` (default) or `horizontal`, which wraps and keeps the label on a line of its own above the radios. Both arrow pairs navigate either way. |
|
|
1534
1559
|
|
|
1535
1560
|
Arrow keys move **and select** inside a group, wrapping at both ends and skipping disabled items;
|
|
1536
1561
|
Tab enters the group once and leaves it once, because a native radio set is already a single tab
|
|
@@ -2660,6 +2685,51 @@ function TrendCell({ cell }: { cell: CellModel<Row> }) {
|
|
|
2660
2685
|
<DataGrid data={rows} def={{ rowKey: 'id', columns: [{ key: 'trend', header: 'Last 12 months', width: 180, Cell: TrendCell }] }} />;
|
|
2661
2686
|
```
|
|
2662
2687
|
|
|
2688
|
+
### Theming a React Flow canvas — `FlowContainer`
|
|
2689
|
+
|
|
2690
|
+
`ChartContainer`'s move for diagrams. `@box-kite/react/components/flow` (server-safe, imports nothing from xyflow) is a Box
|
|
2691
|
+
that declares React Flow's `--xy-*` variables from the palette in both themes, so the canvas — background, pattern, edges,
|
|
2692
|
+
handles, built-in nodes, minimap, controls, selection box, edge labels — names no colour and follows the page:
|
|
2693
|
+
|
|
2694
|
+
```tsx
|
|
2695
|
+
import { Background, Controls, MiniMap, ReactFlow } from '@xyflow/react';
|
|
2696
|
+
import '@xyflow/react/dist/style.css';
|
|
2697
|
+
import { FlowContainer, flowColorMode } from '@box-kite/react/components/flow';
|
|
2698
|
+
|
|
2699
|
+
function Canvas() {
|
|
2700
|
+
const [theme] = Box.useTheme();
|
|
2701
|
+
|
|
2702
|
+
return (
|
|
2703
|
+
<FlowContainer height={120}>
|
|
2704
|
+
<ReactFlow nodes={nodes} edges={edges} colorMode={flowColorMode(theme)} fitView>
|
|
2705
|
+
<Background />
|
|
2706
|
+
<Controls />
|
|
2707
|
+
<MiniMap />
|
|
2708
|
+
</ReactFlow>
|
|
2709
|
+
</FlowContainer>
|
|
2710
|
+
);
|
|
2711
|
+
}
|
|
2712
|
+
```
|
|
2713
|
+
|
|
2714
|
+
| Prop | What it is |
|
|
2715
|
+
| -------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
2716
|
+
| `tokens` | Repaints a role: `{ selection: 'emerald-500' }` for both themes, `{ surface: { light: 'base-50', dark: 'base-950' } }` per theme |
|
|
2717
|
+
| `motion` | `'static'` zeroes every Box transition inside the canvas, for nodes moved or resized every frame |
|
|
2718
|
+
|
|
2719
|
+
- **Eight roles**, each a custom property a custom node can read: `--flow-surface`, `--flow-panel`, `--flow-text`, `--flow-border`,
|
|
2720
|
+
`--flow-grid`, `--flow-edge`, `--flow-selection`, `--flow-handle`. Defaults are `base`/`accent`, so `Box.palette()` reaches them.
|
|
2721
|
+
- **The defaults are the `flowContainer` style node**, so one override repaints every canvas in an app:
|
|
2722
|
+
`Box.components({ flowContainer: { styles: { vars: { 'flow-selection': 'emerald-500' } } } })`; `tokens` is the per-canvas answer.
|
|
2723
|
+
- **One `--xy-*` variable is `vars`** (`vars={{ 'xy-edge-stroke-width': '2' }}`); a prop on `<MiniMap>`/`<Background>` still wins.
|
|
2724
|
+
- **`colorMode={flowColorMode(theme)}`** keeps React Flow's own `.dark` class in step with the page. A canvas in another theme sits
|
|
2725
|
+
in `<Box.Theme use="local">`; never `colorMode="light"` under a dark page — `.light` is no theme boundary, so the two mix.
|
|
2726
|
+
- **A custom node is invisible to the keyboard** — xyflow removes every node's focus outline. Spread `flowNodeStyles` on its
|
|
2727
|
+
root (an outline while xyflow's wrapper is `:focus-visible`) and `flowNodeSelectedStyles` when its `selected` prop is true;
|
|
2728
|
+
both are outlines in the `selection` role, so they survive forced colours. A custom edge on `BaseEdge` takes `flowEdgeStyles`
|
|
2729
|
+
through `useClassNames(flowEdgeStyles, { svg: true, base: false })`.
|
|
2730
|
+
- Colour props refuse `var()`, so a custom node reads a role through `css`:
|
|
2731
|
+
`css={{ backgroundColor: 'var(--flow-panel)', borderColor: 'var(--flow-border)', color: 'var(--flow-text)' }}`.
|
|
2732
|
+
|
|
2663
2733
|
---
|
|
2664
2734
|
|
|
2665
2735
|
## Key Reminders for AI Assistants
|
|
@@ -2677,7 +2747,7 @@ function TrendCell({ cell }: { cell: CellModel<Row> }) {
|
|
|
2677
2747
|
11. **Size shortcuts**: `width="fit"` = 100%, `width="fit-screen"` = 100vw, `width="1/2"` = 50%
|
|
2678
2748
|
12. **Box is memoized** with `React.memo`, and so are `Flex`, `H1`, `Button` and the rest. A component of your own that is a Box with defaults calls `useBoxElement({ p: 4, ...props }, ref)` from its own `forwardRef` rather than returning `<Box>` — one fiber instead of two
|
|
2679
2749
|
13. **`style` is top-level only** — never inside breakpoints (`sm={{ style: ... }}`), pseudo-classes, or theme objects. `css` is the one that nests: `sm={{ css: { objectPosition: 'top' } }}` is a rule
|
|
2680
|
-
14. **A CSS variable is a prop**: `vars={{ 'color-x': 'sky-500' }}` declares `--color-x` for everything inside — and a third-party chart goes in `<ChartContainer series={['revenue']}>` so it names no colour at all
|
|
2750
|
+
14. **A CSS variable is a prop**: `vars={{ 'color-x': 'sky-500' }}` declares `--color-x` for everything inside — and a third-party chart goes in `<ChartContainer series={['revenue']}>` so it names no colour at all, and a React Flow canvas goes in `<FlowContainer>` with `colorMode={flowColorMode(theme)}`
|
|
2681
2751
|
15. **All sizing props use divider 4** — `width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight` are NOT direct pixels. `height={10}` = 2.5rem = 40px
|
|
2682
2752
|
16. **A state your own code sets is a nested prop too**: `dataAttr={{ 'state=open': { … } }}` → `[data-state="open"]`, `ariaAttr={{ selected: { … } }}` → `[aria-selected="true"]`, `has={{ ':checked': { … } }}`, `not={{ hover: { … } }}`, `nth={{ odd: { … } }}`. The attribute itself still goes in `props`
|
|
2683
2753
|
17. **Somebody else's state is `group`/`peer`**: `group={{ 'card/hover': { … } }}` → `.card:hover .x`, `peer={{ 'agree/checked': { … } }}` → `.agree:checked ~ .x`. The ancestor or sibling carries the class via `className`; `hoverGroup` and its four siblings are the older spelling
|
|
@@ -2790,27 +2860,27 @@ named ones carry the reason.
|
|
|
2790
2860
|
|
|
2791
2861
|
### ColumnType
|
|
2792
2862
|
|
|
2793
|
-
| Prop | Type | Default | Description
|
|
2794
|
-
| ------------------------ | ------------------------------------------------------------------------------- | --------- |
|
|
2795
|
-
| `key` | `Key` | required | Column identifier (maps to TRow property)
|
|
2796
|
-
| `header` | `string` | — | Header text
|
|
2797
|
-
| `width` | `number` | `200` | Base width in px
|
|
2798
|
-
| `align` | `'start' \| 'end' \| 'center' \| 'left' \| 'right'` | `'left'` | Cell alignment. `start`/`end` follow the reading order; `left`/`right` stay on the screen side they name
|
|
2799
|
-
| `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
|
|
2800
|
-
| `columns` | `ColumnType[]` | — | Nested columns (grouped header)
|
|
2801
|
-
| `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
|
|
2802
|
-
| `sortable` / `resizable` | `boolean` | inherits | Override grid-level setting
|
|
2803
|
-
| `flexible` | `boolean` | `true` | Participate in flex width distribution
|
|
2804
|
-
| `filterable` | `boolean \| FilterConfig` | — | `true` (text), `{ type: 'number', min?, max? }`, `{ type: 'multiselect', options? }`
|
|
2805
|
-
| `contextMenu` | `boolean \| ContextMenuConfig` | inherits | Override grid-level context menu. `false` hides entirely. `{ sort?, pin?, group? }` controls sections
|
|
2806
|
-
| `aggregate` | `AggregateName \| (values, rows) => value` | — | Total this column over each group row, and over the footer. See below
|
|
2807
|
-
| `AggregateCell` | `({ cell }) => ReactNode` | — | Renders the aggregate. `cell`: `{ value, column, rows, scope }`, `scope` being `'group'` or `'footer'`
|
|
2808
|
-
| `editable` | `boolean \| (row) => boolean` | inherits | Whether this column’s cells can be edited — every row, or the rows a predicate says
|
|
2809
|
-
| `editor` | `CellEditorType \| CellEditorConfig` | inferred | `text`/`number`/`checkbox`/`select`. Without one it is read off the value in the cell
|
|
2810
|
-
| `EditCell` | `({ cell }) => ReactNode` | — | An editor of your own, bound to `cell.draft` with `cell.commitEdit()`/`cell.cancelEdit()`
|
|
2811
|
-
| `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
|
|
2812
|
-
| `list` | `'title' \| 'kicker' \| 'subtitle' \| 'end' \| 'field' \| 'detail' \| 'hidden'` | inferred | Where the column goes in a list item, on a phone
|
|
2813
|
-
| `exportFormat` | `string` | — | The number format its cells wear in a workbook, `'#,##0.00'` or `'yyyy-mm-dd'`
|
|
2863
|
+
| Prop | Type | Default | Description |
|
|
2864
|
+
| ------------------------ | ------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2865
|
+
| `key` | `Key` | required | Column identifier (maps to TRow property) |
|
|
2866
|
+
| `header` | `string` | — | Header text |
|
|
2867
|
+
| `width` | `number` | `200` | Base width in px |
|
|
2868
|
+
| `align` | `'start' \| 'end' \| 'center' \| 'left' \| 'right'` | `'left'` | Cell alignment. `start`/`end` follow the reading order; `left`/`right` stay on the screen side they name |
|
|
2869
|
+
| `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 |
|
|
2870
|
+
| `columns` | `ColumnType[]` | — | Nested columns (grouped header) |
|
|
2871
|
+
| `Cell` | `({ cell }) => ReactNode` | — | Custom renderer. `cell`: `{ value, row, column, grid }`. It fills the cell **edge to edge, with no padding** — the built-in text pads itself `px={3}`, so text of your own does the same. It redraws when its row changes or the grid does — not when another row is selected |
|
|
2872
|
+
| `sortable` / `resizable` | `boolean` | inherits | Override grid-level setting |
|
|
2873
|
+
| `flexible` | `boolean` | `true` | Participate in flex width distribution |
|
|
2874
|
+
| `filterable` | `boolean \| FilterConfig` | — | `true` (text), `{ type: 'number', min?, max? }`, `{ type: 'multiselect', options? }` |
|
|
2875
|
+
| `contextMenu` | `boolean \| ContextMenuConfig` | inherits | Override grid-level context menu. `false` hides entirely. `{ sort?, pin?, group? }` controls sections |
|
|
2876
|
+
| `aggregate` | `AggregateName \| (values, rows) => value` | — | Total this column over each group row, and over the footer. See below |
|
|
2877
|
+
| `AggregateCell` | `({ cell }) => ReactNode` | — | Renders the aggregate. `cell`: `{ value, column, rows, scope }`, `scope` being `'group'` or `'footer'` |
|
|
2878
|
+
| `editable` | `boolean \| (row) => boolean` | inherits | Whether this column’s cells can be edited — every row, or the rows a predicate says |
|
|
2879
|
+
| `editor` | `CellEditorType \| CellEditorConfig` | inferred | `text`/`number`/`checkbox`/`select`. Without one it is read off the value in the cell |
|
|
2880
|
+
| `EditCell` | `({ cell }) => ReactNode` | — | An editor of your own, bound to `cell.draft` with `cell.commitEdit()`/`cell.cancelEdit()` |
|
|
2881
|
+
| `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 |
|
|
2882
|
+
| `list` | `'title' \| 'kicker' \| 'subtitle' \| 'end' \| 'field' \| 'detail' \| 'hidden'` | inferred | Where the column goes in a list item, on a phone |
|
|
2883
|
+
| `exportFormat` | `string` | — | The number format its cells wear in a workbook, `'#,##0.00'` or `'yyyy-mm-dd'` |
|
|
2814
2884
|
|
|
2815
2885
|
### Aggregation
|
|
2816
2886
|
|
|
@@ -3786,15 +3856,16 @@ with `language` and `code`, `rule`, `image`, `tableWrap`, `table`, `row`, `cell`
|
|
|
3786
3856
|
|
|
3787
3857
|
Where content will be, while it is being fetched.
|
|
3788
3858
|
|
|
3789
|
-
| Prop | Type | What it does
|
|
3790
|
-
| -------- | ----------- |
|
|
3791
|
-
| `lines` | `number` | How many bars. Default 1; past that the last one is short.
|
|
3792
|
-
| `circle` | `boolean` | One round bar — an avatar. `width` sets its size
|
|
3793
|
-
| `label` | `ReactNode` | Makes it a `role="status"` naming what is on its way. Absent: `aria-hidden`.
|
|
3859
|
+
| Prop | Type | What it does |
|
|
3860
|
+
| -------- | ----------- | ---------------------------------------------------------------------------------------- |
|
|
3861
|
+
| `lines` | `number` | How many bars. Default 1, which fills the skeleton; past that the last one is short. |
|
|
3862
|
+
| `circle` | `boolean` | One round bar — an avatar. `width` sets its size (40px without one), the height follows. |
|
|
3863
|
+
| `label` | `ReactNode` | Makes it a `role="status"` naming what is on its way. Absent: `aria-hidden`. |
|
|
3794
3864
|
|
|
3795
3865
|
```tsx
|
|
3796
3866
|
<Skeleton lines={3} label="Loading orders" />
|
|
3797
3867
|
<Skeleton circle width={10} />
|
|
3868
|
+
<Skeleton height={40} /> {/* one bar fills it: an image or a chart placeholder */}
|
|
3798
3869
|
```
|
|
3799
3870
|
|
|
3800
3871
|
A `label` belongs on the one skeleton standing for a region and not on each bar — a screen of
|
|
@@ -3802,7 +3873,7 @@ placeholders each announcing itself is a screen nobody can listen to, and a regi
|
|
|
3802
3873
|
with its text is not reliably announced anyway, so what it really buys is words for somebody who lands
|
|
3803
3874
|
there. The gloss is a named duration in milliseconds, so it sits outside what `--transitionTime` zeroes
|
|
3804
3875
|
and stops itself under `prefers-reduced-motion`. It renders on a server. Tree: `skeleton` (+ `bar` with
|
|
3805
|
-
`short` and `
|
|
3876
|
+
`short`, `circle` and `fill` variants, `gloss`).
|
|
3806
3877
|
|
|
3807
3878
|
## The catalog (`@box-kite/react/catalog`)
|
|
3808
3879
|
|
package/BOX_KITE_AI_CONTEXT.md
CHANGED
|
@@ -660,8 +660,15 @@ const wobble = Box.spring({ stiffness: 120, damping: 8 });
|
|
|
660
660
|
the same commit, with nothing to configure. `Tooltip`, the `Dropdown` popup and the DataGrid column
|
|
661
661
|
menu are all built on it, and each expresses its exit as a `closed` variant in its component styles — plus `up`/`closedUp` on `dropdown.items`, since a popup that opened upward has to collapse upward.
|
|
662
662
|
- **`Box.configure({ transition })`** changes what the base class transitions for the whole engine:
|
|
663
|
-
a group name,
|
|
663
|
+
a group name, a comma list of groups and property names (`'colors, opacity'`), or `false` to declare
|
|
664
|
+
nothing at all and leave transitions entirely to the props. Anything else transitions `all` and warns.
|
|
664
665
|
Call it before the first render.
|
|
666
|
+
- **An inline phrasing element is `inline-block`**, where every other Box is `block`: `a`, `span`, `label`, `time`, `mark`, `abbr`, `code`, `kbd`, `samp`, `var`, `em`,
|
|
667
|
+
`strong`, `small`, `b`, `i`, `u`, `s`, `sub`, `sup`, `q`, `cite`, `dfn`, `data`, `output`, `bdi`, `bdo`. It is
|
|
668
|
+
selected on the rendered element at the base class's specificity, so `<Link>`, `<Box tag="a">` and a
|
|
669
|
+
`useClassNames` class on a router's `<a>` all stay in their sentence, margin and padding still apply, and a
|
|
670
|
+
`display` prop or component still wins. Media (`img`, `svg`, `video`, `canvas`, `iframe`) stays block. An `inline-block` never breaks across lines, so a
|
|
671
|
+
prose link or `<Mark>` that has to wrap with the text sets `display="inline"`.
|
|
665
672
|
|
|
666
673
|
---
|
|
667
674
|
|
|
@@ -1263,7 +1270,7 @@ import Textbox from '@box-kite/react/components/textbox';
|
|
|
1263
1270
|
<Flex d="column" gap={2} md={{ d: 'row', gap: 4 }}>{children}</Flex>
|
|
1264
1271
|
|
|
1265
1272
|
// Grid
|
|
1266
|
-
<Grid
|
|
1273
|
+
<Grid gridTemplateColumns={3} gap={4}>{items.map(i => <Box key={i.id}>{i.content}</Box>)}</Grid>
|
|
1267
1274
|
|
|
1268
1275
|
// Card
|
|
1269
1276
|
<Box p={4} bgColor="white" borderRadius={8} shadow="medium">{content}</Box>
|
|
@@ -1320,6 +1327,11 @@ undefined and rendering it costs nothing, so write the line either way. Pass `{
|
|
|
1320
1327
|
second argument for an element inside an `<svg>`. `<Icon>` is this hook plus that flag — reach for
|
|
1321
1328
|
`Icon` for an icon and this only for everything else.
|
|
1322
1329
|
|
|
1330
|
+
The class carries the base reset, which is `display: block` except on an `<a>`, `<span>`, `<label>` and the
|
|
1331
|
+
other phrasing elements, which are `inline-block`: a `NavLink` styled this way stays in its sentence.
|
|
1332
|
+
`{ base: false }` leaves the reset out — for a library's own root (`<ReactFlow>`, `<MiniMap>`, a `BaseEdge`)
|
|
1333
|
+
whose margin, display and transitions are its own.
|
|
1334
|
+
|
|
1323
1335
|
### Group and Peer
|
|
1324
1336
|
|
|
1325
1337
|
```tsx
|
|
@@ -1456,6 +1468,19 @@ Full reference: `docs/a11y-primitives.md`.
|
|
|
1456
1468
|
|
|
1457
1469
|
---
|
|
1458
1470
|
|
|
1471
|
+
## Field ids, external labels and the invalid state
|
|
1472
|
+
|
|
1473
|
+
- **A top-level `id` lands on the control, once.** `<Combobox id="owner">` puts it on the `<input role="combobox">`
|
|
1474
|
+
and nowhere else, as `Textbox`, `Textarea`, `Select` (its button), `Checkbox`, `Switch`, `RadioButton` and
|
|
1475
|
+
`TimeZoneSelect` do, so `<label htmlFor="owner">` names the control. `DateField`, `DatePicker`, `Slider` and
|
|
1476
|
+
`RadioGroup` are groups no `<label>` can name: the `id` goes on the group, and `labelledBy` points it at an
|
|
1477
|
+
external label.
|
|
1478
|
+
- **`aria-invalid` draws the error border on every field.** `props={{ 'aria-invalid': true }}` turns the frame of a
|
|
1479
|
+
`Textbox`, `Textarea`, `Select` or `Combobox` `red-500`, through hover and focus, in both themes. A combobox's
|
|
1480
|
+
attribute is on its input and its border on the shell, so the shell matches `:has([aria-invalid="true"])` —
|
|
1481
|
+
restyle it as `combobox: { styles: { has: { '[aria-invalid="true"]': { … } } } }`, the others with
|
|
1482
|
+
`ariaAttr: { invalid: { … } }`. `DateField` and `DatePicker` take an `invalid` prop that does both.
|
|
1483
|
+
|
|
1459
1484
|
## Sizes: compact, normal, large
|
|
1460
1485
|
|
|
1461
1486
|
Every built-in component is drawn at one of three sizes, and a size is two CSS variables —
|
|
@@ -1521,16 +1546,16 @@ the association needs no `htmlFor`/`id` pair and the whole row is a click target
|
|
|
1521
1546
|
nothing wraps the input — the markup is what it always was. `labelProps` styles that `<label>`;
|
|
1522
1547
|
every other Box prop styles the control itself.
|
|
1523
1548
|
|
|
1524
|
-
| Component | Prop | What it does
|
|
1525
|
-
| ------------ | ------------------------ |
|
|
1526
|
-
| all three | `label` / `labelProps` | The wrapping `<label>` and its styles.
|
|
1527
|
-
| `Checkbox` | `indeterminate` | Sets the DOM property **and** `aria-checked="mixed"`, which is what is announced.
|
|
1528
|
-
| `Switch` | — | `role="switch"` over the same input; Space **and** Enter toggle, Enter does not submit.
|
|
1529
|
-
| `RadioGroup` | `label` | `role="radiogroup"` named by it — a set of radios with nothing over it is a flat list.
|
|
1530
|
-
| `RadioGroup` | `name` | The field every item submits under. Generated when left out.
|
|
1531
|
-
| `RadioGroup` | `value` / `defaultValue` | The selected item's value (a string — the platform's own model for a radio).
|
|
1532
|
-
| `RadioGroup` | `onValueChange` | `(value, { reason, event })` — `click` or `keyboard`. `onChange` is the deprecated spelling.
|
|
1533
|
-
| `RadioGroup` | `orientation` | `vertical` (default) or `horizontal
|
|
1549
|
+
| Component | Prop | What it does |
|
|
1550
|
+
| ------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1551
|
+
| all three | `label` / `labelProps` | The wrapping `<label>` and its styles. |
|
|
1552
|
+
| `Checkbox` | `indeterminate` | Sets the DOM property **and** `aria-checked="mixed"`, which is what is announced. |
|
|
1553
|
+
| `Switch` | — | `role="switch"` over the same input; Space **and** Enter toggle, Enter does not submit. |
|
|
1554
|
+
| `RadioGroup` | `label` | `role="radiogroup"` named by it — a set of radios with nothing over it is a flat list. |
|
|
1555
|
+
| `RadioGroup` | `name` | The field every item submits under. Generated when left out. |
|
|
1556
|
+
| `RadioGroup` | `value` / `defaultValue` | The selected item's value (a string — the platform's own model for a radio). |
|
|
1557
|
+
| `RadioGroup` | `onValueChange` | `(value, { reason, event })` — `click` or `keyboard`. `onChange` is the deprecated spelling. |
|
|
1558
|
+
| `RadioGroup` | `orientation` | `vertical` (default) or `horizontal`, which wraps and keeps the label on a line of its own above the radios. Both arrow pairs navigate either way. |
|
|
1534
1559
|
|
|
1535
1560
|
Arrow keys move **and select** inside a group, wrapping at both ends and skipping disabled items;
|
|
1536
1561
|
Tab enters the group once and leaves it once, because a native radio set is already a single tab
|
|
@@ -2660,6 +2685,51 @@ function TrendCell({ cell }: { cell: CellModel<Row> }) {
|
|
|
2660
2685
|
<DataGrid data={rows} def={{ rowKey: 'id', columns: [{ key: 'trend', header: 'Last 12 months', width: 180, Cell: TrendCell }] }} />;
|
|
2661
2686
|
```
|
|
2662
2687
|
|
|
2688
|
+
### Theming a React Flow canvas — `FlowContainer`
|
|
2689
|
+
|
|
2690
|
+
`ChartContainer`'s move for diagrams. `@box-kite/react/components/flow` (server-safe, imports nothing from xyflow) is a Box
|
|
2691
|
+
that declares React Flow's `--xy-*` variables from the palette in both themes, so the canvas — background, pattern, edges,
|
|
2692
|
+
handles, built-in nodes, minimap, controls, selection box, edge labels — names no colour and follows the page:
|
|
2693
|
+
|
|
2694
|
+
```tsx
|
|
2695
|
+
import { Background, Controls, MiniMap, ReactFlow } from '@xyflow/react';
|
|
2696
|
+
import '@xyflow/react/dist/style.css';
|
|
2697
|
+
import { FlowContainer, flowColorMode } from '@box-kite/react/components/flow';
|
|
2698
|
+
|
|
2699
|
+
function Canvas() {
|
|
2700
|
+
const [theme] = Box.useTheme();
|
|
2701
|
+
|
|
2702
|
+
return (
|
|
2703
|
+
<FlowContainer height={120}>
|
|
2704
|
+
<ReactFlow nodes={nodes} edges={edges} colorMode={flowColorMode(theme)} fitView>
|
|
2705
|
+
<Background />
|
|
2706
|
+
<Controls />
|
|
2707
|
+
<MiniMap />
|
|
2708
|
+
</ReactFlow>
|
|
2709
|
+
</FlowContainer>
|
|
2710
|
+
);
|
|
2711
|
+
}
|
|
2712
|
+
```
|
|
2713
|
+
|
|
2714
|
+
| Prop | What it is |
|
|
2715
|
+
| -------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
2716
|
+
| `tokens` | Repaints a role: `{ selection: 'emerald-500' }` for both themes, `{ surface: { light: 'base-50', dark: 'base-950' } }` per theme |
|
|
2717
|
+
| `motion` | `'static'` zeroes every Box transition inside the canvas, for nodes moved or resized every frame |
|
|
2718
|
+
|
|
2719
|
+
- **Eight roles**, each a custom property a custom node can read: `--flow-surface`, `--flow-panel`, `--flow-text`, `--flow-border`,
|
|
2720
|
+
`--flow-grid`, `--flow-edge`, `--flow-selection`, `--flow-handle`. Defaults are `base`/`accent`, so `Box.palette()` reaches them.
|
|
2721
|
+
- **The defaults are the `flowContainer` style node**, so one override repaints every canvas in an app:
|
|
2722
|
+
`Box.components({ flowContainer: { styles: { vars: { 'flow-selection': 'emerald-500' } } } })`; `tokens` is the per-canvas answer.
|
|
2723
|
+
- **One `--xy-*` variable is `vars`** (`vars={{ 'xy-edge-stroke-width': '2' }}`); a prop on `<MiniMap>`/`<Background>` still wins.
|
|
2724
|
+
- **`colorMode={flowColorMode(theme)}`** keeps React Flow's own `.dark` class in step with the page. A canvas in another theme sits
|
|
2725
|
+
in `<Box.Theme use="local">`; never `colorMode="light"` under a dark page — `.light` is no theme boundary, so the two mix.
|
|
2726
|
+
- **A custom node is invisible to the keyboard** — xyflow removes every node's focus outline. Spread `flowNodeStyles` on its
|
|
2727
|
+
root (an outline while xyflow's wrapper is `:focus-visible`) and `flowNodeSelectedStyles` when its `selected` prop is true;
|
|
2728
|
+
both are outlines in the `selection` role, so they survive forced colours. A custom edge on `BaseEdge` takes `flowEdgeStyles`
|
|
2729
|
+
through `useClassNames(flowEdgeStyles, { svg: true, base: false })`.
|
|
2730
|
+
- Colour props refuse `var()`, so a custom node reads a role through `css`:
|
|
2731
|
+
`css={{ backgroundColor: 'var(--flow-panel)', borderColor: 'var(--flow-border)', color: 'var(--flow-text)' }}`.
|
|
2732
|
+
|
|
2663
2733
|
---
|
|
2664
2734
|
|
|
2665
2735
|
## Key Reminders for AI Assistants
|
|
@@ -2677,7 +2747,7 @@ function TrendCell({ cell }: { cell: CellModel<Row> }) {
|
|
|
2677
2747
|
11. **Size shortcuts**: `width="fit"` = 100%, `width="fit-screen"` = 100vw, `width="1/2"` = 50%
|
|
2678
2748
|
12. **Box is memoized** with `React.memo`, and so are `Flex`, `H1`, `Button` and the rest. A component of your own that is a Box with defaults calls `useBoxElement({ p: 4, ...props }, ref)` from its own `forwardRef` rather than returning `<Box>` — one fiber instead of two
|
|
2679
2749
|
13. **`style` is top-level only** — never inside breakpoints (`sm={{ style: ... }}`), pseudo-classes, or theme objects. `css` is the one that nests: `sm={{ css: { objectPosition: 'top' } }}` is a rule
|
|
2680
|
-
14. **A CSS variable is a prop**: `vars={{ 'color-x': 'sky-500' }}` declares `--color-x` for everything inside — and a third-party chart goes in `<ChartContainer series={['revenue']}>` so it names no colour at all
|
|
2750
|
+
14. **A CSS variable is a prop**: `vars={{ 'color-x': 'sky-500' }}` declares `--color-x` for everything inside — and a third-party chart goes in `<ChartContainer series={['revenue']}>` so it names no colour at all, and a React Flow canvas goes in `<FlowContainer>` with `colorMode={flowColorMode(theme)}`
|
|
2681
2751
|
15. **All sizing props use divider 4** — `width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight` are NOT direct pixels. `height={10}` = 2.5rem = 40px
|
|
2682
2752
|
16. **A state your own code sets is a nested prop too**: `dataAttr={{ 'state=open': { … } }}` → `[data-state="open"]`, `ariaAttr={{ selected: { … } }}` → `[aria-selected="true"]`, `has={{ ':checked': { … } }}`, `not={{ hover: { … } }}`, `nth={{ odd: { … } }}`. The attribute itself still goes in `props`
|
|
2683
2753
|
17. **Somebody else's state is `group`/`peer`**: `group={{ 'card/hover': { … } }}` → `.card:hover .x`, `peer={{ 'agree/checked': { … } }}` → `.agree:checked ~ .x`. The ancestor or sibling carries the class via `className`; `hoverGroup` and its four siblings are the older spelling
|
|
@@ -2790,27 +2860,27 @@ named ones carry the reason.
|
|
|
2790
2860
|
|
|
2791
2861
|
### ColumnType
|
|
2792
2862
|
|
|
2793
|
-
| Prop | Type | Default | Description
|
|
2794
|
-
| ------------------------ | ------------------------------------------------------------------------------- | --------- |
|
|
2795
|
-
| `key` | `Key` | required | Column identifier (maps to TRow property)
|
|
2796
|
-
| `header` | `string` | — | Header text
|
|
2797
|
-
| `width` | `number` | `200` | Base width in px
|
|
2798
|
-
| `align` | `'start' \| 'end' \| 'center' \| 'left' \| 'right'` | `'left'` | Cell alignment. `start`/`end` follow the reading order; `left`/`right` stay on the screen side they name
|
|
2799
|
-
| `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
|
|
2800
|
-
| `columns` | `ColumnType[]` | — | Nested columns (grouped header)
|
|
2801
|
-
| `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
|
|
2802
|
-
| `sortable` / `resizable` | `boolean` | inherits | Override grid-level setting
|
|
2803
|
-
| `flexible` | `boolean` | `true` | Participate in flex width distribution
|
|
2804
|
-
| `filterable` | `boolean \| FilterConfig` | — | `true` (text), `{ type: 'number', min?, max? }`, `{ type: 'multiselect', options? }`
|
|
2805
|
-
| `contextMenu` | `boolean \| ContextMenuConfig` | inherits | Override grid-level context menu. `false` hides entirely. `{ sort?, pin?, group? }` controls sections
|
|
2806
|
-
| `aggregate` | `AggregateName \| (values, rows) => value` | — | Total this column over each group row, and over the footer. See below
|
|
2807
|
-
| `AggregateCell` | `({ cell }) => ReactNode` | — | Renders the aggregate. `cell`: `{ value, column, rows, scope }`, `scope` being `'group'` or `'footer'`
|
|
2808
|
-
| `editable` | `boolean \| (row) => boolean` | inherits | Whether this column’s cells can be edited — every row, or the rows a predicate says
|
|
2809
|
-
| `editor` | `CellEditorType \| CellEditorConfig` | inferred | `text`/`number`/`checkbox`/`select`. Without one it is read off the value in the cell
|
|
2810
|
-
| `EditCell` | `({ cell }) => ReactNode` | — | An editor of your own, bound to `cell.draft` with `cell.commitEdit()`/`cell.cancelEdit()`
|
|
2811
|
-
| `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
|
|
2812
|
-
| `list` | `'title' \| 'kicker' \| 'subtitle' \| 'end' \| 'field' \| 'detail' \| 'hidden'` | inferred | Where the column goes in a list item, on a phone
|
|
2813
|
-
| `exportFormat` | `string` | — | The number format its cells wear in a workbook, `'#,##0.00'` or `'yyyy-mm-dd'`
|
|
2863
|
+
| Prop | Type | Default | Description |
|
|
2864
|
+
| ------------------------ | ------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2865
|
+
| `key` | `Key` | required | Column identifier (maps to TRow property) |
|
|
2866
|
+
| `header` | `string` | — | Header text |
|
|
2867
|
+
| `width` | `number` | `200` | Base width in px |
|
|
2868
|
+
| `align` | `'start' \| 'end' \| 'center' \| 'left' \| 'right'` | `'left'` | Cell alignment. `start`/`end` follow the reading order; `left`/`right` stay on the screen side they name |
|
|
2869
|
+
| `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 |
|
|
2870
|
+
| `columns` | `ColumnType[]` | — | Nested columns (grouped header) |
|
|
2871
|
+
| `Cell` | `({ cell }) => ReactNode` | — | Custom renderer. `cell`: `{ value, row, column, grid }`. It fills the cell **edge to edge, with no padding** — the built-in text pads itself `px={3}`, so text of your own does the same. It redraws when its row changes or the grid does — not when another row is selected |
|
|
2872
|
+
| `sortable` / `resizable` | `boolean` | inherits | Override grid-level setting |
|
|
2873
|
+
| `flexible` | `boolean` | `true` | Participate in flex width distribution |
|
|
2874
|
+
| `filterable` | `boolean \| FilterConfig` | — | `true` (text), `{ type: 'number', min?, max? }`, `{ type: 'multiselect', options? }` |
|
|
2875
|
+
| `contextMenu` | `boolean \| ContextMenuConfig` | inherits | Override grid-level context menu. `false` hides entirely. `{ sort?, pin?, group? }` controls sections |
|
|
2876
|
+
| `aggregate` | `AggregateName \| (values, rows) => value` | — | Total this column over each group row, and over the footer. See below |
|
|
2877
|
+
| `AggregateCell` | `({ cell }) => ReactNode` | — | Renders the aggregate. `cell`: `{ value, column, rows, scope }`, `scope` being `'group'` or `'footer'` |
|
|
2878
|
+
| `editable` | `boolean \| (row) => boolean` | inherits | Whether this column’s cells can be edited — every row, or the rows a predicate says |
|
|
2879
|
+
| `editor` | `CellEditorType \| CellEditorConfig` | inferred | `text`/`number`/`checkbox`/`select`. Without one it is read off the value in the cell |
|
|
2880
|
+
| `EditCell` | `({ cell }) => ReactNode` | — | An editor of your own, bound to `cell.draft` with `cell.commitEdit()`/`cell.cancelEdit()` |
|
|
2881
|
+
| `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 |
|
|
2882
|
+
| `list` | `'title' \| 'kicker' \| 'subtitle' \| 'end' \| 'field' \| 'detail' \| 'hidden'` | inferred | Where the column goes in a list item, on a phone |
|
|
2883
|
+
| `exportFormat` | `string` | — | The number format its cells wear in a workbook, `'#,##0.00'` or `'yyyy-mm-dd'` |
|
|
2814
2884
|
|
|
2815
2885
|
### Aggregation
|
|
2816
2886
|
|
|
@@ -3786,15 +3856,16 @@ with `language` and `code`, `rule`, `image`, `tableWrap`, `table`, `row`, `cell`
|
|
|
3786
3856
|
|
|
3787
3857
|
Where content will be, while it is being fetched.
|
|
3788
3858
|
|
|
3789
|
-
| Prop | Type | What it does
|
|
3790
|
-
| -------- | ----------- |
|
|
3791
|
-
| `lines` | `number` | How many bars. Default 1; past that the last one is short.
|
|
3792
|
-
| `circle` | `boolean` | One round bar — an avatar. `width` sets its size
|
|
3793
|
-
| `label` | `ReactNode` | Makes it a `role="status"` naming what is on its way. Absent: `aria-hidden`.
|
|
3859
|
+
| Prop | Type | What it does |
|
|
3860
|
+
| -------- | ----------- | ---------------------------------------------------------------------------------------- |
|
|
3861
|
+
| `lines` | `number` | How many bars. Default 1, which fills the skeleton; past that the last one is short. |
|
|
3862
|
+
| `circle` | `boolean` | One round bar — an avatar. `width` sets its size (40px without one), the height follows. |
|
|
3863
|
+
| `label` | `ReactNode` | Makes it a `role="status"` naming what is on its way. Absent: `aria-hidden`. |
|
|
3794
3864
|
|
|
3795
3865
|
```tsx
|
|
3796
3866
|
<Skeleton lines={3} label="Loading orders" />
|
|
3797
3867
|
<Skeleton circle width={10} />
|
|
3868
|
+
<Skeleton height={40} /> {/* one bar fills it: an image or a chart placeholder */}
|
|
3798
3869
|
```
|
|
3799
3870
|
|
|
3800
3871
|
A `label` belongs on the one skeleton standing for a region and not on each bar — a screen of
|
|
@@ -3802,7 +3873,7 @@ placeholders each announcing itself is a screen nobody can listen to, and a regi
|
|
|
3802
3873
|
with its text is not reliably announced anyway, so what it really buys is words for somebody who lands
|
|
3803
3874
|
there. The gloss is a named duration in milliseconds, so it sits outside what `--transitionTime` zeroes
|
|
3804
3875
|
and stops itself under `prefers-reduced-motion`. It renders on a server. Tree: `skeleton` (+ `bar` with
|
|
3805
|
-
`short` and `
|
|
3876
|
+
`short`, `circle` and `fill` variants, `gloss`).
|
|
3806
3877
|
|
|
3807
3878
|
## The catalog (`@box-kite/react/catalog`)
|
|
3808
3879
|
|