@godxjp/ui 31.5.0 → 31.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/agent/START-HERE.md +5 -5
  2. package/agent/components/BranchScopePicker.json +5 -0
  3. package/agent/components/Cascader.json +1 -1
  4. package/agent/components/DataTable.json +2 -2
  5. package/agent/components/InfiniteQueryState.json +6 -1
  6. package/agent/components/RangeTimeline.json +6 -0
  7. package/agent/components/RecordPicker.json +10 -0
  8. package/agent/components/Transfer.json +5 -0
  9. package/agent/components/Tree.json +21 -4
  10. package/agent/components/TreeSelect.json +1 -1
  11. package/agent/components.json +57 -9
  12. package/agent/index.json +5 -5
  13. package/agent/llms.txt +6 -6
  14. package/agent/tokens.json +6 -0
  15. package/dist/components/data-display/data-table.d.ts +3 -0
  16. package/dist/components/data-display/data-table.js +415 -370
  17. package/dist/components/data-display/range-timeline.d.ts +20 -0
  18. package/dist/components/data-display/range-timeline.js +217 -197
  19. package/dist/components/data-display/tree.js +179 -23
  20. package/dist/components/data-entry/branch-scope-picker.d.ts +1 -0
  21. package/dist/components/data-entry/branch-scope-picker.js +2 -1
  22. package/dist/components/data-entry/cascader.js +4 -5
  23. package/dist/components/data-entry/record-picker.d.ts +2 -0
  24. package/dist/components/data-entry/record-picker.js +22 -3
  25. package/dist/components/data-entry/transfer.d.ts +1 -1
  26. package/dist/components/data-entry/transfer.js +5 -2
  27. package/dist/components/data-entry/tree-select.js +14 -5
  28. package/dist/components/query/infinite-query-state.d.ts +1 -1
  29. package/dist/components/query/infinite-query-state.js +2 -1
  30. package/dist/contracts/measurement.json +1 -1
  31. package/dist/i18n/messages/en.json +16 -2
  32. package/dist/i18n/messages/ja.json +7 -2
  33. package/dist/i18n/messages/vi.json +7 -2
  34. package/dist/lib/hooks.d.ts +8 -4
  35. package/dist/lib/hooks.js +5 -2
  36. package/dist/lib/tree.d.ts +23 -0
  37. package/dist/lib/tree.js +29 -0
  38. package/dist/props/components/data-display.prop.d.ts +21 -2
  39. package/dist/props/components/data-entry.prop.d.ts +31 -4
  40. package/dist/props/components/query.prop.d.ts +8 -1
  41. package/dist/props/registry.d.ts +24 -6
  42. package/dist/props/registry.js +41 -4
  43. package/dist/props/vocabulary/data.prop.d.ts +32 -2
  44. package/dist/props/vocabulary/index.d.ts +1 -1
  45. package/dist/styles/data-display-layout.css +52 -0
  46. package/dist/styles/layers.json +1 -1
  47. package/dist/styles/table-layout.css +16 -1
  48. package/dist/tokens/components/tree.css +2 -0
  49. package/docs/data-display/data-table/examples/server-paged.tsx +23 -0
  50. package/docs/data-display/data-table/index.md +2 -0
  51. package/docs/data-display/timeline.tsx +33 -0
  52. package/docs/data-display/tree.tsx +154 -0
  53. package/docs/data-entry/branch-scope-picker.tsx +6 -1
  54. package/docs/data-entry/record-picker.tsx +1 -0
  55. package/docs/data-entry/transfer.tsx +1 -0
  56. package/docs/i18n/messages/en.json +19 -0
  57. package/docs/i18n/messages/ja.json +18 -0
  58. package/docs/i18n/messages/vi.json +18 -0
  59. package/docs/query/infinite-query-state.tsx +5 -1
  60. package/docs/roadmap/tree-components.md +2 -1
  61. package/package.json +2 -2
  62. package/scripts/ui-audit.mjs +15 -5
@@ -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.5.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 31.6.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,14 +52,14 @@ 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–35 KB, median 6 KB), carrying its props,
55
+ 2. `components/<Name>.json` — one file per component (1 KB–36 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.
59
- 4. `tokens.json` — 2098 design tokens, each tagged with its `tier`. **If you were handed a
59
+ 4. `tokens.json` — 2099 design tokens, each tagged with its `tier`. **If you were handed a
60
60
  brand, read the 211 `foundation` entries first** — `--primary`, `--background`,
61
61
  `--radius`, `--font-size-base` are the handful everything else derives from. The
62
- 1783 `component` entries are per-part knobs; reach for one only when a role is
62
+ 1784 `component` entries are per-part knobs; reach for one only when a role is
63
63
  right everywhere except one component.
64
64
  5. `anti-ai-tells.json` — 26 shapes that make generated UI look generated, each with the
65
65
  fix. Read before you reach for a gradient hero or a wall of coloured chips.
@@ -145,7 +145,7 @@ has stopped following the brand.
145
145
  |---|---|---|---|
146
146
  | `foundation` | 211 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** Handed a brand colour, this is where it goes: `:root { --primary: <H> <S>% <L>%; }` (HSL components, no `hsl()` wrapper) |
147
147
  | `semantic` | 104 | 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 |
148
- | `component` | 1783 | 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` |
148
+ | `component` | 1784 | 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` |
149
149
 
150
150
  A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
151
151
  value, so the real default is computed where the element paints it. Set it and yours wins.
@@ -59,6 +59,11 @@
59
59
  "name": "searchable",
60
60
  "type": "boolean"
61
61
  },
62
+ {
63
+ "description": "antd `notFoundContent` — shown when the branch SEARCH matches nothing (default: localized `dataEntry.branchScope.noMatches`). An empty `branches` list is `empty`'s job, not this.",
64
+ "name": "notFoundContent",
65
+ "type": "ReactNode"
66
+ },
62
67
  {
63
68
  "description": "Override the localized radio labels (e.g. domain wording like 全店舗).",
64
69
  "name": "allLabel / selectedLabel",
@@ -152,7 +152,7 @@
152
152
  "type": "\"SHOW_CHILD\" | \"SHOW_PARENT\""
153
153
  },
154
154
  {
155
- "description": "antd `loadData` — lazy children. Called ONCE per branch that has no children and isLeaf !== true, the first time it is expanded; push the fetched children into options.",
155
+ "description": "antd `loadData` — lazy children. Called when a branch that has no children and isLeaf !== true is expanded, and never again once its promise resolves; push the fetched children into options. A rejected promise is forgotten, so the next activation of the branch asks again (gh#1041).",
156
156
  "name": "loadData",
157
157
  "type": "(selectedOptions: TreeOptionProp[]) => void | Promise<void>"
158
158
  },
@@ -159,9 +159,9 @@
159
159
  "type": "() => void"
160
160
  },
161
161
  {
162
- "description": "Full row-selection configuration (antd rowSelection). Supersedes — and can be mixed with — selectable/selected/onSelectChange, which drive the same state. type:'radio' makes the column single-choice (no header checkbox at all). getCheckboxProps is the declared home for 'this row cannot be selected' (disabled) and for a per-row accessible name. preserveSelectedRowKeys keeps a key selected after its row leaves `data` (server paging / a filter), which is the only way a select-across-pages bulk action can be correct. selections adds bulk entries under the header checkbox (true = the built-in all · invert · none).",
162
+ "description": "Full row-selection configuration (antd rowSelection). Supersedes — and can be mixed with — selectable/selected/onSelectChange, which drive the same state. type:'radio' makes the column single-choice (no header checkbox at all). getCheckboxProps is the declared home for 'this row cannot be selected' (disabled) and for a per-row accessible name. preserveSelectedRowKeys keeps a key selected after its row leaves `data` (server paging / a filter), which is the only way a select-across-pages bulk action can be correct. selections adds bulk entries under the header checkbox (true = the built-in all · invert · none; a list mixes antd's DataTable.SELECTION_ALL / SELECTION_INVERT / SELECTION_NONE with custom entries in the order given). selectAllLabel names the header checkbox — say what it really selects (on a server-paged table: the page) instead of replacing it through columnTitle. matching (no antd equivalent; Gmail/Jira/GitHub) is the server-paged 'select all N matching' banner: once the whole page is ticked and total exceeds the page, a polite status above the header offers 'Select all N matching rows'; choosing it reports onSelectedChange(true) — send your FILTER, not the ids, for the bulk write — and while selected every row of any page shows ticked. Unticking a row or 'Clear selection' reports false. With matching set, the header checkbox defaults to 'Select all rows on this page'.",
163
163
  "name": "rowSelection",
164
- "type": "{ type?: 'checkbox'|'radio'; selectedRowKeys?: string[]; defaultSelectedRowKeys?: string[]; onChange?: (keys, rows) => void; getCheckboxProps?: (row) => { disabled?, 'aria-label'? }; preserveSelectedRowKeys?: boolean; selections?: true | { key, text, onSelect }[]; hideSelectAll?: boolean; columnTitle?: ReactNode }"
164
+ "type": "{ type?: 'checkbox'|'radio'; selectedRowKeys?: string[]; defaultSelectedRowKeys?: string[]; onChange?: (keys, rows) => void; getCheckboxProps?: (row) => { disabled?, 'aria-label'? }; preserveSelectedRowKeys?: boolean; selections?: true | ({ key, text, onSelect } | DataTable.SELECTION_ALL | DataTable.SELECTION_INVERT | DataTable.SELECTION_NONE)[]; hideSelectAll?: boolean; columnTitle?: ReactNode; selectAllLabel?: string; matching?: { total: number; selected: boolean; onSelectedChange: (selected: boolean) => void } }"
165
165
  },
166
166
  {
167
167
  "description": "Expandable detail rows (antd expandable). Supplying expandedRowRender adds a leading expand column before the selection column and renders the panel in a real <tr> spanning every column, so the table's grid semantics survive. rowExpandable gates the affordance per row; expandedRowKeys + onExpandedRowsChange make it controlled.",
@@ -27,6 +27,11 @@
27
27
  "name": "children",
28
28
  "required": true,
29
29
  "type": "(flat, helpers) => ReactNode"
30
+ },
31
+ {
32
+ "description": "Idle text of the built-in load-more button (default: localized `query.loadMore`, 「さらに読み込む」) — e.g. 「さらに表示」「さらに古い版を表示」. The button, its pending label and disabled-while-fetching stay the component's. Ignored when `loadMore` replaces the footer.",
33
+ "name": "loadMoreLabel",
34
+ "type": "ReactNode"
30
35
  }
31
36
  ],
32
37
  "related": [
@@ -43,7 +48,7 @@
43
48
  "DO: Always pass `skeleton` (e.g. `<SkeletonTable />` or `<SkeletonStat />`). It shows on initial `isPending`, on refetch-after-error, and whenever `data` is absent. Never show a blank area while loading.",
44
49
  "DO: Pass `empty` (an `<EmptyState>` node) to handle the zero-results case — without it the children render-prop is called with an empty array and you get a silent blank screen. Provide a custom `isEmpty` only when `TFlat` is not an array.",
45
50
  "DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; a 401 is handled by the app-root `AuthExpiryProvider` (auto, once, skeleton + live region) or, without one, by the `onAuthError` sign-in button; raw backend/token text is never rendered.",
46
- "DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Override only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
51
+ "DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Rename it with `loadMoreLabel` (keeps the pending state); replace it only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
47
52
  "DON'T: Use `InfiniteQueryState` for a `useQuery` result — it expects `UseInfiniteQueryResult` shape (`pages`, `hasNextPage`, `fetchNextPage`, `isFetchingNextPage`). For regular `useQuery` use `DataState` instead.",
48
53
  "DON'T: Confuse the two generics: `TPage` is the raw page shape from the API, `TFlat` is what `flatten` returns (usually `TItem[]`). The `children` render-prop receives `TFlat`, not `TPage`. Pass `isEmpty` if `TFlat` is not a plain array so empty detection works correctly."
49
54
  ],
@@ -64,6 +64,12 @@
64
64
  "description": "Fires with the next expanded parent ids when a disclosure is toggled, for controlled and uncontrolled timelines alike.",
65
65
  "name": "onExpandedValuesChange",
66
66
  "type": "(values: string[]) => void"
67
+ },
68
+ {
69
+ "defaultValue": "false",
70
+ "description": "antd Table `sticky` — keep the axis header (bands + ticks) on screen while the PAGE scrolls a long schedule. `offsetHeader` is px from the top of the scrolling viewport (the height of a fixed app topbar). The header moves out of the horizontal scroller and follows its scrollLeft; the section becomes `overflow: clip` and the body scroller takes the keyboard tab stop. Like antd, it needs no clipping ancestor between the timeline and the page scroller: `Card` is `overflow: hidden`, so a sticky timeline goes in the page as its own section, not inside a Card. antd's `offsetScroll` / `getContainer` (sticky horizontal scrollbar) are not ported.",
71
+ "name": "sticky",
72
+ "type": "boolean | { offsetHeader?: number }"
67
73
  }
68
74
  ],
69
75
  "rules": [],
@@ -82,6 +82,16 @@
82
82
  "name": "dialogTitle",
83
83
  "type": "string"
84
84
  },
85
+ {
86
+ "description": "antd `notFoundContent` — shown when the search finds nothing, in the dropdown shape and the dialog alike (default: localized `dataEntry.recordPicker.empty`). Say WHAT has no match, e.g. 「該当するファイルはありません」.",
87
+ "name": "notFoundContent",
88
+ "type": "ReactNode"
89
+ },
90
+ {
91
+ "description": "Text of the dialog's load-more button (default: localized `dataEntry.recordPicker.more`). Only the label — the button still appears only while the server returns a `nextCursor`.",
92
+ "name": "loadMoreLabel",
93
+ "type": "ReactNode"
94
+ },
85
95
  {
86
96
  "description": "Control height of the trigger, on the shared control ladder.",
87
97
  "name": "size",
@@ -109,6 +109,11 @@
109
109
  "name": "onSelectChange",
110
110
  "type": "(sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void"
111
111
  },
112
+ {
113
+ "description": "antd `locale`, ported for `notFoundContent` only: what an empty or search-emptied pane shows (default: localized `dataEntry.transfer.empty`). A `[source, target]` pair sets each pane separately, as in antd.",
114
+ "name": "locale",
115
+ "type": "{ notFoundContent?: ReactNode | [ReactNode, ReactNode] }"
116
+ },
112
117
  {
113
118
  "description": "Fires when items move between panels; you own `targetKeys` state.",
114
119
  "name": "onValueChange",
@@ -86,7 +86,7 @@
86
86
  "type": "boolean"
87
87
  },
88
88
  {
89
- "description": "Lazy children (antd `loadData`). Called ONCE per node the first time a branch with no `children` and `isLeaf !== true` is expanded; a Skeleton row and `aria-busy` cover the wait. Push the fetched children into `treeData`.",
89
+ "description": "Lazy children (antd `loadData`). Called when a branch with no `children` and `isLeaf !== true` is expanded, and never again once its promise RESOLVES; a Skeleton row and `aria-busy` cover the wait. Push the fetched children into `treeData`. A REJECTED promise is not a load (rc-tree, gh#1041): the branch folds back shut (uncontrolled expansion) and the next expand asks again, up to 10 attempts.",
90
90
  "name": "loadData",
91
91
  "type": "(node: TreeNodeProp) => void | Promise<void>"
92
92
  },
@@ -95,6 +95,22 @@
95
95
  "name": "titleRender",
96
96
  "type": "(node: TreeNodeProp) => ReactNode"
97
97
  },
98
+ {
99
+ "description": "Highlight the nodes this returns true for (antd `filterTreeNode`, gh#1043) — the match of a search box above the tree. Matching rows are MARKED, not hidden: `data-filter-node=\"true\"`, the label in `--tree-node-filter-foreground` (default `hsl(var(--primary))`) at the medium weight, plus sr-only \"matches the filter\" text. Outline, keyboard and ARIA are unchanged. To also open the branches that hold a match, drive `expandedValues`.",
100
+ "name": "filterTreeNode",
101
+ "type": "(node: TreeNodeProp) => boolean"
102
+ },
103
+ {
104
+ "description": "Viewport height in px (antd `height`, gh#1042). The tree scrolls inside it and, unless `virtual={false}`, renders only the rows in view plus a small overscan — the windowed rows stay a flat run of treeitems whose `aria-level`/`aria-setsize`/`aria-posinset` still describe the whole outline. Rows must share one height (the `size` tier).",
105
+ "name": "height",
106
+ "type": "number"
107
+ },
108
+ {
109
+ "defaultValue": "true",
110
+ "description": "Set `false` to keep the `height` viewport but render every row (antd `virtual`).",
111
+ "name": "virtual",
112
+ "type": "boolean"
113
+ },
98
114
  {
99
115
  "defaultValue": "false",
100
116
  "description": "Draw the connector rails between a parent and its children (antd `showLine`). Off by default — a rail is chrome, and chrome defaults quiet.",
@@ -152,7 +168,7 @@
152
168
  "TreeSelect — the same hierarchy INSIDE a Popover, as a form field. Use TreeSelect when the answer is a value in a form; use Tree when the hierarchy itself is the page.",
153
169
  "Cascader — a path picker across columns. Use it when the user walks one path to a leaf; use Tree when several branches are open at once.",
154
170
  "Accordion — single-level disclosure with rich panel content. It is not a hierarchy and has no tree keyboard model.",
155
- "ScrollArea — wrap Tree in one to cap a long outline; Tree does not virtualise in v1."
171
+ "ScrollArea — not needed around Tree: pass `height`, and the tree is its own (windowed) scroll viewport."
156
172
  ],
157
173
  "rules": [
158
174
  2,
@@ -172,8 +188,9 @@
172
188
  "DON'T nest a Button, Checkbox, Link or any focusable control inside a node label. A tree item owns exactly ONE tab stop; the disclosure triangle and the tick box are decorative glyphs for that reason. Put row actions in a sibling column outside the tree, or open a detail pane on selection.",
173
189
  "DON'T hand-roll an indented `<ul>` (or a NavList / ListRow stack with a per-depth margin) for a hierarchy. A flat indented list only LOOKS like a tree: no expand/collapse, no `role=\"tree\"`, no keyboard model, no selection contract. `TreeList` was exactly that list and was REMOVED in 21.0.0 — Tree is what replaced it, and it is the one to reach for whenever nodes expand, collapse or are keyboard-navigated.",
174
190
  "DO pass `divided` when the tree IS the navigation of a page — a wiki/document outline or a section index sitting in a `Card` (`Card` > `CardContent flush` > `Tree divided`). Without a rule the rows run together and the outline reads as one block; with it, it reads as the ruled list ListRow and Table already give a flat list. Retint it per theme with `--tree-divider-color`, never with a per-page utility class.",
175
- "DO cap a long tree with `ScrollArea` — virtualisation is not in v1, so a 5,000-node tree renders 5,000 rows.",
176
- "DO push fetched children into `treeData` from `loadData`; the tree calls it once per node and shows a Skeleton row until the data lands."
191
+ "DO pass `height` for a long tree (antd `height` + `virtual`): the tree scrolls inside that height and keeps only the rows in view in the DOM, so a 421-child level renders a few dozen rows. Do not wrap it in `ScrollArea` as well — the tree is its own viewport.",
192
+ "DO push fetched children into `treeData` from `loadData`; the tree calls it once per node that loads and shows a Skeleton row until the data lands. Reject the promise on failure — the branch folds shut and the next expand retries.",
193
+ "DON'T wait for a paged \"load more\" node — antd's Tree has none, and `height` covers a long list. If the API itself is paged, append a leaf node such as `{ value: `${parent}::more`, label: t(\"…load more\"), isLeaf: true }` to the loaded children and fetch the next page from `onValueChange` when it is selected: it is a real treeitem, so it is keyboard-reachable and announced."
177
194
  ],
178
195
  "useCases": [
179
196
  "A permission tree: modules → resources → actions with tri-state checkboxes, where ticking a module ticks everything under it and a partly-granted module shows the dash.",
@@ -179,7 +179,7 @@
179
179
  "type": "boolean"
180
180
  },
181
181
  {
182
- "description": "antd `loadData` — lazy children. Called ONCE per node that has no children and isLeaf !== true, the first time it is expanded; push the fetched children into treeData. Such a node still reads as expandable (aria-expanded + a working expander).",
182
+ "description": "antd `loadData` — lazy children. Called when a node that has no children and isLeaf !== true is expanded, and never again once its promise resolves; push the fetched children into treeData. A rejected promise folds the branch shut and the next expand asks again, up to 10 attempts (rc-tree, gh#1041). Such a node still reads as expandable (aria-expanded + a working expander).",
183
183
  "name": "loadData",
184
184
  "type": "(node: TreeOptionProp) => void | Promise<void>"
185
185
  },
@@ -590,6 +590,12 @@
590
590
  "description": "Fires with the next expanded parent ids when a disclosure is toggled, for controlled and uncontrolled timelines alike.",
591
591
  "name": "onExpandedValuesChange",
592
592
  "type": "(values: string[]) => void"
593
+ },
594
+ {
595
+ "defaultValue": "false",
596
+ "description": "antd Table `sticky` — keep the axis header (bands + ticks) on screen while the PAGE scrolls a long schedule. `offsetHeader` is px from the top of the scrolling viewport (the height of a fixed app topbar). The header moves out of the horizontal scroller and follows its scrollLeft; the section becomes `overflow: clip` and the body scroller takes the keyboard tab stop. Like antd, it needs no clipping ancestor between the timeline and the page scroller: `Card` is `overflow: hidden`, so a sticky timeline goes in the page as its own section, not inside a Card. antd's `offsetScroll` / `getContainer` (sticky horizontal scrollbar) are not ported.",
597
+ "name": "sticky",
598
+ "type": "boolean | { offsetHeader?: number }"
593
599
  }
594
600
  ],
595
601
  "rules": [],
@@ -3525,9 +3531,9 @@
3525
3531
  "type": "() => void"
3526
3532
  },
3527
3533
  {
3528
- "description": "Full row-selection configuration (antd rowSelection). Supersedes — and can be mixed with — selectable/selected/onSelectChange, which drive the same state. type:'radio' makes the column single-choice (no header checkbox at all). getCheckboxProps is the declared home for 'this row cannot be selected' (disabled) and for a per-row accessible name. preserveSelectedRowKeys keeps a key selected after its row leaves `data` (server paging / a filter), which is the only way a select-across-pages bulk action can be correct. selections adds bulk entries under the header checkbox (true = the built-in all · invert · none).",
3534
+ "description": "Full row-selection configuration (antd rowSelection). Supersedes — and can be mixed with — selectable/selected/onSelectChange, which drive the same state. type:'radio' makes the column single-choice (no header checkbox at all). getCheckboxProps is the declared home for 'this row cannot be selected' (disabled) and for a per-row accessible name. preserveSelectedRowKeys keeps a key selected after its row leaves `data` (server paging / a filter), which is the only way a select-across-pages bulk action can be correct. selections adds bulk entries under the header checkbox (true = the built-in all · invert · none; a list mixes antd's DataTable.SELECTION_ALL / SELECTION_INVERT / SELECTION_NONE with custom entries in the order given). selectAllLabel names the header checkbox — say what it really selects (on a server-paged table: the page) instead of replacing it through columnTitle. matching (no antd equivalent; Gmail/Jira/GitHub) is the server-paged 'select all N matching' banner: once the whole page is ticked and total exceeds the page, a polite status above the header offers 'Select all N matching rows'; choosing it reports onSelectedChange(true) — send your FILTER, not the ids, for the bulk write — and while selected every row of any page shows ticked. Unticking a row or 'Clear selection' reports false. With matching set, the header checkbox defaults to 'Select all rows on this page'.",
3529
3535
  "name": "rowSelection",
3530
- "type": "{ type?: 'checkbox'|'radio'; selectedRowKeys?: string[]; defaultSelectedRowKeys?: string[]; onChange?: (keys, rows) => void; getCheckboxProps?: (row) => { disabled?, 'aria-label'? }; preserveSelectedRowKeys?: boolean; selections?: true | { key, text, onSelect }[]; hideSelectAll?: boolean; columnTitle?: ReactNode }"
3536
+ "type": "{ type?: 'checkbox'|'radio'; selectedRowKeys?: string[]; defaultSelectedRowKeys?: string[]; onChange?: (keys, rows) => void; getCheckboxProps?: (row) => { disabled?, 'aria-label'? }; preserveSelectedRowKeys?: boolean; selections?: true | ({ key, text, onSelect } | DataTable.SELECTION_ALL | DataTable.SELECTION_INVERT | DataTable.SELECTION_NONE)[]; hideSelectAll?: boolean; columnTitle?: ReactNode; selectAllLabel?: string; matching?: { total: number; selected: boolean; onSelectedChange: (selected: boolean) => void } }"
3531
3537
  },
3532
3538
  {
3533
3539
  "description": "Expandable detail rows (antd expandable). Supplying expandedRowRender adds a leading expand column before the selection column and renders the panel in a real <tr> spanning every column, so the table's grid semantics survive. rowExpandable gates the affordance per row; expandedRowKeys + onExpandedRowsChange make it controlled.",
@@ -5346,6 +5352,11 @@
5346
5352
  "name": "children",
5347
5353
  "required": true,
5348
5354
  "type": "(flat, helpers) => ReactNode"
5355
+ },
5356
+ {
5357
+ "description": "Idle text of the built-in load-more button (default: localized `query.loadMore`, 「さらに読み込む」) — e.g. 「さらに表示」「さらに古い版を表示」. The button, its pending label and disabled-while-fetching stay the component's. Ignored when `loadMore` replaces the footer.",
5358
+ "name": "loadMoreLabel",
5359
+ "type": "ReactNode"
5349
5360
  }
5350
5361
  ],
5351
5362
  "related": [
@@ -5362,7 +5373,7 @@
5362
5373
  "DO: Always pass `skeleton` (e.g. `<SkeletonTable />` or `<SkeletonStat />`). It shows on initial `isPending`, on refetch-after-error, and whenever `data` is absent. Never show a blank area while loading.",
5363
5374
  "DO: Pass `empty` (an `<EmptyState>` node) to handle the zero-results case — without it the children render-prop is called with an empty array and you get a silent blank screen. Provide a custom `isEmpty` only when `TFlat` is not an array.",
5364
5375
  "DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; a 401 is handled by the app-root `AuthExpiryProvider` (auto, once, skeleton + live region) or, without one, by the `onAuthError` sign-in button; raw backend/token text is never rendered.",
5365
- "DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Override only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
5376
+ "DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Rename it with `loadMoreLabel` (keeps the pending state); replace it only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
5366
5377
  "DON'T: Use `InfiniteQueryState` for a `useQuery` result — it expects `UseInfiniteQueryResult` shape (`pages`, `hasNextPage`, `fetchNextPage`, `isFetchingNextPage`). For regular `useQuery` use `DataState` instead.",
5367
5378
  "DON'T: Confuse the two generics: `TPage` is the raw page shape from the API, `TFlat` is what `flatten` returns (usually `TItem[]`). The `children` render-prop receives `TFlat`, not `TPage`. Pass `isEmpty` if `TFlat` is not a plain array so empty detection works correctly."
5368
5379
  ],
@@ -6117,6 +6128,16 @@
6117
6128
  "name": "dialogTitle",
6118
6129
  "type": "string"
6119
6130
  },
6131
+ {
6132
+ "description": "antd `notFoundContent` — shown when the search finds nothing, in the dropdown shape and the dialog alike (default: localized `dataEntry.recordPicker.empty`). Say WHAT has no match, e.g. 「該当するファイルはありません」.",
6133
+ "name": "notFoundContent",
6134
+ "type": "ReactNode"
6135
+ },
6136
+ {
6137
+ "description": "Text of the dialog's load-more button (default: localized `dataEntry.recordPicker.more`). Only the label — the button still appears only while the server returns a `nextCursor`.",
6138
+ "name": "loadMoreLabel",
6139
+ "type": "ReactNode"
6140
+ },
6120
6141
  {
6121
6142
  "description": "Control height of the trigger, on the shared control ladder.",
6122
6143
  "name": "size",
@@ -9078,7 +9099,7 @@
9078
9099
  "type": "\"SHOW_CHILD\" | \"SHOW_PARENT\""
9079
9100
  },
9080
9101
  {
9081
- "description": "antd `loadData` — lazy children. Called ONCE per branch that has no children and isLeaf !== true, the first time it is expanded; push the fetched children into options.",
9102
+ "description": "antd `loadData` — lazy children. Called when a branch that has no children and isLeaf !== true is expanded, and never again once its promise resolves; push the fetched children into options. A rejected promise is forgotten, so the next activation of the branch asks again (gh#1041).",
9082
9103
  "name": "loadData",
9083
9104
  "type": "(selectedOptions: TreeOptionProp[]) => void | Promise<void>"
9084
9105
  },
@@ -9319,7 +9340,7 @@
9319
9340
  "type": "boolean"
9320
9341
  },
9321
9342
  {
9322
- "description": "antd `loadData` — lazy children. Called ONCE per node that has no children and isLeaf !== true, the first time it is expanded; push the fetched children into treeData. Such a node still reads as expandable (aria-expanded + a working expander).",
9343
+ "description": "antd `loadData` — lazy children. Called when a node that has no children and isLeaf !== true is expanded, and never again once its promise resolves; push the fetched children into treeData. A rejected promise folds the branch shut and the next expand asks again, up to 10 attempts (rc-tree, gh#1041). Such a node still reads as expandable (aria-expanded + a working expander).",
9323
9344
  "name": "loadData",
9324
9345
  "type": "(node: TreeOptionProp) => void | Promise<void>"
9325
9346
  },
@@ -9481,6 +9502,11 @@
9481
9502
  "name": "onSelectChange",
9482
9503
  "type": "(sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void"
9483
9504
  },
9505
+ {
9506
+ "description": "antd `locale`, ported for `notFoundContent` only: what an empty or search-emptied pane shows (default: localized `dataEntry.transfer.empty`). A `[source, target]` pair sets each pane separately, as in antd.",
9507
+ "name": "locale",
9508
+ "type": "{ notFoundContent?: ReactNode | [ReactNode, ReactNode] }"
9509
+ },
9484
9510
  {
9485
9511
  "description": "Fires when items move between panels; you own `targetKeys` state.",
9486
9512
  "name": "onValueChange",
@@ -11046,7 +11072,7 @@
11046
11072
  "type": "boolean"
11047
11073
  },
11048
11074
  {
11049
- "description": "Lazy children (antd `loadData`). Called ONCE per node the first time a branch with no `children` and `isLeaf !== true` is expanded; a Skeleton row and `aria-busy` cover the wait. Push the fetched children into `treeData`.",
11075
+ "description": "Lazy children (antd `loadData`). Called when a branch with no `children` and `isLeaf !== true` is expanded, and never again once its promise RESOLVES; a Skeleton row and `aria-busy` cover the wait. Push the fetched children into `treeData`. A REJECTED promise is not a load (rc-tree, gh#1041): the branch folds back shut (uncontrolled expansion) and the next expand asks again, up to 10 attempts.",
11050
11076
  "name": "loadData",
11051
11077
  "type": "(node: TreeNodeProp) => void | Promise<void>"
11052
11078
  },
@@ -11055,6 +11081,22 @@
11055
11081
  "name": "titleRender",
11056
11082
  "type": "(node: TreeNodeProp) => ReactNode"
11057
11083
  },
11084
+ {
11085
+ "description": "Highlight the nodes this returns true for (antd `filterTreeNode`, gh#1043) — the match of a search box above the tree. Matching rows are MARKED, not hidden: `data-filter-node=\"true\"`, the label in `--tree-node-filter-foreground` (default `hsl(var(--primary))`) at the medium weight, plus sr-only \"matches the filter\" text. Outline, keyboard and ARIA are unchanged. To also open the branches that hold a match, drive `expandedValues`.",
11086
+ "name": "filterTreeNode",
11087
+ "type": "(node: TreeNodeProp) => boolean"
11088
+ },
11089
+ {
11090
+ "description": "Viewport height in px (antd `height`, gh#1042). The tree scrolls inside it and, unless `virtual={false}`, renders only the rows in view plus a small overscan — the windowed rows stay a flat run of treeitems whose `aria-level`/`aria-setsize`/`aria-posinset` still describe the whole outline. Rows must share one height (the `size` tier).",
11091
+ "name": "height",
11092
+ "type": "number"
11093
+ },
11094
+ {
11095
+ "defaultValue": "true",
11096
+ "description": "Set `false` to keep the `height` viewport but render every row (antd `virtual`).",
11097
+ "name": "virtual",
11098
+ "type": "boolean"
11099
+ },
11058
11100
  {
11059
11101
  "defaultValue": "false",
11060
11102
  "description": "Draw the connector rails between a parent and its children (antd `showLine`). Off by default — a rail is chrome, and chrome defaults quiet.",
@@ -11112,7 +11154,7 @@
11112
11154
  "TreeSelect — the same hierarchy INSIDE a Popover, as a form field. Use TreeSelect when the answer is a value in a form; use Tree when the hierarchy itself is the page.",
11113
11155
  "Cascader — a path picker across columns. Use it when the user walks one path to a leaf; use Tree when several branches are open at once.",
11114
11156
  "Accordion — single-level disclosure with rich panel content. It is not a hierarchy and has no tree keyboard model.",
11115
- "ScrollArea — wrap Tree in one to cap a long outline; Tree does not virtualise in v1."
11157
+ "ScrollArea — not needed around Tree: pass `height`, and the tree is its own (windowed) scroll viewport."
11116
11158
  ],
11117
11159
  "rules": [
11118
11160
  2,
@@ -11132,8 +11174,9 @@
11132
11174
  "DON'T nest a Button, Checkbox, Link or any focusable control inside a node label. A tree item owns exactly ONE tab stop; the disclosure triangle and the tick box are decorative glyphs for that reason. Put row actions in a sibling column outside the tree, or open a detail pane on selection.",
11133
11175
  "DON'T hand-roll an indented `<ul>` (or a NavList / ListRow stack with a per-depth margin) for a hierarchy. A flat indented list only LOOKS like a tree: no expand/collapse, no `role=\"tree\"`, no keyboard model, no selection contract. `TreeList` was exactly that list and was REMOVED in 21.0.0 — Tree is what replaced it, and it is the one to reach for whenever nodes expand, collapse or are keyboard-navigated.",
11134
11176
  "DO pass `divided` when the tree IS the navigation of a page — a wiki/document outline or a section index sitting in a `Card` (`Card` > `CardContent flush` > `Tree divided`). Without a rule the rows run together and the outline reads as one block; with it, it reads as the ruled list ListRow and Table already give a flat list. Retint it per theme with `--tree-divider-color`, never with a per-page utility class.",
11135
- "DO cap a long tree with `ScrollArea` — virtualisation is not in v1, so a 5,000-node tree renders 5,000 rows.",
11136
- "DO push fetched children into `treeData` from `loadData`; the tree calls it once per node and shows a Skeleton row until the data lands."
11177
+ "DO pass `height` for a long tree (antd `height` + `virtual`): the tree scrolls inside that height and keeps only the rows in view in the DOM, so a 421-child level renders a few dozen rows. Do not wrap it in `ScrollArea` as well — the tree is its own viewport.",
11178
+ "DO push fetched children into `treeData` from `loadData`; the tree calls it once per node that loads and shows a Skeleton row until the data lands. Reject the promise on failure — the branch folds shut and the next expand retries.",
11179
+ "DON'T wait for a paged \"load more\" node — antd's Tree has none, and `height` covers a long list. If the API itself is paged, append a leaf node such as `{ value: `${parent}::more`, label: t(\"…load more\"), isLeaf: true }` to the loaded children and fetch the next page from `onValueChange` when it is selected: it is a real treeitem, so it is keyboard-reachable and announced."
11137
11180
  ],
11138
11181
  "useCases": [
11139
11182
  "A permission tree: modules → resources → actions with tri-state checkboxes, where ticking a module ticks everything under it and a partly-granted module shows the dash.",
@@ -14655,6 +14698,11 @@
14655
14698
  "name": "searchable",
14656
14699
  "type": "boolean"
14657
14700
  },
14701
+ {
14702
+ "description": "antd `notFoundContent` — shown when the branch SEARCH matches nothing (default: localized `dataEntry.branchScope.noMatches`). An empty `branches` list is `empty`'s job, not this.",
14703
+ "name": "notFoundContent",
14704
+ "type": "ReactNode"
14705
+ },
14658
14706
  {
14659
14707
  "description": "Override the localized radio labels (e.g. domain wording like 全店舗).",
14660
14708
  "name": "allLabel / selectedLabel",
package/agent/index.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "components": 177,
5
5
  "patterns": 21,
6
6
  "rules": 50,
7
- "tokens": 2098,
7
+ "tokens": 2099,
8
8
  "vocabulary": 14
9
9
  },
10
10
  "files": [
@@ -15,7 +15,7 @@
15
15
  },
16
16
  {
17
17
  "file": "components/<Name>.json",
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.",
18
+ "note": "One file per component (1 KB–36 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,19 +48,19 @@
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.5.0/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.6.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",
55
55
  "tokenTiers": {
56
56
  "component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
57
57
  "counts": {
58
- "component": 1783,
58
+ "component": 1784,
59
59
  "foundation": 211,
60
60
  "semantic": 104
61
61
  },
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.5.0"
65
+ "version": "31.6.0"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
- > A Japanese-enterprise React design system: 177 components, 2098 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.5.0.
3
+ > A Japanese-enterprise React design system: 177 components, 2099 design tokens,
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.6.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.5.0`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.6.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,9 +16,9 @@ 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): 47 KB — all 177 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
19
- - [components/&lt;Name&gt;.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.
19
+ - [components/&lt;Name&gt;.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–36 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
- - [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), 104 `semantic` roles, 1783 `component` knobs.
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), 104 `semantic` roles, 1784 `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.
23
23
  - [rules.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json): 50 cardinal rules.
24
24
  - [anti-ai-tells.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/anti-ai-tells.json): 26 shapes that make generated UI look generated, each with its fix.
@@ -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.5.0/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.6.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.
package/agent/tokens.json CHANGED
@@ -12191,6 +12191,12 @@
12191
12191
  "tier": "component",
12192
12192
  "value": "initial"
12193
12193
  },
12194
+ {
12195
+ "description": "Label of a node `filterTreeNode` matched (antd `.filter-node` title: primary colour, strong weight). A role mirror, so `initial` with the role at the call site. Documented default: --tree-node-filter-foreground = hsl(var(--text-brand, var(--primary))).",
12196
+ "name": "--tree-node-filter-foreground",
12197
+ "tier": "component",
12198
+ "value": "initial"
12199
+ },
12194
12200
  {
12195
12201
  "description": "Width of the indeterminate bar shown while `loadData` resolves a branch. A fraction, not a length: it has to read as \"part of a row\" at every row width and every density.",
12196
12202
  "name": "--tree-loading-bar-inline-size",
@@ -175,6 +175,9 @@ export declare namespace DataTable {
175
175
  label?: React.ReactNode;
176
176
  }) => React.JSX.Element | null;
177
177
  export var SelectAll: () => React.JSX.Element | null;
178
+ export var SELECTION_ALL: "SELECT_ALL";
179
+ export var SELECTION_INVERT: "SELECT_INVERT";
180
+ export var SELECTION_NONE: "SELECT_NONE";
178
181
  export var BulkActions: ({ count, children, className, }: BulkActionsProps) => React.JSX.Element | null;
179
182
  export var DensityToggle: () => React.JSX.Element;
180
183
  export var Content: () => React.JSX.Element;