@godxjp/ui 31.0.4 → 31.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/START-HERE.md +2 -2
- package/agent/components/DataTable.json +1 -1
- package/agent/components/ListRow.json +1 -0
- package/agent/components.json +2 -1
- package/agent/index.json +3 -3
- package/agent/llms.txt +4 -4
- package/dist/components/charts/chart-cartesian.d.ts +2 -1
- package/dist/components/charts/chart-cartesian.js +14 -4
- package/dist/components/data-display/data-table.js +2 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/props/components/charts.prop.d.ts +8 -0
- package/dist/props/vocabulary/data.prop.d.ts +8 -0
- package/dist/styles/chart-layout.css +4 -0
- package/dist/styles/layers.json +1 -1
- package/dist/styles/table-layout.css +6 -2
- package/docs/data-display/data-table/examples/selectable-inbox.tsx +113 -0
- package/docs/data-display/data-table/index.md +2 -0
- package/package.json +2 -2
package/agent/START-HERE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
You are about to write code against a design system you did not author. This file is the whole
|
|
4
4
|
contract. Read it before you write JSX.
|
|
5
5
|
|
|
6
|
-
**This catalog describes `@godxjp/ui` 31.0
|
|
6
|
+
**This catalog describes `@godxjp/ui` 31.2.0.** If the project you are editing has a different
|
|
7
7
|
version in its `package.json`, read the pinned catalog for THAT version instead
|
|
8
8
|
(`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
|
|
9
9
|
not exist yet; older, and it hides props that do. Neither failure announces itself.
|
|
@@ -52,7 +52,7 @@ Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `li
|
|
|
52
52
|
names that **do not exist** and map to it — `Combobox`, `Autocomplete`, `CountrySelect` and
|
|
53
53
|
`SearchSelect` are all `Select`. If you are about to hand-roll something, search this field
|
|
54
54
|
first; it exists because that is the mistake.
|
|
55
|
-
2. `components/<Name>.json` — one file per component (1 KB–
|
|
55
|
+
2. `components/<Name>.json` — one file per component (1 KB–35 KB, median 6 KB), carrying its props,
|
|
56
56
|
its `importPath`, and its examples. Fetch only the handful you picked in step 1.
|
|
57
57
|
3. `rules.json` — 50 cardinal rules. The ones about raw HTML and hardcoded colour are not
|
|
58
58
|
style advice.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"type": "T[]"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
"description": "Lean column definitions (adapted to TanStack internally — `meta.lean` is the declared home for every custom column option, so `priority` needs no second TanStack channel). Each column: { key: string; header: ReactNode; ariaLabel?: string; render?: (row: T) => ReactNode; sortable?: boolean; width?: string; align?: 'left'|'center'|'right'; hideBelow?: 'sm'|'md'|'lg'|'xl' (same contract as Flex hideBelow — stamped as data-hide-below on th/td); hiddenOnMobile?: boolean (alias for hideBelow:'md'); enableHiding?: boolean; pin?: 'end'; priority?: 'primary'|'secondary'|'meta'|'actions' }. priority is the column-priority contract read by preset=\"action-collection\" — DataTable stamps it as data-priority on the <th> AND every <td> of the column, so the preset can allocate the narrow-frame measure; leave the free-text column unmarked (it takes the remaining space), and prefer priority over width under the preset because an explicit width utility wins the cascade and defeats the measure. If render is omitted, the raw value at row[key] is rendered as a string. sortable opts the column into the sort cycle (client-side by default, or server-side via sort+onSortChange). enableHiding (default true) lists the column in DataTable.ViewOptions; set false to keep a key/actions column always visible. pin:'end' sticks the column (typically row actions) to the inline-end edge on horizontal scroll with a separating shadow — pin at most one column. ariaLabel gives a VISUALLY-EMPTY header (header='' — an action or selection column) a screen-reader name (e.g. 'Actions'/'Select'): it renders as an sr-only label inside the <th> so the column is never nameless (axe: empty-table-header). ANT DESIGN PARITY on the same column: fixed:'start'|'end' freezes the column against a scroll edge (logical, so it mirrors in RTL; the stacking offsets are MEASURED from the rendered header, so several adjacent frozen columns are correct at any width — pin:'end' is the older spelling of fixed:'end'). ellipsis holds the cell to one line and keeps the full value as its title (it also switches the table to table-layout: fixed, without which no ellipsis truncates anything). sorter is antd's richer `sortable`: true | (a, b) => number | { compare, multiple }, where multiple is the MULTI-column sort priority (highest sorts first). sortOrder / defaultSortOrder / sortDirections control and shape the cycle per column, and showSorterTooltip explains the next step. filters + onFilter + filteredValue / defaultFilteredValue / filterMultiple add a real filter menu to the header (filterMultiple: false makes it single-choice); omit onFilter for a server filter and drive it from the table's onFilterChange.",
|
|
17
|
+
"description": "Lean column definitions (adapted to TanStack internally — `meta.lean` is the declared home for every custom column option, so `priority` needs no second TanStack channel). Each column: { key: string; header: ReactNode; ariaLabel?: string; render?: (row: T) => ReactNode; sortable?: boolean; width?: string; align?: 'left'|'center'|'right'; hideBelow?: 'sm'|'md'|'lg'|'xl' (same contract as Flex hideBelow — stamped as data-hide-below on th/td); hiddenOnMobile?: boolean (alias for hideBelow:'md'); enableHiding?: boolean; pin?: 'end'; priority?: 'primary'|'secondary'|'meta'|'actions'; flush?: boolean }. flush (gh#1016 — the same word as TableCell flush / CardContent flush) drops the column's BODY cell padding so a self-padded row primitive owns the inset: a selectable list whose single column renders a `ListRow` writes `{ key: 'row', header: '', ariaLabel: …, flush: true, render: (m) => <ListRow asChild …><a/></ListRow> }` — the ListRow then sits 16px from the checkbox (not 32px) and the row is ListRow's own height; an unread ListRow in a flush cell paints `--list-row-unread-background` across the whole table row (checkbox cell included). The header keeps its padding, so the header label sits on the ListRow's inset. Never zero the cell with a className instead. priority is the column-priority contract read by preset=\"action-collection\" — DataTable stamps it as data-priority on the <th> AND every <td> of the column, so the preset can allocate the narrow-frame measure; leave the free-text column unmarked (it takes the remaining space), and prefer priority over width under the preset because an explicit width utility wins the cascade and defeats the measure. If render is omitted, the raw value at row[key] is rendered as a string. sortable opts the column into the sort cycle (client-side by default, or server-side via sort+onSortChange). enableHiding (default true) lists the column in DataTable.ViewOptions; set false to keep a key/actions column always visible. pin:'end' sticks the column (typically row actions) to the inline-end edge on horizontal scroll with a separating shadow — pin at most one column. ariaLabel gives a VISUALLY-EMPTY header (header='' — an action or selection column) a screen-reader name (e.g. 'Actions'/'Select'): it renders as an sr-only label inside the <th> so the column is never nameless (axe: empty-table-header). ANT DESIGN PARITY on the same column: fixed:'start'|'end' freezes the column against a scroll edge (logical, so it mirrors in RTL; the stacking offsets are MEASURED from the rendered header, so several adjacent frozen columns are correct at any width — pin:'end' is the older spelling of fixed:'end'). ellipsis holds the cell to one line and keeps the full value as its title (it also switches the table to table-layout: fixed, without which no ellipsis truncates anything). sorter is antd's richer `sortable`: true | (a, b) => number | { compare, multiple }, where multiple is the MULTI-column sort priority (highest sorts first). sortOrder / defaultSortOrder / sortDirections control and shape the cycle per column, and showSorterTooltip explains the next step. filters + onFilter + filteredValue / defaultFilteredValue / filterMultiple add a real filter menu to the header (filterMultiple: false makes it single-choice); omit onFilter for a server filter and drive it from the table's onFilterChange.",
|
|
18
18
|
"name": "columns",
|
|
19
19
|
"required": true,
|
|
20
20
|
"type": "ColumnDef<T>[]"
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"usage": [
|
|
76
76
|
"DO use ListRow for a SHORT (≈2–8 item) list of entities inside a Card where each row is one line with an action — account sessions, API keys, linked identities, passkeys. Stack rows in a `<Card><CardContent flush>` so the rows draw their own quiet dividers edge-to-edge.",
|
|
77
77
|
"DON'T reach for DataTable here — it carries sorting/selection/pagination chrome that a 3-item list doesn't need. DON'T nest a Card per row either (card-in-card). ListRow is the in-between surface.",
|
|
78
|
+
"DO host ListRow in a DataTable column when the list needs ROW SELECTION (a mail/notification inbox with bulk actions) — DataTable is the only primitive that selects rows. Mark that column `flush: true` so the cell drops its own padding and the ListRow owns the inset (gh#1016); without it the cell padding stacks on the row's (a 32px dead gap after the checkbox, taller rows) and the unread band stops at the checkbox cell. DON'T zero the cell with a className.",
|
|
78
79
|
"DO write a list of LINKS as `<Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\">` + `<ListRow as=\"li\" asChild><Link/></ListRow>` — one call gives the list item, the whole-row link and the divider. DON'T wrap the row in your own `<li>` or `role=\"listitem\"` element to get list semantics back: the divider rule reads `:not(:last-child)` among the rows themselves, so a wrapper per row makes each one an only child and EVERY divider disappears — silently, which is why `ui-audit` now flags it as `no-hand-rolled-list`.",
|
|
79
80
|
"DON'T hand-roll `<div className=\"flex items-center justify-between border-b py-3\">` — that is exactly the repeated pattern ListRow replaces (border/radius/padding are tokenized via `--list-row-*`).",
|
|
80
81
|
"DO put the row's action in `trailing` (a `ghost`/`outline` Button, a DropdownMenu trigger, a Switch, or a status Badge). DO pass `as=\"li\"` when the rows live inside a semantic list, and build that list as `<Flex as=\"ul\" marker=\"none\">` — `ui-audit` flags a raw `<ul>`/`role=\"list\"` as `no-hand-rolled-list`.",
|
package/agent/components.json
CHANGED
|
@@ -3380,7 +3380,7 @@
|
|
|
3380
3380
|
"type": "T[]"
|
|
3381
3381
|
},
|
|
3382
3382
|
{
|
|
3383
|
-
"description": "Lean column definitions (adapted to TanStack internally — `meta.lean` is the declared home for every custom column option, so `priority` needs no second TanStack channel). Each column: { key: string; header: ReactNode; ariaLabel?: string; render?: (row: T) => ReactNode; sortable?: boolean; width?: string; align?: 'left'|'center'|'right'; hideBelow?: 'sm'|'md'|'lg'|'xl' (same contract as Flex hideBelow — stamped as data-hide-below on th/td); hiddenOnMobile?: boolean (alias for hideBelow:'md'); enableHiding?: boolean; pin?: 'end'; priority?: 'primary'|'secondary'|'meta'|'actions' }. priority is the column-priority contract read by preset=\"action-collection\" — DataTable stamps it as data-priority on the <th> AND every <td> of the column, so the preset can allocate the narrow-frame measure; leave the free-text column unmarked (it takes the remaining space), and prefer priority over width under the preset because an explicit width utility wins the cascade and defeats the measure. If render is omitted, the raw value at row[key] is rendered as a string. sortable opts the column into the sort cycle (client-side by default, or server-side via sort+onSortChange). enableHiding (default true) lists the column in DataTable.ViewOptions; set false to keep a key/actions column always visible. pin:'end' sticks the column (typically row actions) to the inline-end edge on horizontal scroll with a separating shadow — pin at most one column. ariaLabel gives a VISUALLY-EMPTY header (header='' — an action or selection column) a screen-reader name (e.g. 'Actions'/'Select'): it renders as an sr-only label inside the <th> so the column is never nameless (axe: empty-table-header). ANT DESIGN PARITY on the same column: fixed:'start'|'end' freezes the column against a scroll edge (logical, so it mirrors in RTL; the stacking offsets are MEASURED from the rendered header, so several adjacent frozen columns are correct at any width — pin:'end' is the older spelling of fixed:'end'). ellipsis holds the cell to one line and keeps the full value as its title (it also switches the table to table-layout: fixed, without which no ellipsis truncates anything). sorter is antd's richer `sortable`: true | (a, b) => number | { compare, multiple }, where multiple is the MULTI-column sort priority (highest sorts first). sortOrder / defaultSortOrder / sortDirections control and shape the cycle per column, and showSorterTooltip explains the next step. filters + onFilter + filteredValue / defaultFilteredValue / filterMultiple add a real filter menu to the header (filterMultiple: false makes it single-choice); omit onFilter for a server filter and drive it from the table's onFilterChange.",
|
|
3383
|
+
"description": "Lean column definitions (adapted to TanStack internally — `meta.lean` is the declared home for every custom column option, so `priority` needs no second TanStack channel). Each column: { key: string; header: ReactNode; ariaLabel?: string; render?: (row: T) => ReactNode; sortable?: boolean; width?: string; align?: 'left'|'center'|'right'; hideBelow?: 'sm'|'md'|'lg'|'xl' (same contract as Flex hideBelow — stamped as data-hide-below on th/td); hiddenOnMobile?: boolean (alias for hideBelow:'md'); enableHiding?: boolean; pin?: 'end'; priority?: 'primary'|'secondary'|'meta'|'actions'; flush?: boolean }. flush (gh#1016 — the same word as TableCell flush / CardContent flush) drops the column's BODY cell padding so a self-padded row primitive owns the inset: a selectable list whose single column renders a `ListRow` writes `{ key: 'row', header: '', ariaLabel: …, flush: true, render: (m) => <ListRow asChild …><a/></ListRow> }` — the ListRow then sits 16px from the checkbox (not 32px) and the row is ListRow's own height; an unread ListRow in a flush cell paints `--list-row-unread-background` across the whole table row (checkbox cell included). The header keeps its padding, so the header label sits on the ListRow's inset. Never zero the cell with a className instead. priority is the column-priority contract read by preset=\"action-collection\" — DataTable stamps it as data-priority on the <th> AND every <td> of the column, so the preset can allocate the narrow-frame measure; leave the free-text column unmarked (it takes the remaining space), and prefer priority over width under the preset because an explicit width utility wins the cascade and defeats the measure. If render is omitted, the raw value at row[key] is rendered as a string. sortable opts the column into the sort cycle (client-side by default, or server-side via sort+onSortChange). enableHiding (default true) lists the column in DataTable.ViewOptions; set false to keep a key/actions column always visible. pin:'end' sticks the column (typically row actions) to the inline-end edge on horizontal scroll with a separating shadow — pin at most one column. ariaLabel gives a VISUALLY-EMPTY header (header='' — an action or selection column) a screen-reader name (e.g. 'Actions'/'Select'): it renders as an sr-only label inside the <th> so the column is never nameless (axe: empty-table-header). ANT DESIGN PARITY on the same column: fixed:'start'|'end' freezes the column against a scroll edge (logical, so it mirrors in RTL; the stacking offsets are MEASURED from the rendered header, so several adjacent frozen columns are correct at any width — pin:'end' is the older spelling of fixed:'end'). ellipsis holds the cell to one line and keeps the full value as its title (it also switches the table to table-layout: fixed, without which no ellipsis truncates anything). sorter is antd's richer `sortable`: true | (a, b) => number | { compare, multiple }, where multiple is the MULTI-column sort priority (highest sorts first). sortOrder / defaultSortOrder / sortDirections control and shape the cycle per column, and showSorterTooltip explains the next step. filters + onFilter + filteredValue / defaultFilteredValue / filterMultiple add a real filter menu to the header (filterMultiple: false makes it single-choice); omit onFilter for a server filter and drive it from the table's onFilterChange.",
|
|
3384
3384
|
"name": "columns",
|
|
3385
3385
|
"required": true,
|
|
3386
3386
|
"type": "ColumnDef<T>[]"
|
|
@@ -4386,6 +4386,7 @@
|
|
|
4386
4386
|
"usage": [
|
|
4387
4387
|
"DO use ListRow for a SHORT (≈2–8 item) list of entities inside a Card where each row is one line with an action — account sessions, API keys, linked identities, passkeys. Stack rows in a `<Card><CardContent flush>` so the rows draw their own quiet dividers edge-to-edge.",
|
|
4388
4388
|
"DON'T reach for DataTable here — it carries sorting/selection/pagination chrome that a 3-item list doesn't need. DON'T nest a Card per row either (card-in-card). ListRow is the in-between surface.",
|
|
4389
|
+
"DO host ListRow in a DataTable column when the list needs ROW SELECTION (a mail/notification inbox with bulk actions) — DataTable is the only primitive that selects rows. Mark that column `flush: true` so the cell drops its own padding and the ListRow owns the inset (gh#1016); without it the cell padding stacks on the row's (a 32px dead gap after the checkbox, taller rows) and the unread band stops at the checkbox cell. DON'T zero the cell with a className.",
|
|
4389
4390
|
"DO write a list of LINKS as `<Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\">` + `<ListRow as=\"li\" asChild><Link/></ListRow>` — one call gives the list item, the whole-row link and the divider. DON'T wrap the row in your own `<li>` or `role=\"listitem\"` element to get list semantics back: the divider rule reads `:not(:last-child)` among the rows themselves, so a wrapper per row makes each one an only child and EVERY divider disappears — silently, which is why `ui-audit` now flags it as `no-hand-rolled-list`.",
|
|
4390
4391
|
"DON'T hand-roll `<div className=\"flex items-center justify-between border-b py-3\">` — that is exactly the repeated pattern ListRow replaces (border/radius/padding are tokenized via `--list-row-*`).",
|
|
4391
4392
|
"DO put the row's action in `trailing` (a `ghost`/`outline` Button, a DropdownMenu trigger, a Switch, or a status Badge). DO pass `as=\"li\"` when the rows live inside a semantic list, and build that list as `<Flex as=\"ul\" marker=\"none\">` — `ui-audit` flags a raw `<ul>`/`role=\"list\"` as `no-hand-rolled-list`.",
|
package/agent/index.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"file": "components/<Name>.json",
|
|
18
|
-
"note": "One file per component (1 KB–
|
|
18
|
+
"note": "One file per component (1 KB–35 KB, median 6 KB), each carrying its importPath. This is the selective route: read the index, then fetch only what you need instead of the 1.2 MB blob.",
|
|
19
19
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/<Name>.json"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
|
|
49
49
|
"read": {
|
|
50
50
|
"live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
|
|
51
|
-
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.0
|
|
51
|
+
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.2.0/agent/index.json"
|
|
52
52
|
},
|
|
53
53
|
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
|
|
54
54
|
"start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
|
|
63
63
|
"semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
|
|
64
64
|
},
|
|
65
|
-
"version": "31.0
|
|
65
|
+
"version": "31.2.0"
|
|
66
66
|
}
|
package/agent/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @godxjp/ui
|
|
2
2
|
|
|
3
3
|
> A Japanese-enterprise React design system: 175 components, 2074 design tokens,
|
|
4
|
-
> 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.0.
|
|
4
|
+
> 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.2.0.
|
|
5
5
|
|
|
6
6
|
If your client can run a process, do not read these files — run the MCP server instead
|
|
7
|
-
(`npx @godxjp/ui-mcp@31.0
|
|
7
|
+
(`npx @godxjp/ui-mcp@31.2.0`). It is searchable and version-locked. These files exist for agents
|
|
8
8
|
that can only fetch URLs.
|
|
9
9
|
|
|
10
10
|
## Start
|
|
@@ -16,7 +16,7 @@ that can only fetch URLs.
|
|
|
16
16
|
|
|
17
17
|
- [patterns-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/patterns-index.json): 21 whole-task patterns (name, tagline, tags). Start here when the task is a TASK — "build a settings page" — then fetch `patterns/<name>.json` for complete code.
|
|
18
18
|
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 46 KB — all 175 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
|
|
19
|
-
- [components/<Name>.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–
|
|
19
|
+
- [components/<Name>.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–35 KB, median 6 KB). Read the index, then fetch only the ones you chose — this is the selective route, and the reason you do not need the blob.
|
|
20
20
|
- [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.2 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
|
|
21
21
|
- [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 103 `semantic` roles, 1760 `component` knobs.
|
|
22
22
|
- [vocabulary.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json): the controlled prop vocabulary — which prop name means what, across every component.
|
|
@@ -26,7 +26,7 @@ that can only fetch URLs.
|
|
|
26
26
|
## Pinning
|
|
27
27
|
|
|
28
28
|
Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
|
|
29
|
-
the tag: `.../godx-jp/godxjp-ui/v31.0
|
|
29
|
+
the tag: `.../godx-jp/godxjp-ui/v31.2.0/agent/...`. A catalog that does not match the installed
|
|
30
30
|
package describes props that are absent, or hides props that are present, and says nothing either way.
|
|
31
31
|
|
|
32
32
|
Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
|
|
@@ -21,6 +21,7 @@ type CartesianChartProps = {
|
|
|
21
21
|
valueTicks?: number[];
|
|
22
22
|
numberFormat?: Intl.NumberFormatOptions;
|
|
23
23
|
emptyMessage?: string;
|
|
24
|
+
onCategoryClick?: (datum: ChartDatum, index: number) => void;
|
|
24
25
|
className?: string;
|
|
25
26
|
id?: string;
|
|
26
27
|
/** line/area */
|
|
@@ -32,5 +33,5 @@ type CartesianChartProps = {
|
|
|
32
33
|
/** bar */
|
|
33
34
|
horizontal?: boolean;
|
|
34
35
|
};
|
|
35
|
-
export declare function CartesianChart({ kind, data, series, categoryKey, label, showCaption, description, size, height, showLegend, showGrid, valueDomain, valueTicks, numberFormat, emptyMessage, className, id, curved, showDots, stacked, horizontal, }: CartesianChartProps): React.JSX.Element;
|
|
36
|
+
export declare function CartesianChart({ kind, data, series, categoryKey, label, showCaption, description, size, height, showLegend, showGrid, valueDomain, valueTicks, numberFormat, emptyMessage, onCategoryClick, className, id, curved, showDots, stacked, horizontal, }: CartesianChartProps): React.JSX.Element;
|
|
36
37
|
export {};
|
|
@@ -39,6 +39,7 @@ function CartesianChart({
|
|
|
39
39
|
valueTicks,
|
|
40
40
|
numberFormat,
|
|
41
41
|
emptyMessage,
|
|
42
|
+
onCategoryClick,
|
|
42
43
|
className,
|
|
43
44
|
id,
|
|
44
45
|
curved = false,
|
|
@@ -151,9 +152,16 @@ function CartesianChart({
|
|
|
151
152
|
/* @__PURE__ */ jsx(Tooltip, { formatter: (value) => fmt.format(Number(value)) }),
|
|
152
153
|
showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null
|
|
153
154
|
] });
|
|
155
|
+
const pickCategory = (raw) => {
|
|
156
|
+
const index = raw === null || raw === void 0 || raw === "" ? NaN : Number(raw);
|
|
157
|
+
if (onCategoryClick && Number.isInteger(index) && data[index])
|
|
158
|
+
onCategoryClick(data[index], index);
|
|
159
|
+
};
|
|
160
|
+
const onChartClick = onCategoryClick ? (state) => pickCategory(state?.activeTooltipIndex) : void 0;
|
|
161
|
+
const onBarClick = onCategoryClick ? (_entry, index) => pickCategory(index) : void 0;
|
|
154
162
|
const chart = () => {
|
|
155
163
|
if (kind === "line") {
|
|
156
|
-
return /* @__PURE__ */ jsxs(RLineChart, { data, children: [
|
|
164
|
+
return /* @__PURE__ */ jsxs(RLineChart, { data, onClick: onChartClick, children: [
|
|
157
165
|
axes(true),
|
|
158
166
|
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
159
167
|
Line,
|
|
@@ -169,7 +177,7 @@ function CartesianChart({
|
|
|
169
177
|
] });
|
|
170
178
|
}
|
|
171
179
|
if (kind === "area") {
|
|
172
|
-
return /* @__PURE__ */ jsxs(RAreaChart, { data, children: [
|
|
180
|
+
return /* @__PURE__ */ jsxs(RAreaChart, { data, onClick: onChartClick, children: [
|
|
173
181
|
axes(true),
|
|
174
182
|
series.map((s, i) => /* @__PURE__ */ jsx(
|
|
175
183
|
Area,
|
|
@@ -195,7 +203,9 @@ function CartesianChart({
|
|
|
195
203
|
name: s.label ?? s.dataKey,
|
|
196
204
|
stackId: stacked ? "stack" : void 0,
|
|
197
205
|
fill: chartColor(i, s.color),
|
|
198
|
-
radius: 2
|
|
206
|
+
radius: 2,
|
|
207
|
+
onClick: onBarClick,
|
|
208
|
+
background: onCategoryClick ? { fill: "transparent" } : void 0
|
|
199
209
|
},
|
|
200
210
|
s.dataKey
|
|
201
211
|
))
|
|
@@ -211,7 +221,7 @@ function CartesianChart({
|
|
|
211
221
|
imgSummary: summary.img,
|
|
212
222
|
hasData,
|
|
213
223
|
height: resolvedHeight,
|
|
214
|
-
className,
|
|
224
|
+
className: cn(onCategoryClick && "ui-chart-clickable", className),
|
|
215
225
|
id,
|
|
216
226
|
canvasRef,
|
|
217
227
|
canvasExtra: measuresCategoryAxis ? (
|
|
@@ -1296,7 +1296,8 @@ DataTable.Content = function DataTableContent() {
|
|
|
1296
1296
|
...fixedCellProps(col.key, fixedEdge(col)),
|
|
1297
1297
|
...columnHideBelowProps(col),
|
|
1298
1298
|
style: columnCellStyle(col),
|
|
1299
|
-
|
|
1299
|
+
flush: col.flush,
|
|
1300
|
+
className: cn(!col.flush && cellPadding, columnCellClass(col)),
|
|
1300
1301
|
children: rendered
|
|
1301
1302
|
},
|
|
1302
1303
|
col.key
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "31.0
|
|
3
|
+
"version": "31.2.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -69,6 +69,14 @@ type ChartCartesianBase = {
|
|
|
69
69
|
numberFormat?: Intl.NumberFormatOptions;
|
|
70
70
|
/** Message shown when `data` is empty. Defaults to a localized "no data". */
|
|
71
71
|
emptyMessage?: EmptyMessageProp;
|
|
72
|
+
/**
|
|
73
|
+
* Called with the clicked category's datum and its index — drill-down, zoom-to-bucket. The whole
|
|
74
|
+
* category column is the target, not only the mark, so a zero-height bar is still clickable.
|
|
75
|
+
*
|
|
76
|
+
* A POINTER SHORTCUT, not the only way in: the plot is `role="img"`, which has no keyboard
|
|
77
|
+
* interaction, so the screen must offer the same action through a real control as well.
|
|
78
|
+
*/
|
|
79
|
+
onCategoryClick?: (datum: ChartDatum, index: number) => void;
|
|
72
80
|
className?: ClassNameProp;
|
|
73
81
|
id?: IdProp;
|
|
74
82
|
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type * as React from "react";
|
|
6
6
|
import type { BreakpointProp, ColumnAlignProp, SortDirectionProp } from "./interaction.prop.js";
|
|
7
7
|
import type { TableDensityProp } from "./layout.prop.js";
|
|
8
|
+
import type { FlushProp } from "./shared.prop.js";
|
|
8
9
|
/** Generic row identifier extractor for tables with selection. */
|
|
9
10
|
export type GetRowIdProp<T> = (row: T) => string;
|
|
10
11
|
/**
|
|
@@ -82,6 +83,13 @@ export type ColumnDefProp<T> = {
|
|
|
82
83
|
* is kept reachable as the cell's `title`, so nothing is silently lost.
|
|
83
84
|
*/
|
|
84
85
|
ellipsis?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The cell's CONTENT owns its inset (the same contract as `TableCell flush`): the column's body
|
|
88
|
+
* cells drop their padding so a self-padded row primitive — a `ListRow` in a selectable list —
|
|
89
|
+
* sits on the column's text axis instead of stacking a second inset on the cell's (gh#1016).
|
|
90
|
+
* The header cell keeps its padding, which lines the header label up with the row's own inset.
|
|
91
|
+
*/
|
|
92
|
+
flush?: FlushProp;
|
|
85
93
|
/**
|
|
86
94
|
* Sort declaration (antd `ColumnType.sorter`). `sorter: true` is the same opt-in as `sortable`;
|
|
87
95
|
* a comparator sorts by it; `{ compare, multiple }` joins the MULTI-column sort, highest
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
min-inline-size: 0;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
.ui-chart-clickable .recharts-surface {
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
.ui-chart .recharts-text,
|
|
24
28
|
.ui-chart .recharts-legend-item-text {
|
|
25
29
|
font-size: var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)));
|
package/dist/styles/layers.json
CHANGED
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
white-space: nowrap;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
[data-slot="table-head"]:has([role="checkbox"]) {
|
|
139
|
+
[data-slot="table-head"]:has([data-slot="checkbox"], [role="checkbox"]) {
|
|
140
140
|
padding-inline-end: 0;
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
white-space: nowrap;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
[data-slot="table-cell"]:has([role="checkbox"]) {
|
|
154
|
+
[data-slot="table-cell"]:has([data-slot="checkbox"], [role="checkbox"]) {
|
|
155
155
|
padding-inline-end: 0;
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -187,6 +187,10 @@
|
|
|
187
187
|
padding: 0;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
.ui-table-row:has(> [data-slot="table-cell"][data-flush] > [data-slot="list-row"][data-unread]) {
|
|
191
|
+
background-color: var(--list-row-unread-background, hsl(var(--muted)));
|
|
192
|
+
}
|
|
193
|
+
|
|
190
194
|
.ui-data-table-toolbar {
|
|
191
195
|
display: flex;
|
|
192
196
|
flex-wrap: wrap;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Avatar,
|
|
5
|
+
AvatarFallback,
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
DataTable,
|
|
11
|
+
ListRow,
|
|
12
|
+
type ColumnDef,
|
|
13
|
+
} from "@godxjp/ui/data-display";
|
|
14
|
+
import { PageContainer } from "@godxjp/ui/layout";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A selectable inbox — `ListRow` hosted in a DataTable column (gh#1016).
|
|
18
|
+
*
|
|
19
|
+
* DataTable is the only primitive that selects rows; ListRow is the canonical entity row. The
|
|
20
|
+
* column is marked `flush: true`, so its body cells drop their own padding and the ListRow owns
|
|
21
|
+
* the inset: the avatar sits 16px from the checkbox (the same step as any other column, not 32px),
|
|
22
|
+
* the row is ListRow's own height, and an unread row's band spans the whole table row — the
|
|
23
|
+
* checkbox cell included. The header cell keeps its padding, so its label sits on the row's inset.
|
|
24
|
+
*/
|
|
25
|
+
type Message = {
|
|
26
|
+
id: string;
|
|
27
|
+
from: string;
|
|
28
|
+
initials: string;
|
|
29
|
+
subject: string;
|
|
30
|
+
read: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const MESSAGES: Message[] = [
|
|
34
|
+
{
|
|
35
|
+
id: "m1",
|
|
36
|
+
from: "Aiko Tanaka",
|
|
37
|
+
initials: "AT",
|
|
38
|
+
subject: "April invoice is ready",
|
|
39
|
+
read: false,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "m2",
|
|
43
|
+
from: "Minh Nguyen",
|
|
44
|
+
initials: "MN",
|
|
45
|
+
subject: "Release notes for 4.2",
|
|
46
|
+
read: false,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "m3",
|
|
50
|
+
from: "Kenji Sato",
|
|
51
|
+
initials: "KS",
|
|
52
|
+
subject: "Meeting minutes",
|
|
53
|
+
read: true,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "m4",
|
|
57
|
+
from: "Lan Pham",
|
|
58
|
+
initials: "LP",
|
|
59
|
+
subject: "Access request approved",
|
|
60
|
+
read: true,
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const columns: ColumnDef<Message>[] = [
|
|
65
|
+
{
|
|
66
|
+
key: "message",
|
|
67
|
+
header: "Message",
|
|
68
|
+
// The cell's CONTENT owns the inset — without it the cell padding stacks on the row's.
|
|
69
|
+
flush: true,
|
|
70
|
+
render: (m) => (
|
|
71
|
+
<ListRow
|
|
72
|
+
asChild
|
|
73
|
+
density="compact"
|
|
74
|
+
unread={!m.read}
|
|
75
|
+
leading={
|
|
76
|
+
<Avatar size="sm">
|
|
77
|
+
<AvatarFallback>{m.initials}</AvatarFallback>
|
|
78
|
+
</Avatar>
|
|
79
|
+
}
|
|
80
|
+
title={m.from}
|
|
81
|
+
description={m.subject}
|
|
82
|
+
>
|
|
83
|
+
<a href={`#${m.id}`} />
|
|
84
|
+
</ListRow>
|
|
85
|
+
),
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
export default function SelectableInboxDemo() {
|
|
90
|
+
const [selected, setSelected] = useState<Set<string>>(new Set(["m2"]));
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<PageContainer title="Inbox" subtitle="DataTable selectable · ListRow in a flush column">
|
|
94
|
+
<Card>
|
|
95
|
+
<CardHeader>
|
|
96
|
+
<CardTitle>Messages</CardTitle>
|
|
97
|
+
</CardHeader>
|
|
98
|
+
<CardContent flush>
|
|
99
|
+
<DataTable
|
|
100
|
+
data={MESSAGES}
|
|
101
|
+
columns={columns}
|
|
102
|
+
getRowId={(m) => m.id}
|
|
103
|
+
getRowLabel={(m) => m.subject}
|
|
104
|
+
selectable
|
|
105
|
+
selected={selected}
|
|
106
|
+
onSelectChange={setSelected}
|
|
107
|
+
density="compact"
|
|
108
|
+
/>
|
|
109
|
+
</CardContent>
|
|
110
|
+
</Card>
|
|
111
|
+
</PageContainer>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -3,3 +3,5 @@
|
|
|
3
3
|
The admin list primitive — sticky header, sorting, bulk selection, density toggle, cursor pagination, and a built-in empty/loading state. Never wrap it in a `data.length === 0` guard; the empty state renders itself. See Examples for a full list-page screen.
|
|
4
4
|
|
|
5
5
|
See the "Approval queue" example.
|
|
6
|
+
|
|
7
|
+
See the "Selectable inbox" example for a `ListRow` hosted in a selectable DataTable: mark that column `flush: true` so the cell drops its own padding and the ListRow owns the inset (gh#1016).
|