@godxjp/ui 33.12.8 → 33.14.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/README.md +8 -4
- package/agent/START-HERE.md +5 -5
- package/agent/components/BranchScopePicker.json +21 -1
- package/agent/components/Breadcrumb.json +8 -2
- package/agent/components/DataTable.json +2 -1
- package/agent/components/MobileShell.json +1 -0
- package/agent/components/PageContainer.json +6 -0
- package/agent/components/Persona.json +66 -0
- package/agent/components/Sidebar.json +1 -0
- package/agent/components/Table.json +1 -1
- package/agent/components-index.json +5 -0
- package/agent/components.json +106 -5
- package/agent/index.json +7 -7
- package/agent/llms.txt +6 -6
- package/agent/tokens.json +62 -2
- package/agent/vocabulary.json +13 -0
- package/dist/components/data-display/data-table.js +51 -10
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/persona.d.ts +11 -0
- package/dist/components/data-display/persona.js +34 -0
- package/dist/components/data-entry/branch-scope-picker.d.ts +3 -0
- package/dist/components/data-entry/branch-scope-picker.js +65 -11
- package/dist/components/layout/breadcrumb-ellipsis.js +33 -1
- package/dist/components/layout/breadcrumb.d.ts +4 -2
- package/dist/components/layout/breadcrumb.js +13 -3
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +5 -3
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +2 -1
- package/dist/i18n/messages/ja.json +13 -12
- package/dist/i18n/messages/vi.json +2 -1
- package/dist/props/components/data-display.prop.d.ts +10 -0
- package/dist/props/components/data-entry.prop.d.ts +6 -0
- package/dist/props/components/layout.prop.d.ts +5 -1
- package/dist/props/registry.d.ts +24 -2
- package/dist/props/registry.js +26 -0
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/navigation.prop.d.ts +2 -0
- package/dist/styles/control.css +29 -3
- package/dist/styles/data-display-layout.css +58 -0
- package/dist/styles/layers.json +8 -2
- package/dist/styles/table-layout.css +32 -12
- package/dist/styles.css +1 -1
- package/dist/tokens/base.css +2 -0
- package/dist/tokens/components/persona.css +13 -0
- package/dist/tokens/components/table.css +1 -1
- package/docs/TOKENS.md +11 -0
- package/package.json +2 -2
- package/scripts/cli.mjs +8 -2
- package/scripts/visual-audit.mjs +119 -27
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ npm i @godxjp/ui
|
|
|
18
18
|
|
|
19
19
|
<!-- agent-catalog-counts:start -->
|
|
20
20
|
|
|
21
|
-
The catalog —
|
|
21
|
+
The catalog — 170 components, 2251 tokens, 44 cardinal rules — is published in two forms from one
|
|
22
22
|
source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
|
|
23
23
|
<!-- agent-catalog-counts:end -->
|
|
24
24
|
|
|
@@ -53,10 +53,10 @@ static files straight from this public repo — no hosting, no deploy step:
|
|
|
53
53
|
| [`agent/llms.txt`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/llms.txt) | 3 KB | the [llms.txt](https://llmstxt.org/) entry point |
|
|
54
54
|
| [`agent/DESIGN.md`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md) | 18 KB | the visual spec: tokens, colour scarcity, admin screen anatomy, do/don't |
|
|
55
55
|
| [`agent/index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json) | 3 KB | manifest: version, counts, every file's URL |
|
|
56
|
-
| [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 53 KB | all
|
|
56
|
+
| [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 53 KB | all 170 as name + group + tagline — **fetch this first** |
|
|
57
57
|
| [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1 KB–37 KB | one file per component, each with its `importPath` — **this is the route to take** |
|
|
58
|
-
| [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.3 MB | all
|
|
59
|
-
| [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) |
|
|
58
|
+
| [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.3 MB | all 170 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
|
|
59
|
+
| [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 860 KB | all 2251 design tokens, their values and why they exist |
|
|
60
60
|
| [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
|
|
61
61
|
| [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 23 KB | the 44 cardinal rules |
|
|
62
62
|
<!-- prettier-ignore-end -->
|
|
@@ -274,8 +274,12 @@ pnpm release --ui <patch|minor|major> --mcp <…|skip> # publish lib + MCP in
|
|
|
274
274
|
node node_modules/@godxjp/ui/scripts/visual-audit.mjs http://localhost:5173 /invoices /settings
|
|
275
275
|
node node_modules/@godxjp/ui/scripts/visual-audit.mjs http://localhost:5173 --format json # machine-readable
|
|
276
276
|
node node_modules/@godxjp/ui/scripts/visual-audit.mjs --strict http://localhost:5173 # CI gate
|
|
277
|
+
# reuse a playwright-core + Chrome you already have, instead of installing the peer:
|
|
278
|
+
node node_modules/@godxjp/ui/scripts/visual-audit.mjs --playwright-module /abs/path/playwright-core --executable-path /path/to/chrome http://localhost:5173
|
|
277
279
|
```
|
|
278
280
|
|
|
281
|
+
The engine is resolved in this order: `--playwright-module` (a package directory or entry file exporting `chromium`; a bad path is an error, never a silent fallback) → the `playwright` peer → `playwright-core`. `--executable-path` is passed to `chromium.launch` and wins over `PLAYWRIGHT_CHROMIUM_EXECUTABLE`. When nothing resolves, the bootstrap error lists every place it looked.
|
|
282
|
+
|
|
279
283
|
**Tested peer range** (pin one of these): `playwright >=1.55 <2` (tested 1.61.1)
|
|
280
284
|
|
|
281
285
|
`--format json` **always** emits valid JSON — even on bootstrap failure — with a `status` (`ok` · `partial` · `error`) that separates **infrastructure errors** (missing peers, page won't load, axe won't inject → `errors[]`, `summary: null`/flagged) from **product findings** (`findings[]`). A tool failure can therefore never be misread as "zero violations". `pnpm check:visual-audit` is the CI smoke test: it serves a fixture page tripping all five rule families and asserts each one fires.
|
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` 33.
|
|
6
|
+
**This catalog describes `@godxjp/ui` 33.14.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 @@ screen anatomy, the visual target the rules below do not give you. Then fetch in
|
|
|
52
52
|
task is a task** — "build a settings page", "confirm a destructive delete", "a list page with
|
|
53
53
|
filters" — start HERE, not at the components. Then fetch `patterns/<name>.json` for complete,
|
|
54
54
|
copy-paste-ready code. A component index answers "does X exist"; it cannot answer "build Y".
|
|
55
|
-
1. `components-index.json` — 53 KB, all
|
|
55
|
+
1. `components-index.json` — 53 KB, all 170 components as name + group +
|
|
56
56
|
tagline. Read this when you already know the SHAPE you need. Each entry may carry `absorbed`:
|
|
57
57
|
names that **do not exist** and map to it — `Combobox`, `Autocomplete`, `CountrySelect` and
|
|
58
58
|
`SearchSelect` are all `Select`. If you are about to hand-roll something, search this field
|
|
@@ -61,10 +61,10 @@ screen anatomy, the visual target the rules below do not give you. Then fetch in
|
|
|
61
61
|
its `importPath`, and its examples. Fetch only the handful you picked in step 1.
|
|
62
62
|
3. `rules.json` — 44 cardinal rules. The ones about raw HTML and hardcoded colour are not
|
|
63
63
|
style advice.
|
|
64
|
-
4. `tokens.json` —
|
|
64
|
+
4. `tokens.json` — 2251 design tokens, each tagged with its `tier`. **If you were handed a
|
|
65
65
|
brand, read the 218 `foundation` entries first** — `--primary`, `--background`,
|
|
66
66
|
`--radius`, `--font-size-base` are the handful everything else derives from. The
|
|
67
|
-
|
|
67
|
+
1920 `component` entries are per-part knobs; reach for one only when a role is
|
|
68
68
|
right everywhere except one component.
|
|
69
69
|
5. `anti-ai-tells.json` — 35 shapes that make generated UI look generated, each with the
|
|
70
70
|
fix. Read before you reach for a gradient hero or a wall of coloured chips.
|
|
@@ -150,7 +150,7 @@ has stopped following the brand.
|
|
|
150
150
|
|---|---|---|---|
|
|
151
151
|
| `foundation` | 218 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** The default `--primary` is a neutral placeholder: set it (`:root { --primary: <H> <S>% <L>%; }`, HSL components, no `hsl()` wrapper) or import a theme preset such as `@godxjp/ui/themes/godx.css` |
|
|
152
152
|
| `semantic` | 113 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
|
|
153
|
-
| `component` |
|
|
153
|
+
| `component` | 1920 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
|
|
154
154
|
|
|
155
155
|
A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
|
|
156
156
|
value, so the real default is computed where the element paints it. Set it and yours wins.
|
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
"importPath": "@godxjp/ui/data-entry",
|
|
6
6
|
"name": "BranchScopePicker",
|
|
7
7
|
"props": [
|
|
8
|
+
{
|
|
9
|
+
"defaultValue": "\"multiple\"",
|
|
10
|
+
"description": "Single compact Select for all/one branch; multiple keeps the existing all/subset editor.",
|
|
11
|
+
"name": "selectionMode",
|
|
12
|
+
"type": "\"single\" | \"multiple\""
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"defaultValue": "true",
|
|
16
|
+
"description": "Allow organization-wide scope. Set false for record fields, then validate exactly one permitted concrete branch on submit. Invalid supplied values are not silently coerced.",
|
|
17
|
+
"name": "allowAll",
|
|
18
|
+
"type": "boolean"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"defaultValue": "false",
|
|
22
|
+
"description": "Hide after a successful read of one enabled branch, except validation errors. Does not emit a change or normalize URL/default values; the app does that.",
|
|
23
|
+
"name": "hideWhenSingle",
|
|
24
|
+
"type": "boolean"
|
|
25
|
+
},
|
|
8
26
|
{
|
|
9
27
|
"description": "Giá trị có kiểm soát: phạm vi đang chọn.",
|
|
10
28
|
"name": "value",
|
|
@@ -70,7 +88,7 @@
|
|
|
70
88
|
"type": "ReactNode"
|
|
71
89
|
},
|
|
72
90
|
{
|
|
73
|
-
"description": "Native form name
|
|
91
|
+
"description": "Native form name: multiple submits the MODE radio; single submits the Select key (`all` or `branch:<id>`). Serialize the public `{ mode, branchIds }` value yourself for domain records; native keys are not branch ownership.",
|
|
74
92
|
"name": "name",
|
|
75
93
|
"type": "string"
|
|
76
94
|
},
|
|
@@ -91,6 +109,8 @@
|
|
|
91
109
|
"storyPath": "data-entry/branch-scope-picker.tsx",
|
|
92
110
|
"tagline": "Canonical scope control: all branches vs an explicit subset.",
|
|
93
111
|
"usage": [
|
|
112
|
+
"Use selectionMode=single in Sidebar action (above navigation) and a Sheet opened from MobileShell tabBar More. These are compositions, not a new switcher. OrgSwitcher changes organization and stays in platform chrome.",
|
|
113
|
+
"The app owns URL parsing, validation against authorized branches, popstate/router updates, and onValueChange navigation. Resolve a single authorized branch in the app before hiding the picker. Viewing scope is not record ownership: record forms use separate state, selectionMode=single and allowAll=false, and validate exactly one permitted id on submit. Future groups need an explicit value contract; do not encode them as branch ids.",
|
|
94
114
|
"DO treat scope as ONE form field: the single { mode, branchIds } value goes through FormField like any other control.",
|
|
95
115
|
"DO import it from @godxjp/ui/data-entry — never compose an ad-hoc radio+checkbox scope block per screen.",
|
|
96
116
|
"DO use `error` ONLY for validation (e.g. mode=selected with zero branches checked); a failed branch fetch is `listError`, a 403 is `denied`.",
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
"name": "Breadcrumb",
|
|
6
6
|
"props": [
|
|
7
7
|
{
|
|
8
|
-
"
|
|
8
|
+
"defaultValue": "\"last\"",
|
|
9
|
+
"description": "last | none. none renders an ancestor-only trail: every item with to stays linked and no default segment has aria-current. Missing paths remain text. menu and itemRender keep owning their segment bodies; custom renderers own their ARIA.",
|
|
10
|
+
"name": "currentItem",
|
|
11
|
+
"type": "BreadcrumbCurrentItemProp"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Array of { label, to?, menu?, ellipsis? } — omit `to` on the last (current) segment. `ellipsis` (`boolean | { tooltip?: ReactNode }`, gh#1046; antd Breadcrumb has none, the shape is antd Typography's) keeps the trail on one line and cuts that segment's label with an ellipsis, with the full label as aria-label and title on the clipped element (no duplicate aria-describedby), showing the full label in a tooltip on hover and on keyboard focus of the crumb link; `{ tooltip: false }` drops the tooltip. Link and current segments only. `--breadcrumb-item-max-inline-size` (default `none`) caps such a segment. `menu` (Ant Design `BreadcrumbItemType.menu`) hangs a sibling picker off that segment: `{ items: { value, label, to?, disabled? }[], onSelect? }`. The segment then renders as a menu button, not a link — an entry with `to` becomes a real anchor inside the menu while keeping `role=\"menuitem\"`.",
|
|
9
15
|
"name": "items",
|
|
10
16
|
"required": true,
|
|
11
17
|
"type": "BreadcrumbItemProp[]"
|
|
@@ -37,7 +43,7 @@
|
|
|
37
43
|
"tagline": "Standalone breadcrumb nav rendering an ordered trail of page segments.",
|
|
38
44
|
"usage": [
|
|
39
45
|
"DO import from `@godxjp/ui/layout` (not from a navigation or general sub-path) and pass a single `items` prop — an ordered array of `{ label, to? }` objects. No children, no sub-components, no render-prop API.",
|
|
40
|
-
"DO omit `to` on the last (current-page) segment — the component automatically renders it as a `<span aria-current=\"page\">` instead of a router `<Link>`.
|
|
46
|
+
"DO omit `to` on the last (current-page) segment — the component automatically renders it as a `<span aria-current=\"page\">` instead of a router `<Link>`. In the default currentItem=\"last\" mode, passing `to` on the last item does NOT make it a link. For ancestor-only trails use currentItem=\"none\" and supply every ancestor destination.",
|
|
41
47
|
"DO pass the Breadcrumb node as a ReactNode to the `breadcrumb` prop of `AppShell` (or `AppShell`) for shell-level breadcrumbs, or to `PageContainer`'s `breadcrumb` prop (which accepts `BreadcrumbItemProp[]` directly — not a ReactNode). When passing to `PageContainer`, pass the raw array; when passing to `AppShell`, wrap it: `breadcrumb={<Breadcrumb items={…} />}`.",
|
|
42
48
|
"DON'T hand-roll a breadcrumb strip (divs with chevrons, anchors, separators) — Breadcrumb ships the `<nav aria-label=\"Breadcrumb\">` + `<ol>` + `aria-hidden` chevrons. Any custom trail is a violation of the no-hand-roll rule and will fail `npm run ui:audit`.",
|
|
43
49
|
"DON'T use Breadcrumb for tab-style or step-style navigation (multi-step forms, wizard progress). Those flows belong to `Steps`. Breadcrumb is strictly a spatial location trail, not a process indicator.",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
"defaultValue": "'default'",
|
|
89
|
-
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: below collapseBelow the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*) under table-layout: fixed, cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a
|
|
89
|
+
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: below collapseBelow the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*) under table-layout: fixed, cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
|
|
90
90
|
"name": "preset",
|
|
91
91
|
"type": "'default' | 'action-collection' | 'stacked-record-collection'"
|
|
92
92
|
},
|
|
@@ -238,6 +238,7 @@
|
|
|
238
238
|
"storyPath": "data-display/data-table/index.tsx",
|
|
239
239
|
"tagline": "The one TanStack-powered compound admin list — sticky header, sorting, global search, column visibility ('set view'), bulk selection, BOTH cursor and numbered pagination, density, and built-in empty/loading states. Keep the SIMPLE `data` + lean `columns` (ColumnDef) API for the common case; opt into the full grid chrome via the compound parts. Internally driven by @tanstack/react-table (a real dependency). Lives on @godxjp/ui/data-display only (it is NOT on the runtime-neutral root/admin barrel because it pulls TanStack). Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout.",
|
|
240
240
|
"usage": [
|
|
241
|
+
"Ellipsis cells expose full rendered text for default/custom renderers, including stacked layout. Renderer-owned descendant titles or aria-describedby tooltips suppress a duplicate cell title (#1406).",
|
|
241
242
|
"DO use `striped` on dense list tables (many columns, a row the eye must follow across the width). To stripe EVERY Table and DataTable in a service, set it ONCE in the theme — `:root { --table-row-striped-alpha: 100%; }` — instead of passing `striped` at each call site; `striped={false}` then opts one table out. Retint with `--table-row-striped-background`, never with a `rowClassName` utility or `:nth-child` page CSS (those count DOM rows, so an expanded detail row shifts every stripe after it).",
|
|
242
243
|
"DO pass loading={isFetching} during data fetches — it renders a loading row in the table body and suppresses the empty state. Never show a spinner outside DataTable while the table is visible.",
|
|
243
244
|
"DO NOT add a data.length===0 conditional around DataTable. When data is empty and loading is false, the built-in EmptyState renders automatically. Pass empty={<EmptyState title='...'/>} only when you need a custom message.",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"storyPath": "layout/mobile-shell.tsx",
|
|
58
58
|
"tagline": "Handheld app shell (gh#354) — status band · app bar · the ONE scroll region · sticky action bar · bottom tab bar, with device safe-area insets and a document that never scrolls.",
|
|
59
59
|
"usage": [
|
|
60
|
+
"Compose a More destination in tabBar with SheetTrigger + Sheet + BranchScopePicker selectionMode=single; see layout/mobile-branch-scope.tsx. MobileShell does not own routes or a built-in More menu.",
|
|
60
61
|
"DO use MobileShell for a HANDHELD app screen — a warehouse/handy terminal, a driver app, a field-work PWA. It is the fourth root shell: AppShell (needs a sidebar) · the auth CenteredShell (unauthenticated card) · CenteredShell (authenticated scrolling document) · MobileShell (a phone app that does NOT scroll its document).",
|
|
61
62
|
"DO let `children` be the only thing that scrolls. Put the primary verb in `actions` and navigation in `tabBar` — both sit outside the scroll region, so neither needs `position: sticky`, a z-index, or bottom padding to clear the other.",
|
|
62
63
|
"DO NOT compose one out of <Card> + `ui-card-inset*` + `overflow-y-auto` (what docs/showcase/case6 did before gh#354). That reproduces the look and neither behaviour that matters on a device: the document still scrolls, and nothing pads out of env(safe-area-inset-*), so the notch covers the app bar and the home indicator covers the primary button.",
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
"importPath": "@godxjp/ui/layout",
|
|
5
5
|
"name": "PageContainer",
|
|
6
6
|
"props": [
|
|
7
|
+
{
|
|
8
|
+
"defaultValue": "\"last\"",
|
|
9
|
+
"description": "last | none, identical to Breadcrumb.currentItem. none keeps every supplied ancestor destination linked and emits no aria-current; the record name belongs in title. breadcrumb items also support ellipsis with the full label in aria-label and title, without a duplicate tooltip description.",
|
|
10
|
+
"name": "breadcrumbCurrentItem",
|
|
11
|
+
"type": "BreadcrumbCurrentItemProp"
|
|
12
|
+
},
|
|
7
13
|
{
|
|
8
14
|
"description": "Instance footer inset; omitted preserves the theme.",
|
|
9
15
|
"name": "footerPad",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"docPath": "data-display/persona.tsx",
|
|
3
|
+
"example": "import { Persona } from \"@godxjp/ui/data-display\";\n\n<Persona name={member.name} description={member.email} size=\"sm\" />",
|
|
4
|
+
"group": "data-display",
|
|
5
|
+
"importPath": "@godxjp/ui/data-display",
|
|
6
|
+
"name": "Persona",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"description": "Full primary label, retained in native title and accessible text; dir=auto preserves its beginning in RTL.",
|
|
10
|
+
"name": "name",
|
|
11
|
+
"required": true,
|
|
12
|
+
"type": "string"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"description": "Optional full secondary label (email/handle), independently truncated and titled with dir=auto.",
|
|
16
|
+
"name": "description",
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Optional image URL; Avatar owns loading and fallback.",
|
|
21
|
+
"name": "avatarSrc",
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "Fallback content; defaults to the first Unicode code point of name. Avatar is decorative beside the full name.",
|
|
26
|
+
"name": "avatarFallback",
|
|
27
|
+
"type": "ReactNode"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "Optional noninteractive badge beside the name, outside its truncation box.",
|
|
31
|
+
"name": "trailing",
|
|
32
|
+
"type": "ReactNode"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"defaultValue": "\"md\"",
|
|
36
|
+
"description": "Avatar control tier and name type scale; secondary text stays xs.",
|
|
37
|
+
"name": "size",
|
|
38
|
+
"type": "\"sm\" | \"md\""
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"description": "Root class; native span attributes and ref are forwarded.",
|
|
42
|
+
"name": "className",
|
|
43
|
+
"type": "string"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"related": [
|
|
47
|
+
"Avatar — mark only",
|
|
48
|
+
"ListRow — padded short-list row with actions",
|
|
49
|
+
"AccountChip — signed-in account control"
|
|
50
|
+
],
|
|
51
|
+
"rules": [
|
|
52
|
+
6,
|
|
53
|
+
45
|
|
54
|
+
],
|
|
55
|
+
"storyPath": "data-display/persona.tsx",
|
|
56
|
+
"tagline": "Compact person identity: fixed avatar, independently truncated name and muted secondary line, optional inline badge.",
|
|
57
|
+
"usage": [
|
|
58
|
+
"Use inside DataTable renderers and stacked rows. Persona owns no row padding, navigation, account action or business state.",
|
|
59
|
+
"Pass localized labels and badge content. Retune --persona-* tokens; avatar sizing uses existing --avatar-* tokens.",
|
|
60
|
+
"Both full labels stay in the accessibility tree and native titles. Keep trailing content noninteractive."
|
|
61
|
+
],
|
|
62
|
+
"useCases": [
|
|
63
|
+
"Member lists",
|
|
64
|
+
"Assignee and owner cells"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
],
|
|
98
98
|
"tagline": "Data-driven vertical nav rail with collapsible submenu groups and a collapsed icon-only mode — never build nav manually with raw buttons.",
|
|
99
99
|
"usage": [
|
|
100
|
+
"For app-local branch viewing scope, put one BranchScopePicker selectionMode=single in action above navigation; see data-entry/branch-scope-navigation.tsx. This differs from platform OrgSwitcher.",
|
|
100
101
|
"DO: Define all nav items as a SidebarSectionProp[] data structure and pass it to sections — never hand-roll nav buttons alongside or instead of the Sidebar.",
|
|
101
102
|
"DO: Add children: SidebarItemProp[] to any SidebarItemProp to create a collapsible submenu group. The group auto-opens and highlights when activeId matches any descendant. The SAME field drives NavList (gh#815), so one grouped settings nav is one NavList and one <nav> landmark.",
|
|
102
103
|
"DO: Mirror the collapsed boolean between AppShell's sidebarCollapsed prop and Sidebar's collapsed prop — they must stay in sync so the shell layout grid adjusts correctly.",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"defaultValue": "\"default\"",
|
|
72
|
-
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a
|
|
72
|
+
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
|
|
73
73
|
"name": "preset",
|
|
74
74
|
"type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
|
|
75
75
|
},
|
|
@@ -263,6 +263,11 @@
|
|
|
263
263
|
"name": "FeatureList",
|
|
264
264
|
"tagline": "A list of STATEMENTS, each with a leading state glyph (included ✓ / limited − / excluded ✗), an optional wrapping description, and the glyph aligned to the first text line."
|
|
265
265
|
},
|
|
266
|
+
{
|
|
267
|
+
"group": "data-display",
|
|
268
|
+
"name": "Persona",
|
|
269
|
+
"tagline": "Compact person identity: fixed avatar, independently truncated name and muted secondary line, optional inline badge."
|
|
270
|
+
},
|
|
266
271
|
{
|
|
267
272
|
"group": "data-display",
|
|
268
273
|
"name": "ListRow",
|
package/agent/components.json
CHANGED
|
@@ -651,6 +651,12 @@
|
|
|
651
651
|
"importPath": "@godxjp/ui/layout",
|
|
652
652
|
"name": "PageContainer",
|
|
653
653
|
"props": [
|
|
654
|
+
{
|
|
655
|
+
"defaultValue": "\"last\"",
|
|
656
|
+
"description": "last | none, identical to Breadcrumb.currentItem. none keeps every supplied ancestor destination linked and emits no aria-current; the record name belongs in title. breadcrumb items also support ellipsis with the full label in aria-label and title, without a duplicate tooltip description.",
|
|
657
|
+
"name": "breadcrumbCurrentItem",
|
|
658
|
+
"type": "BreadcrumbCurrentItemProp"
|
|
659
|
+
},
|
|
654
660
|
{
|
|
655
661
|
"description": "Instance footer inset; omitted preserves the theme.",
|
|
656
662
|
"name": "footerPad",
|
|
@@ -1541,6 +1547,7 @@
|
|
|
1541
1547
|
"storyPath": "layout/mobile-shell.tsx",
|
|
1542
1548
|
"tagline": "Handheld app shell (gh#354) — status band · app bar · the ONE scroll region · sticky action bar · bottom tab bar, with device safe-area insets and a document that never scrolls.",
|
|
1543
1549
|
"usage": [
|
|
1550
|
+
"Compose a More destination in tabBar with SheetTrigger + Sheet + BranchScopePicker selectionMode=single; see layout/mobile-branch-scope.tsx. MobileShell does not own routes or a built-in More menu.",
|
|
1544
1551
|
"DO use MobileShell for a HANDHELD app screen — a warehouse/handy terminal, a driver app, a field-work PWA. It is the fourth root shell: AppShell (needs a sidebar) · the auth CenteredShell (unauthenticated card) · CenteredShell (authenticated scrolling document) · MobileShell (a phone app that does NOT scroll its document).",
|
|
1545
1552
|
"DO let `children` be the only thing that scrolls. Put the primary verb in `actions` and navigation in `tabBar` — both sit outside the scroll region, so neither needs `position: sticky`, a z-index, or bottom padding to clear the other.",
|
|
1546
1553
|
"DO NOT compose one out of <Card> + `ui-card-inset*` + `overflow-y-auto` (what docs/showcase/case6 did before gh#354). That reproduces the look and neither behaviour that matters on a device: the document still scrolls, and nothing pads out of env(safe-area-inset-*), so the notch covers the app bar and the home indicator covers the primary button.",
|
|
@@ -1654,6 +1661,7 @@
|
|
|
1654
1661
|
],
|
|
1655
1662
|
"tagline": "Data-driven vertical nav rail with collapsible submenu groups and a collapsed icon-only mode — never build nav manually with raw buttons.",
|
|
1656
1663
|
"usage": [
|
|
1664
|
+
"For app-local branch viewing scope, put one BranchScopePicker selectionMode=single in action above navigation; see data-entry/branch-scope-navigation.tsx. This differs from platform OrgSwitcher.",
|
|
1657
1665
|
"DO: Define all nav items as a SidebarSectionProp[] data structure and pass it to sections — never hand-roll nav buttons alongside or instead of the Sidebar.",
|
|
1658
1666
|
"DO: Add children: SidebarItemProp[] to any SidebarItemProp to create a collapsible submenu group. The group auto-opens and highlights when activeId matches any descendant. The SAME field drives NavList (gh#815), so one grouped settings nav is one NavList and one <nav> landmark.",
|
|
1659
1667
|
"DO: Mirror the collapsed boolean between AppShell's sidebarCollapsed prop and Sidebar's collapsed prop — they must stay in sync so the shell layout grid adjusts correctly.",
|
|
@@ -2495,7 +2503,13 @@
|
|
|
2495
2503
|
"name": "Breadcrumb",
|
|
2496
2504
|
"props": [
|
|
2497
2505
|
{
|
|
2498
|
-
"
|
|
2506
|
+
"defaultValue": "\"last\"",
|
|
2507
|
+
"description": "last | none. none renders an ancestor-only trail: every item with to stays linked and no default segment has aria-current. Missing paths remain text. menu and itemRender keep owning their segment bodies; custom renderers own their ARIA.",
|
|
2508
|
+
"name": "currentItem",
|
|
2509
|
+
"type": "BreadcrumbCurrentItemProp"
|
|
2510
|
+
},
|
|
2511
|
+
{
|
|
2512
|
+
"description": "Array of { label, to?, menu?, ellipsis? } — omit `to` on the last (current) segment. `ellipsis` (`boolean | { tooltip?: ReactNode }`, gh#1046; antd Breadcrumb has none, the shape is antd Typography's) keeps the trail on one line and cuts that segment's label with an ellipsis, with the full label as aria-label and title on the clipped element (no duplicate aria-describedby), showing the full label in a tooltip on hover and on keyboard focus of the crumb link; `{ tooltip: false }` drops the tooltip. Link and current segments only. `--breadcrumb-item-max-inline-size` (default `none`) caps such a segment. `menu` (Ant Design `BreadcrumbItemType.menu`) hangs a sibling picker off that segment: `{ items: { value, label, to?, disabled? }[], onSelect? }`. The segment then renders as a menu button, not a link — an entry with `to` becomes a real anchor inside the menu while keeping `role=\"menuitem\"`.",
|
|
2499
2513
|
"name": "items",
|
|
2500
2514
|
"required": true,
|
|
2501
2515
|
"type": "BreadcrumbItemProp[]"
|
|
@@ -2527,7 +2541,7 @@
|
|
|
2527
2541
|
"tagline": "Standalone breadcrumb nav rendering an ordered trail of page segments.",
|
|
2528
2542
|
"usage": [
|
|
2529
2543
|
"DO import from `@godxjp/ui/layout` (not from a navigation or general sub-path) and pass a single `items` prop — an ordered array of `{ label, to? }` objects. No children, no sub-components, no render-prop API.",
|
|
2530
|
-
"DO omit `to` on the last (current-page) segment — the component automatically renders it as a `<span aria-current=\"page\">` instead of a router `<Link>`.
|
|
2544
|
+
"DO omit `to` on the last (current-page) segment — the component automatically renders it as a `<span aria-current=\"page\">` instead of a router `<Link>`. In the default currentItem=\"last\" mode, passing `to` on the last item does NOT make it a link. For ancestor-only trails use currentItem=\"none\" and supply every ancestor destination.",
|
|
2531
2545
|
"DO pass the Breadcrumb node as a ReactNode to the `breadcrumb` prop of `AppShell` (or `AppShell`) for shell-level breadcrumbs, or to `PageContainer`'s `breadcrumb` prop (which accepts `BreadcrumbItemProp[]` directly — not a ReactNode). When passing to `PageContainer`, pass the raw array; when passing to `AppShell`, wrap it: `breadcrumb={<Breadcrumb items={…} />}`.",
|
|
2532
2546
|
"DON'T hand-roll a breadcrumb strip (divs with chevrons, anchors, separators) — Breadcrumb ships the `<nav aria-label=\"Breadcrumb\">` + `<ol>` + `aria-hidden` chevrons. Any custom trail is a violation of the no-hand-roll rule and will fail `npm run ui:audit`.",
|
|
2533
2547
|
"DON'T use Breadcrumb for tab-style or step-style navigation (multi-step forms, wizard progress). Those flows belong to `Steps`. Breadcrumb is strictly a spatial location trail, not a process indicator.",
|
|
@@ -3475,7 +3489,7 @@
|
|
|
3475
3489
|
},
|
|
3476
3490
|
{
|
|
3477
3491
|
"defaultValue": "'default'",
|
|
3478
|
-
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: below collapseBelow the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*) under table-layout: fixed, cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a
|
|
3492
|
+
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: below collapseBelow the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*) under table-layout: fixed, cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
|
|
3479
3493
|
"name": "preset",
|
|
3480
3494
|
"type": "'default' | 'action-collection' | 'stacked-record-collection'"
|
|
3481
3495
|
},
|
|
@@ -3627,6 +3641,7 @@
|
|
|
3627
3641
|
"storyPath": "data-display/data-table/index.tsx",
|
|
3628
3642
|
"tagline": "The one TanStack-powered compound admin list — sticky header, sorting, global search, column visibility ('set view'), bulk selection, BOTH cursor and numbered pagination, density, and built-in empty/loading states. Keep the SIMPLE `data` + lean `columns` (ColumnDef) API for the common case; opt into the full grid chrome via the compound parts. Internally driven by @tanstack/react-table (a real dependency). Lives on @godxjp/ui/data-display only (it is NOT on the runtime-neutral root/admin barrel because it pulls TanStack). Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout.",
|
|
3629
3643
|
"usage": [
|
|
3644
|
+
"Ellipsis cells expose full rendered text for default/custom renderers, including stacked layout. Renderer-owned descendant titles or aria-describedby tooltips suppress a duplicate cell title (#1406).",
|
|
3630
3645
|
"DO use `striped` on dense list tables (many columns, a row the eye must follow across the width). To stripe EVERY Table and DataTable in a service, set it ONCE in the theme — `:root { --table-row-striped-alpha: 100%; }` — instead of passing `striped` at each call site; `striped={false}` then opts one table out. Retint with `--table-row-striped-background`, never with a `rowClassName` utility or `:nth-child` page CSS (those count DOM rows, so an expanded detail row shifts every stripe after it).",
|
|
3631
3646
|
"DO pass loading={isFetching} during data fetches — it renders a loading row in the table body and suppresses the empty state. Never show a spinner outside DataTable while the table is visible.",
|
|
3632
3647
|
"DO NOT add a data.length===0 conditional around DataTable. When data is empty and loading is false, the built-in EmptyState renders automatically. Pass empty={<EmptyState title='...'/>} only when you need a custom message.",
|
|
@@ -4347,6 +4362,72 @@
|
|
|
4347
4362
|
"A capability or compatibility list — supported / partially supported / unsupported."
|
|
4348
4363
|
]
|
|
4349
4364
|
},
|
|
4365
|
+
{
|
|
4366
|
+
"docPath": "data-display/persona.tsx",
|
|
4367
|
+
"example": "import { Persona } from \"@godxjp/ui/data-display\";\n\n<Persona name={member.name} description={member.email} size=\"sm\" />",
|
|
4368
|
+
"group": "data-display",
|
|
4369
|
+
"importPath": "@godxjp/ui/data-display",
|
|
4370
|
+
"name": "Persona",
|
|
4371
|
+
"props": [
|
|
4372
|
+
{
|
|
4373
|
+
"description": "Full primary label, retained in native title and accessible text; dir=auto preserves its beginning in RTL.",
|
|
4374
|
+
"name": "name",
|
|
4375
|
+
"required": true,
|
|
4376
|
+
"type": "string"
|
|
4377
|
+
},
|
|
4378
|
+
{
|
|
4379
|
+
"description": "Optional full secondary label (email/handle), independently truncated and titled with dir=auto.",
|
|
4380
|
+
"name": "description",
|
|
4381
|
+
"type": "string"
|
|
4382
|
+
},
|
|
4383
|
+
{
|
|
4384
|
+
"description": "Optional image URL; Avatar owns loading and fallback.",
|
|
4385
|
+
"name": "avatarSrc",
|
|
4386
|
+
"type": "string"
|
|
4387
|
+
},
|
|
4388
|
+
{
|
|
4389
|
+
"description": "Fallback content; defaults to the first Unicode code point of name. Avatar is decorative beside the full name.",
|
|
4390
|
+
"name": "avatarFallback",
|
|
4391
|
+
"type": "ReactNode"
|
|
4392
|
+
},
|
|
4393
|
+
{
|
|
4394
|
+
"description": "Optional noninteractive badge beside the name, outside its truncation box.",
|
|
4395
|
+
"name": "trailing",
|
|
4396
|
+
"type": "ReactNode"
|
|
4397
|
+
},
|
|
4398
|
+
{
|
|
4399
|
+
"defaultValue": "\"md\"",
|
|
4400
|
+
"description": "Avatar control tier and name type scale; secondary text stays xs.",
|
|
4401
|
+
"name": "size",
|
|
4402
|
+
"type": "\"sm\" | \"md\""
|
|
4403
|
+
},
|
|
4404
|
+
{
|
|
4405
|
+
"description": "Root class; native span attributes and ref are forwarded.",
|
|
4406
|
+
"name": "className",
|
|
4407
|
+
"type": "string"
|
|
4408
|
+
}
|
|
4409
|
+
],
|
|
4410
|
+
"related": [
|
|
4411
|
+
"Avatar — mark only",
|
|
4412
|
+
"ListRow — padded short-list row with actions",
|
|
4413
|
+
"AccountChip — signed-in account control"
|
|
4414
|
+
],
|
|
4415
|
+
"rules": [
|
|
4416
|
+
6,
|
|
4417
|
+
45
|
|
4418
|
+
],
|
|
4419
|
+
"storyPath": "data-display/persona.tsx",
|
|
4420
|
+
"tagline": "Compact person identity: fixed avatar, independently truncated name and muted secondary line, optional inline badge.",
|
|
4421
|
+
"usage": [
|
|
4422
|
+
"Use inside DataTable renderers and stacked rows. Persona owns no row padding, navigation, account action or business state.",
|
|
4423
|
+
"Pass localized labels and badge content. Retune --persona-* tokens; avatar sizing uses existing --avatar-* tokens.",
|
|
4424
|
+
"Both full labels stay in the accessibility tree and native titles. Keep trailing content noninteractive."
|
|
4425
|
+
],
|
|
4426
|
+
"useCases": [
|
|
4427
|
+
"Member lists",
|
|
4428
|
+
"Assignee and owner cells"
|
|
4429
|
+
]
|
|
4430
|
+
},
|
|
4350
4431
|
{
|
|
4351
4432
|
"example": "import { Card, CardContent, CardHeader, CardTitle, ListRow, Badge } from \"@godxjp/ui/data-display\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Smartphone } from \"lucide-react\";\nimport { Flex } from \"@godxjp/ui/layout\";\n\n// The three scenarios below are three CARDS on one page, so they are wrapped in a Flex — the gap\n// between sibling cards belongs to the stack, never to the cards (audit: sibling-cards-need-flex).\n<Flex direction=\"col\" gap=\"lg\">\n<Card>\n <CardHeader>\n <CardTitle>アクティブなセッション</CardTitle>\n </CardHeader>\n <CardContent flush>\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"iPhone 15 · Tokyo\"\n description=\"最終アクセス 2分前\"\n trailing={<Badge status=\"active\" />}\n />\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"MacBook Pro · Osaka\"\n description=\"最終アクセス 3日前\"\n trailing={<Button size=\"xs\" variant=\"outline\">ログアウト</Button>}\n />\n </CardContent>\n</Card>\n\n// Notifications — unread dot + emphasized surface, wrapping title, two inline actions\n<Card>\n <CardContent flush>\n <ListRow\n unread\n align=\"start\"\n overflow=\"wrap\"\n title=\"組織「グローバル・トランスフォーメーション推進本部」への招待が届いています\"\n description=\"2026-07-30 09:12 JST\"\n trailing={\n <>\n <Button size=\"xs\" variant=\"ghost\">既読にする</Button>\n <Button size=\"xs\" variant=\"outline\">開く</Button>\n </>\n }\n />\n <ListRow unread={false} align=\"start\" overflow=\"wrap\" title=\"請求書が発行されました\" description=\"2026-07-28 18:40 JST\" />\n </CardContent>\n</Card>\n\n{/* A list of LINKS — `as=\"li\"` gives the list item, `asChild` gives the whole-row link, and the\n item carries the divider. Never wrap the row in your own li, and never reach for a raw ul:\n `marker=\"none\"` is the semantic list container (no bullet, no indent, gap token). */}\n<Card>\n <CardContent flush>\n <Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\">\n {projects.map((project) => (\n <ListRow key={project.key} as=\"li\" asChild title={project.name} description={project.key}>\n <Link href={`/projects/${project.key}`} />\n </ListRow>\n ))}\n </Flex>\n </CardContent>\n</Card>\n</Flex>",
|
|
4352
4433
|
"group": "data-display",
|
|
@@ -5615,7 +5696,7 @@
|
|
|
5615
5696
|
},
|
|
5616
5697
|
{
|
|
5617
5698
|
"defaultValue": "\"default\"",
|
|
5618
|
-
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a
|
|
5699
|
+
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
|
|
5619
5700
|
"name": "preset",
|
|
5620
5701
|
"type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
|
|
5621
5702
|
},
|
|
@@ -15024,6 +15105,24 @@
|
|
|
15024
15105
|
"importPath": "@godxjp/ui/data-entry",
|
|
15025
15106
|
"name": "BranchScopePicker",
|
|
15026
15107
|
"props": [
|
|
15108
|
+
{
|
|
15109
|
+
"defaultValue": "\"multiple\"",
|
|
15110
|
+
"description": "Single compact Select for all/one branch; multiple keeps the existing all/subset editor.",
|
|
15111
|
+
"name": "selectionMode",
|
|
15112
|
+
"type": "\"single\" | \"multiple\""
|
|
15113
|
+
},
|
|
15114
|
+
{
|
|
15115
|
+
"defaultValue": "true",
|
|
15116
|
+
"description": "Allow organization-wide scope. Set false for record fields, then validate exactly one permitted concrete branch on submit. Invalid supplied values are not silently coerced.",
|
|
15117
|
+
"name": "allowAll",
|
|
15118
|
+
"type": "boolean"
|
|
15119
|
+
},
|
|
15120
|
+
{
|
|
15121
|
+
"defaultValue": "false",
|
|
15122
|
+
"description": "Hide after a successful read of one enabled branch, except validation errors. Does not emit a change or normalize URL/default values; the app does that.",
|
|
15123
|
+
"name": "hideWhenSingle",
|
|
15124
|
+
"type": "boolean"
|
|
15125
|
+
},
|
|
15027
15126
|
{
|
|
15028
15127
|
"description": "Giá trị có kiểm soát: phạm vi đang chọn.",
|
|
15029
15128
|
"name": "value",
|
|
@@ -15089,7 +15188,7 @@
|
|
|
15089
15188
|
"type": "ReactNode"
|
|
15090
15189
|
},
|
|
15091
15190
|
{
|
|
15092
|
-
"description": "Native form name
|
|
15191
|
+
"description": "Native form name: multiple submits the MODE radio; single submits the Select key (`all` or `branch:<id>`). Serialize the public `{ mode, branchIds }` value yourself for domain records; native keys are not branch ownership.",
|
|
15093
15192
|
"name": "name",
|
|
15094
15193
|
"type": "string"
|
|
15095
15194
|
},
|
|
@@ -15110,6 +15209,8 @@
|
|
|
15110
15209
|
"storyPath": "data-entry/branch-scope-picker.tsx",
|
|
15111
15210
|
"tagline": "Canonical scope control: all branches vs an explicit subset.",
|
|
15112
15211
|
"usage": [
|
|
15212
|
+
"Use selectionMode=single in Sidebar action (above navigation) and a Sheet opened from MobileShell tabBar More. These are compositions, not a new switcher. OrgSwitcher changes organization and stays in platform chrome.",
|
|
15213
|
+
"The app owns URL parsing, validation against authorized branches, popstate/router updates, and onValueChange navigation. Resolve a single authorized branch in the app before hiding the picker. Viewing scope is not record ownership: record forms use separate state, selectionMode=single and allowAll=false, and validate exactly one permitted id on submit. Future groups need an explicit value contract; do not encode them as branch ids.",
|
|
15113
15214
|
"DO treat scope as ONE form field: the single { mode, branchIds } value goes through FormField like any other control.",
|
|
15114
15215
|
"DO import it from @godxjp/ui/data-entry — never compose an ad-hoc radio+checkbox scope block per screen.",
|
|
15115
15216
|
"DO use `error` ONLY for validation (e.g. mode=selected with zero branches checked); a failed branch fetch is `listError`, a 403 is `denied`.",
|
package/agent/index.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
3
|
"anti-ai-tells": 35,
|
|
4
|
-
"components":
|
|
4
|
+
"components": 170,
|
|
5
5
|
"patterns": 28,
|
|
6
6
|
"rules": 44,
|
|
7
|
-
"tokens":
|
|
8
|
-
"vocabulary":
|
|
7
|
+
"tokens": 2251,
|
|
8
|
+
"vocabulary": 15
|
|
9
9
|
},
|
|
10
10
|
"design": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md",
|
|
11
11
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"file": "components-index.json",
|
|
19
|
-
"note": "53 KB — name + group + tagline for all
|
|
19
|
+
"note": "53 KB — name + group + tagline for all 170. FETCH THIS FIRST, then fetch only the components you chose.",
|
|
20
20
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
@@ -54,19 +54,19 @@
|
|
|
54
54
|
"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.",
|
|
55
55
|
"read": {
|
|
56
56
|
"live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
|
|
57
|
-
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v33.
|
|
57
|
+
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v33.14.0/agent/index.json"
|
|
58
58
|
},
|
|
59
59
|
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
|
|
60
60
|
"start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
|
|
61
61
|
"tokenTiers": {
|
|
62
62
|
"component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
|
|
63
63
|
"counts": {
|
|
64
|
-
"component":
|
|
64
|
+
"component": 1920,
|
|
65
65
|
"foundation": 218,
|
|
66
66
|
"semantic": 113
|
|
67
67
|
},
|
|
68
68
|
"foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
|
|
69
69
|
"semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
|
|
70
70
|
},
|
|
71
|
-
"version": "33.
|
|
71
|
+
"version": "33.14.0"
|
|
72
72
|
}
|