@godxjp/ui 33.2.1 → 33.4.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 +5 -5
- package/agent/START-HERE.md +6 -6
- package/agent/components/ResponsiveGrid.json +2 -2
- package/agent/components/SearchForm.json +115 -0
- package/agent/components/Toolbar.json +6 -3
- package/agent/components/TopbarItem.json +1 -1
- package/agent/components-index.json +10 -1
- package/agent/components.json +124 -6
- package/agent/index.json +7 -7
- package/agent/llms.txt +7 -7
- package/agent/patterns/condition-builder.json +31 -0
- package/agent/patterns/data-table-page.json +1 -1
- package/agent/patterns/filter-bar-layouts.json +30 -0
- package/agent/patterns/inertia-list-page.json +1 -1
- package/agent/patterns/list-filters.json +1 -1
- package/agent/patterns/search-form.json +27 -0
- package/agent/patterns-index.json +85 -0
- package/agent/patterns.json +91 -3
- package/agent/rules.json +1 -1
- package/agent/tokens.json +16 -4
- package/dist/components/data-display/popover.js +8 -6
- package/dist/components/feedback/overlay-close-focus.js +3 -2
- package/dist/components/feedback/tooltip.js +2 -1
- package/dist/components/layout/topbar-item.js +1 -1
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/search-form.d.ts +31 -0
- package/dist/components/navigation/search-form.js +112 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +9 -0
- package/dist/i18n/messages/ja.json +9 -0
- package/dist/i18n/messages/vi.json +9 -0
- package/dist/lab/mega-menu.js +3 -2
- package/dist/lib/active-element.d.ts +12 -0
- package/dist/lib/active-element.js +10 -0
- package/dist/lib/shortcut.js +3 -2
- package/dist/props/components/layout.prop.d.ts +6 -1
- package/dist/props/components/navigation.prop.d.ts +43 -3
- package/dist/props/registry.d.ts +5 -0
- package/dist/props/registry.js +12 -0
- package/dist/styles/layers.json +11 -1
- package/dist/styles/layout.css +41 -0
- package/dist/styles/shell-layout.css +19 -3
- package/dist/styles.css +1 -1
- package/dist/tokens/components/shell.css +4 -2
- package/docs/CONSUMER-RULES.md +1 -1
- package/package.json +2 -2
- package/scripts/ui-audit.mjs +148 -3
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 — 166 components, 2232 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,12 +53,12 @@ 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) | 17 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) |
|
|
56
|
+
| [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 50 KB | all 166 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 166 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) | 858 KB | all 2232 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
|
-
| [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) |
|
|
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 -->
|
|
63
63
|
<!-- agent-catalog-files:end -->
|
|
64
64
|
|
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.4.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.
|
|
@@ -48,11 +48,11 @@ inspect every screenshot at every width region by region (no top-N cap), and han
|
|
|
48
48
|
→ These files are for you. Read `DESIGN.md` (in this folder) first: tokens, scarcity rules and
|
|
49
49
|
screen anatomy, the visual target the rules below do not give you. Then fetch in this order:
|
|
50
50
|
|
|
51
|
-
0. `patterns-index.json` —
|
|
51
|
+
0. `patterns-index.json` — 28 whole-task patterns as name + tagline + tags. **If your
|
|
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` —
|
|
55
|
+
1. `components-index.json` — 50 KB, all 166 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` — 2232 design tokens, each tagged with its `tier`. **If you were handed a
|
|
65
65
|
brand, read the 216 `foundation` entries first** — `--primary`, `--background`,
|
|
66
66
|
`--radius`, `--font-size-base` are the handful everything else derives from. The
|
|
67
|
-
|
|
67
|
+
1903 `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` | 216 | 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` | 1903 | 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.
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"type": "\"rows\" | \"columns\""
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
"description": "antd `Row align`, spelled with Flex's FlexAlignProp values (antd `top` → \"start\"). \"stretch\" makes every cell as tall as the tallest one. Omitted, flow=\"columns\" keeps \"start\" and flow=\"rows\" keeps the grid's natural stretch.",
|
|
24
|
+
"description": "antd `Row align`, spelled with Flex's FlexAlignProp values (antd `top` → \"start\", `middle` → \"center\"). \"center\" puts cells of different heights on one centre line (a condition row's number and +/− buttons beside taller controls). \"stretch\" makes every cell as tall as the tallest one. Omitted, flow=\"columns\" keeps \"start\" and flow=\"rows\" keeps the grid's natural stretch.",
|
|
25
25
|
"name": "align",
|
|
26
|
-
"type": "\"start\" | \"stretch\""
|
|
26
|
+
"type": "\"start\" | \"center\" | \"stretch\""
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"defaultValue": "4",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"absorbed": [
|
|
3
|
+
"QueryFilter",
|
|
4
|
+
"SearchPanel"
|
|
5
|
+
],
|
|
6
|
+
"docPath": "navigation/search-form",
|
|
7
|
+
"example": "import { SearchForm } from \"@godxjp/ui/navigation\";\nimport { FormField, Input, Select, DatePicker } from \"@godxjp/ui/data-entry\";\n\n<SearchForm\n controlWidth=\"100%\"\n onSubmit={(e) => { e.preventDefault(); setApplied(draft); }}\n onReset={() => { setDraft(EMPTY); setApplied(EMPTY); }}\n summary=\"国籍: ベトナム / 状態: 実習中\"\n resultCount={rows.length}\n>\n <FormField label=\"氏名\" id=\"q-name\"><Input id=\"q-name\" value={draft.name} onChange={(e) => set({ name: e.target.value })} /></FormField>\n <FormField label=\"国籍\" id=\"q-nat\"><Select id=\"q-nat\" options={nationalities} value={draft.nationality} onValueChange={(v) => set({ nationality: v })} /></FormField>\n <FormField label=\"入国日\" id=\"q-entry\"><DatePicker id=\"q-entry\" range /></FormField>\n <FormField label=\"職種\" id=\"q-occ\"><Select id=\"q-occ\" options={occupations} value={draft.occupation} onValueChange={(v) => set({ occupation: v })} /></FormField>\n</SearchForm>",
|
|
8
|
+
"group": "navigation",
|
|
9
|
+
"importPath": "@godxjp/ui/navigation",
|
|
10
|
+
"name": "SearchForm",
|
|
11
|
+
"props": [
|
|
12
|
+
{
|
|
13
|
+
"description": "One FormField per condition, in reading order. Each direct child is one grid cell; a range (「日付」 From〜To, 「金額」 「下限〜上限」) is ONE FormField holding both controls.",
|
|
14
|
+
"name": "children",
|
|
15
|
+
"required": true,
|
|
16
|
+
"type": "ReactNode"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"defaultValue": "{ base: 1, md: 2, lg: 3 }",
|
|
20
|
+
"description": "Grid columns per container breakpoint — one column on a phone, aligned columns above. Conditions never spread to the edges.",
|
|
21
|
+
"name": "columns",
|
|
22
|
+
"type": "ResponsiveGridColumnsProp"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "Conditions shown while collapsed (antd `defaultFormItemsNumber`). Default: the widest `columns` step, i.e. the first row.",
|
|
26
|
+
"name": "visibleFields",
|
|
27
|
+
"type": "number"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "Controlled: whether the conditions past `visibleFields` are shown.",
|
|
31
|
+
"name": "open",
|
|
32
|
+
"type": "boolean"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"defaultValue": "false",
|
|
36
|
+
"description": "Uncontrolled initial state — collapsed to the first row by default.",
|
|
37
|
+
"name": "defaultOpen",
|
|
38
|
+
"type": "boolean"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"description": "Fires when the 「詳細条件を開く」 / 「閉じる」 toggle is pressed.",
|
|
42
|
+
"name": "onOpenChange",
|
|
43
|
+
"type": "(open: boolean) => void"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"description": "Search (「検索」) — the native form submit. Call `preventDefault()` and copy the draft conditions into the applied query; the form never searches on each keystroke.",
|
|
47
|
+
"name": "onSubmit",
|
|
48
|
+
"type": "FormEventHandler<HTMLFormElement>"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"description": "Clear (「クリア」). The button renders only when this is set.",
|
|
52
|
+
"name": "onReset",
|
|
53
|
+
"type": "() => void"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"description": "The conditions the CURRENT results were searched with, shown after the localized 「検索条件」 label in the status line (e.g. 「国「籍」: 「ベトナム」 / 「状態」: 「実習」中」).",
|
|
57
|
+
"name": "summary",
|
|
58
|
+
"type": "ReactNode"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"description": "Localized, pluralized result count in the same `role=\"status\"` line.",
|
|
62
|
+
"name": "resultCount",
|
|
63
|
+
"type": "number"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"description": "Puts the Search button in its loading state while the query runs.",
|
|
67
|
+
"name": "loading",
|
|
68
|
+
"type": "boolean"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"description": "Overrides the localized Search (「検索」) text.",
|
|
72
|
+
"name": "searchLabel",
|
|
73
|
+
"type": "ReactNode"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"description": "Overrides the localized Clear (「クリア」) text.",
|
|
77
|
+
"name": "resetLabel",
|
|
78
|
+
"type": "ReactNode"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"description": "Accessible name of the `search` landmark (default: localized 'Search conditions').",
|
|
82
|
+
"name": "label",
|
|
83
|
+
"type": "string"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"description": "Passed to the underlying Form: `layout=\"vertical\"` (default) is label-top; `layout=\"horizontal\"` + `labelWidth` is the label-left table style of Japanese business systems. Set `controlWidth` once here so Selects fill their column.",
|
|
87
|
+
"name": "layout / labelWidth / controlWidth / labelAlign",
|
|
88
|
+
"type": "Form props"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"related": [
|
|
92
|
+
"Toolbar — FilterBar, the live filter strip; use it for a few filters applied as the user changes them.",
|
|
93
|
+
"Form — the layout container SearchForm renders (`role=\"search\"`); its `layout`, `labelWidth` and `controlWidth` pass through.",
|
|
94
|
+
"FormField — one per condition; a range is one FormField holding two controls.",
|
|
95
|
+
"DataTable — the results under the form."
|
|
96
|
+
],
|
|
97
|
+
"rules": [
|
|
98
|
+
23,
|
|
99
|
+
51
|
|
100
|
+
],
|
|
101
|
+
"storyPath": "navigation/search-form.tsx",
|
|
102
|
+
"tagline": "Submit-driven 「検索条件フォーム」 (Ant Design ProComponents QueryFilter): a `search` landmark holding a grid of labelled conditions aligned in columns, Search 「検索」 + Clear 「クリア」, the conditions past the first row behind ONE 「詳細条件を開く」 toggle that names how many are hidden, and a 「検索条件」 summary + result count in a role=\"status\" line after the search. Use it instead of a live FilterBar when the conditions are many, the query is heavy, or users expect an explicit 「検索」 button (rule #51 decision table).",
|
|
103
|
+
"usage": [
|
|
104
|
+
"DO pick the shape from rule #51's decision table: a few filters applied instantly → FilterBar; many conditions / a heavy query / users who expect an explicit 「検索」 button → SearchForm; arbitrary field × operator conditions saved as views → pattern `condition-builder`.",
|
|
105
|
+
"DO put one FormField per condition as a direct child. Ranges (「入国日」 From〜To, 「金額」 「下限〜上限」) are ONE FormField with both controls; 「部分一致」 / 「完全一致」 is a RadioGroup inside the name field.",
|
|
106
|
+
"DO keep a draft and an applied copy of the conditions: inputs edit the draft, onSubmit copies it to the applied query, onReset clears both. Pass the applied conditions as `summary` and the hit count as `resultCount`.",
|
|
107
|
+
"DO NOT wrap it in another Form, and do not rebuild it as a Form + ResponsiveGrid + Collapsible by hand — the collapse, the hidden-count toggle, the search landmark and the status line are what this component owns. Hidden conditions stay mounted, so a typed value survives collapsing.",
|
|
108
|
+
"A SearchForm (or any `<Form role=\"search\" onSubmit>`) above a table is NOT flagged by ui-audit `form-on-list-page` or `hand-rolled-filter-bar`; a submitting create form without role=\"search\" still is."
|
|
109
|
+
],
|
|
110
|
+
"useCases": [
|
|
111
|
+
"「検索条件フォーム」 on a Japanese business-system list (「基幹系」 / 「業務システム」): 6–12 conditions, 「検索」 and 「クリア」 buttons, 「詳細条件を開く」.",
|
|
112
|
+
"Search screens over a heavy query where searching on every keystroke would be too expensive.",
|
|
113
|
+
"Label-left (「表形式」) or label-top search panels above a DataTable, with the applied conditions summarized after the search."
|
|
114
|
+
]
|
|
115
|
+
}
|
|
@@ -101,7 +101,8 @@
|
|
|
101
101
|
"DataTable.Toolbar — the in-table strip for column/density/bulk-action controls; Toolbar (this) is the page-level filter strip that sits ABOVE the table.",
|
|
102
102
|
"SearchInput — the debounced free-text control placed as the first child of Toolbar.",
|
|
103
103
|
"Badge — compose Badge + a sibling icon Button to render each active-filter chip; Badge itself is a non-interactive leaf.",
|
|
104
|
-
"Sheet — holds the filters beyond the 2–4 inline ones, opened by ONE 'More filters' Button inside the bar (pattern `list-filters`, rule #51). Not a ScrollArea: a filter strip never scrolls sideways."
|
|
104
|
+
"Sheet — holds the filters beyond the 2–4 inline ones, opened by ONE 'More filters' Button inside the bar (pattern `list-filters`, rule #51). Not a ScrollArea: a filter strip never scrolls sideways.",
|
|
105
|
+
"SearchForm — the submit-driven 「検索条件フォーム」 for many conditions or an explicit 「検索」 button (rule #51 decision table); FilterBar is the live strip."
|
|
105
106
|
],
|
|
106
107
|
"rules": [
|
|
107
108
|
23,
|
|
@@ -116,7 +117,7 @@
|
|
|
116
117
|
"FilterBar",
|
|
117
118
|
"FilterBarGroup"
|
|
118
119
|
],
|
|
119
|
-
"tagline": "List-page filter strip — SearchInput + labelled ToolbarGroup filter slots (or the typed `search`/`filters`/`chips` model) + a clear-all affordance, optionally sticky. It WRAPS and never scrolls sideways; many filters → 2–4 inline + ONE 'More filters' Sheet (rule #51, pattern `list-filters`). `FilterBar`/`FilterBarGroup` are the same components under their alias names.",
|
|
120
|
+
"tagline": "List-page filter strip — SearchInput + labelled ToolbarGroup filter slots (or the typed `search`/`filters`/`chips` model) + a clear-all affordance, optionally sticky. It WRAPS and never scrolls sideways; many filters → 2–4 inline + ONE 'More filters' Sheet (rule #51, pattern `list-filters`). `FilterBar`/`FilterBarGroup` are the same components under their alias names. Eleven layouts (inline, Filter menu + chips, More-filters Sheet, saved views, two rows, actions + sticky, faceted sidebar, compact in a card, date range, phone, docked panel) → pattern `filter-bar-layouts`.",
|
|
120
121
|
"usage": [
|
|
121
122
|
"DO prefer the typed model on a canonical list page — `search`/`filters`/`chips`/`onChipRemove`/`actions`/`resultCount` (+ `loading`/`disabled`/`error`) make the bar own layout, widths, chip lifecycle and keyboard order through --filter-bar-* tokens while ALL state stays consumer data. Children composition remains fully supported and unchanged when no model prop is passed.",
|
|
122
123
|
"DO place Toolbar ABOVE the table Card, never inside a `CardContent flush`. Put SearchInput as a direct child (it self-labels) and wrap every other control in a ToolbarGroup with a `label`.",
|
|
@@ -124,6 +125,7 @@
|
|
|
124
125
|
"DO set `sticky` for long list pages so the filters stay reachable while scrolling; if a topbar sits above the list, raise `--filter-bar-sticky-offset` so the strip parks below it.",
|
|
125
126
|
"DO pass `controlId` on a ToolbarGroup that wraps ONE control and give that control the same `id` — the visible caption then becomes the control's real `<label htmlFor>` (WCAG 2.5.3 label-in-name). Without it the caption only names the group wrapper and the control itself is NAMELESS (axe: select-name / button-name), so you must fall back to an `aria-label` that repeats the caption.",
|
|
126
127
|
"DO let the strip WRAP — cardinal rule #51 (`get_rule 51`). It stacks below 640px and wraps onto extra rows above; it never scrolls sideways, and neither may anything you wrap it in (`ScrollArea orientation=\"horizontal\"`, an `overflow-x-auto` box): the `overflow` prop and its `scroll` strategy were removed in 33.0.0 because a sideways strip hid filters off-screen and clipped the first label. When the filters are many, keep the 2–4 most-used ones inline and put the rest behind ONE 'More filters' Button (`count` = hidden filters applied) that opens a Sheet holding them as FilterBarGroup + control; every applied filter, inline or hidden, is a chip in `chips` (the chips row wraps too). Copy-paste composition: pattern `list-filters` (`get_pattern list-filters`); there is no dedicated component. Never re-implement the geometry in the page: no page-local flex/grid/width/overflow rules on a filter strip.",
|
|
128
|
+
"DO keep filters PACKED at the start of the bar with its one gap. Never lay them out yourself in a Flex/div/Card/ResponsiveGrid of FormFields above the table, and never spread them with `justify=\"between\"|\"around\"|\"evenly\"` — the only far-end element of a filter bar is its `actions` slot (with the clear button). ui-audit `hand-rolled-filter-bar` flags both. For a layout other than one strip (saved views, a Filter menu with chips, a faceted sidebar, a docked side panel, a compact bar inside a table card, a date range with quick ranges, a phone Sheet), copy pattern `filter-bar-layouts` (docs page navigation/filter-bar-layouts).",
|
|
127
129
|
"DON'T build active-filter chips by nesting a Button inside a Badge (invalid markup + broken focus). Render each chip as a Badge label with a SIBLING icon Button (`aria-label` = 'clear <filter>'); a ghost `size='sm'` Button clears all.",
|
|
128
130
|
"DON'T hand-roll a debounced search box or a raw `<select>` — compose SearchInput and Select. Toolbar is layout + clear-all only; the controls own their own state and a11y.",
|
|
129
131
|
"Prefer the typed model for a canonical list page: pass `search` + `filters` + `chips` + `resultCount` and the bar owns the layout, widths, chip lifecycle, keyboard order (search → filters → children → reset → actions → chip removes) and responsive stacking through --filter-bar-* tokens. Everything stays consumer data — the bar renders state, never owns it.",
|
|
@@ -134,6 +136,7 @@
|
|
|
134
136
|
"useCases": [
|
|
135
137
|
"Master list screens (members, organizations, subscriptions, invoices) that need free-text search plus a few dropdown filters above a DataTable.",
|
|
136
138
|
"Sticky filter strip over a tall list where the filters must remain visible as the user scrolls the results.",
|
|
137
|
-
"Filtered views that surface applied conditions as removable chips (clear-one via each chip's × Button, clear-all via `onClear`)."
|
|
139
|
+
"Filtered views that surface applied conditions as removable chips (clear-one via each chip's × Button, clear-all via `onClear`).",
|
|
140
|
+
"Filter layout choices — saved views filter tabs above the bar, a search-first bar with one Filter menu, a faceted filter sidebar with per-facet counts, a docked filter panel beside the table, a compact table toolbar, a date range filter with quick ranges, a phone filter Sheet: pattern `filter-bar-layouts`."
|
|
138
141
|
]
|
|
139
142
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"type": "BreakpointProp"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
"description": "Overlaid count; does not change the bar cell width. Rendered under asChild too (gh#1171): the pill goes inside the child element after its children, so `<TopbarItem asChild badge={3}><a href=\"/notifications\">…</a></TopbarItem>` keeps its count.",
|
|
23
|
+
"description": "Overlaid count; does not change the bar cell width. Rendered under asChild too (gh#1171): the pill goes inside the child element after its children, so `<TopbarItem asChild badge={3}><a href=\"/notifications\">…</a></TopbarItem>` keeps its count. Nothing renders for `undefined`, `\"\"` or the number `0` (gh#1341) — pass the count as it is, never `count === 0 ? undefined : count`; pass the string `\"0\"` to show a zero on purpose.",
|
|
24
24
|
"name": "badge",
|
|
25
25
|
"type": "ReactNode"
|
|
26
26
|
},
|
|
@@ -493,7 +493,16 @@
|
|
|
493
493
|
{
|
|
494
494
|
"group": "navigation",
|
|
495
495
|
"name": "Toolbar",
|
|
496
|
-
"tagline": "List-page filter strip — SearchInput + labelled ToolbarGroup filter slots (or the typed `search`/`filters`/`chips` model) + a clear-all affordance, optionally sticky. It WRAPS and never scrolls sideways; many filters → 2–4 inline + ONE 'More filters' Sheet (rule #51, pattern `list-filters`). `FilterBar`/`FilterBarGroup` are the same components under their alias names."
|
|
496
|
+
"tagline": "List-page filter strip — SearchInput + labelled ToolbarGroup filter slots (or the typed `search`/`filters`/`chips` model) + a clear-all affordance, optionally sticky. It WRAPS and never scrolls sideways; many filters → 2–4 inline + ONE 'More filters' Sheet (rule #51, pattern `list-filters`). `FilterBar`/`FilterBarGroup` are the same components under their alias names. Eleven layouts (inline, Filter menu + chips, More-filters Sheet, saved views, two rows, actions + sticky, faceted sidebar, compact in a card, date range, phone, docked panel) → pattern `filter-bar-layouts`."
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"absorbed": [
|
|
500
|
+
"QueryFilter",
|
|
501
|
+
"SearchPanel"
|
|
502
|
+
],
|
|
503
|
+
"group": "navigation",
|
|
504
|
+
"name": "SearchForm",
|
|
505
|
+
"tagline": "Submit-driven 「検索条件フォーム」 (Ant Design ProComponents QueryFilter): a `search` landmark holding a grid of labelled conditions aligned in columns, Search 「検索」 + Clear 「クリア」, the conditions past the first row behind ONE 「詳細条件を開く」 toggle that names how many are hidden, and a 「検索条件」 summary + result count in a role=\"status\" line after the search. Use it instead of a live FilterBar when the conditions are many, the query is heavy, or users expect an explicit 「検索」 button (rule #51 decision table)."
|
|
497
506
|
},
|
|
498
507
|
{
|
|
499
508
|
"group": "providers",
|
package/agent/components.json
CHANGED
|
@@ -1123,9 +1123,9 @@
|
|
|
1123
1123
|
"type": "\"rows\" | \"columns\""
|
|
1124
1124
|
},
|
|
1125
1125
|
{
|
|
1126
|
-
"description": "antd `Row align`, spelled with Flex's FlexAlignProp values (antd `top` → \"start\"). \"stretch\" makes every cell as tall as the tallest one. Omitted, flow=\"columns\" keeps \"start\" and flow=\"rows\" keeps the grid's natural stretch.",
|
|
1126
|
+
"description": "antd `Row align`, spelled with Flex's FlexAlignProp values (antd `top` → \"start\", `middle` → \"center\"). \"center\" puts cells of different heights on one centre line (a condition row's number and +/− buttons beside taller controls). \"stretch\" makes every cell as tall as the tallest one. Omitted, flow=\"columns\" keeps \"start\" and flow=\"rows\" keeps the grid's natural stretch.",
|
|
1127
1127
|
"name": "align",
|
|
1128
|
-
"type": "\"start\" | \"stretch\""
|
|
1128
|
+
"type": "\"start\" | \"center\" | \"stretch\""
|
|
1129
1129
|
},
|
|
1130
1130
|
{
|
|
1131
1131
|
"defaultValue": "4",
|
|
@@ -1791,7 +1791,7 @@
|
|
|
1791
1791
|
"type": "BreakpointProp"
|
|
1792
1792
|
},
|
|
1793
1793
|
{
|
|
1794
|
-
"description": "Overlaid count; does not change the bar cell width. Rendered under asChild too (gh#1171): the pill goes inside the child element after its children, so `<TopbarItem asChild badge={3}><a href=\"/notifications\">…</a></TopbarItem>` keeps its count.",
|
|
1794
|
+
"description": "Overlaid count; does not change the bar cell width. Rendered under asChild too (gh#1171): the pill goes inside the child element after its children, so `<TopbarItem asChild badge={3}><a href=\"/notifications\">…</a></TopbarItem>` keeps its count. Nothing renders for `undefined`, `\"\"` or the number `0` (gh#1341) — pass the count as it is, never `count === 0 ? undefined : count`; pass the string `\"0\"` to show a zero on purpose.",
|
|
1795
1795
|
"name": "badge",
|
|
1796
1796
|
"type": "ReactNode"
|
|
1797
1797
|
},
|
|
@@ -8835,7 +8835,8 @@
|
|
|
8835
8835
|
"DataTable.Toolbar — the in-table strip for column/density/bulk-action controls; Toolbar (this) is the page-level filter strip that sits ABOVE the table.",
|
|
8836
8836
|
"SearchInput — the debounced free-text control placed as the first child of Toolbar.",
|
|
8837
8837
|
"Badge — compose Badge + a sibling icon Button to render each active-filter chip; Badge itself is a non-interactive leaf.",
|
|
8838
|
-
"Sheet — holds the filters beyond the 2–4 inline ones, opened by ONE 'More filters' Button inside the bar (pattern `list-filters`, rule #51). Not a ScrollArea: a filter strip never scrolls sideways."
|
|
8838
|
+
"Sheet — holds the filters beyond the 2–4 inline ones, opened by ONE 'More filters' Button inside the bar (pattern `list-filters`, rule #51). Not a ScrollArea: a filter strip never scrolls sideways.",
|
|
8839
|
+
"SearchForm — the submit-driven 「検索条件フォーム」 for many conditions or an explicit 「検索」 button (rule #51 decision table); FilterBar is the live strip."
|
|
8839
8840
|
],
|
|
8840
8841
|
"rules": [
|
|
8841
8842
|
23,
|
|
@@ -8850,7 +8851,7 @@
|
|
|
8850
8851
|
"FilterBar",
|
|
8851
8852
|
"FilterBarGroup"
|
|
8852
8853
|
],
|
|
8853
|
-
"tagline": "List-page filter strip — SearchInput + labelled ToolbarGroup filter slots (or the typed `search`/`filters`/`chips` model) + a clear-all affordance, optionally sticky. It WRAPS and never scrolls sideways; many filters → 2–4 inline + ONE 'More filters' Sheet (rule #51, pattern `list-filters`). `FilterBar`/`FilterBarGroup` are the same components under their alias names.",
|
|
8854
|
+
"tagline": "List-page filter strip — SearchInput + labelled ToolbarGroup filter slots (or the typed `search`/`filters`/`chips` model) + a clear-all affordance, optionally sticky. It WRAPS and never scrolls sideways; many filters → 2–4 inline + ONE 'More filters' Sheet (rule #51, pattern `list-filters`). `FilterBar`/`FilterBarGroup` are the same components under their alias names. Eleven layouts (inline, Filter menu + chips, More-filters Sheet, saved views, two rows, actions + sticky, faceted sidebar, compact in a card, date range, phone, docked panel) → pattern `filter-bar-layouts`.",
|
|
8854
8855
|
"usage": [
|
|
8855
8856
|
"DO prefer the typed model on a canonical list page — `search`/`filters`/`chips`/`onChipRemove`/`actions`/`resultCount` (+ `loading`/`disabled`/`error`) make the bar own layout, widths, chip lifecycle and keyboard order through --filter-bar-* tokens while ALL state stays consumer data. Children composition remains fully supported and unchanged when no model prop is passed.",
|
|
8856
8857
|
"DO place Toolbar ABOVE the table Card, never inside a `CardContent flush`. Put SearchInput as a direct child (it self-labels) and wrap every other control in a ToolbarGroup with a `label`.",
|
|
@@ -8858,6 +8859,7 @@
|
|
|
8858
8859
|
"DO set `sticky` for long list pages so the filters stay reachable while scrolling; if a topbar sits above the list, raise `--filter-bar-sticky-offset` so the strip parks below it.",
|
|
8859
8860
|
"DO pass `controlId` on a ToolbarGroup that wraps ONE control and give that control the same `id` — the visible caption then becomes the control's real `<label htmlFor>` (WCAG 2.5.3 label-in-name). Without it the caption only names the group wrapper and the control itself is NAMELESS (axe: select-name / button-name), so you must fall back to an `aria-label` that repeats the caption.",
|
|
8860
8861
|
"DO let the strip WRAP — cardinal rule #51 (`get_rule 51`). It stacks below 640px and wraps onto extra rows above; it never scrolls sideways, and neither may anything you wrap it in (`ScrollArea orientation=\"horizontal\"`, an `overflow-x-auto` box): the `overflow` prop and its `scroll` strategy were removed in 33.0.0 because a sideways strip hid filters off-screen and clipped the first label. When the filters are many, keep the 2–4 most-used ones inline and put the rest behind ONE 'More filters' Button (`count` = hidden filters applied) that opens a Sheet holding them as FilterBarGroup + control; every applied filter, inline or hidden, is a chip in `chips` (the chips row wraps too). Copy-paste composition: pattern `list-filters` (`get_pattern list-filters`); there is no dedicated component. Never re-implement the geometry in the page: no page-local flex/grid/width/overflow rules on a filter strip.",
|
|
8862
|
+
"DO keep filters PACKED at the start of the bar with its one gap. Never lay them out yourself in a Flex/div/Card/ResponsiveGrid of FormFields above the table, and never spread them with `justify=\"between\"|\"around\"|\"evenly\"` — the only far-end element of a filter bar is its `actions` slot (with the clear button). ui-audit `hand-rolled-filter-bar` flags both. For a layout other than one strip (saved views, a Filter menu with chips, a faceted sidebar, a docked side panel, a compact bar inside a table card, a date range with quick ranges, a phone Sheet), copy pattern `filter-bar-layouts` (docs page navigation/filter-bar-layouts).",
|
|
8861
8863
|
"DON'T build active-filter chips by nesting a Button inside a Badge (invalid markup + broken focus). Render each chip as a Badge label with a SIBLING icon Button (`aria-label` = 'clear <filter>'); a ghost `size='sm'` Button clears all.",
|
|
8862
8864
|
"DON'T hand-roll a debounced search box or a raw `<select>` — compose SearchInput and Select. Toolbar is layout + clear-all only; the controls own their own state and a11y.",
|
|
8863
8865
|
"Prefer the typed model for a canonical list page: pass `search` + `filters` + `chips` + `resultCount` and the bar owns the layout, widths, chip lifecycle, keyboard order (search → filters → children → reset → actions → chip removes) and responsive stacking through --filter-bar-* tokens. Everything stays consumer data — the bar renders state, never owns it.",
|
|
@@ -8868,7 +8870,123 @@
|
|
|
8868
8870
|
"useCases": [
|
|
8869
8871
|
"Master list screens (members, organizations, subscriptions, invoices) that need free-text search plus a few dropdown filters above a DataTable.",
|
|
8870
8872
|
"Sticky filter strip over a tall list where the filters must remain visible as the user scrolls the results.",
|
|
8871
|
-
"Filtered views that surface applied conditions as removable chips (clear-one via each chip's × Button, clear-all via `onClear`)."
|
|
8873
|
+
"Filtered views that surface applied conditions as removable chips (clear-one via each chip's × Button, clear-all via `onClear`).",
|
|
8874
|
+
"Filter layout choices — saved views filter tabs above the bar, a search-first bar with one Filter menu, a faceted filter sidebar with per-facet counts, a docked filter panel beside the table, a compact table toolbar, a date range filter with quick ranges, a phone filter Sheet: pattern `filter-bar-layouts`."
|
|
8875
|
+
]
|
|
8876
|
+
},
|
|
8877
|
+
{
|
|
8878
|
+
"absorbed": [
|
|
8879
|
+
"QueryFilter",
|
|
8880
|
+
"SearchPanel"
|
|
8881
|
+
],
|
|
8882
|
+
"docPath": "navigation/search-form",
|
|
8883
|
+
"example": "import { SearchForm } from \"@godxjp/ui/navigation\";\nimport { FormField, Input, Select, DatePicker } from \"@godxjp/ui/data-entry\";\n\n<SearchForm\n controlWidth=\"100%\"\n onSubmit={(e) => { e.preventDefault(); setApplied(draft); }}\n onReset={() => { setDraft(EMPTY); setApplied(EMPTY); }}\n summary=\"国籍: ベトナム / 状態: 実習中\"\n resultCount={rows.length}\n>\n <FormField label=\"氏名\" id=\"q-name\"><Input id=\"q-name\" value={draft.name} onChange={(e) => set({ name: e.target.value })} /></FormField>\n <FormField label=\"国籍\" id=\"q-nat\"><Select id=\"q-nat\" options={nationalities} value={draft.nationality} onValueChange={(v) => set({ nationality: v })} /></FormField>\n <FormField label=\"入国日\" id=\"q-entry\"><DatePicker id=\"q-entry\" range /></FormField>\n <FormField label=\"職種\" id=\"q-occ\"><Select id=\"q-occ\" options={occupations} value={draft.occupation} onValueChange={(v) => set({ occupation: v })} /></FormField>\n</SearchForm>",
|
|
8884
|
+
"group": "navigation",
|
|
8885
|
+
"importPath": "@godxjp/ui/navigation",
|
|
8886
|
+
"name": "SearchForm",
|
|
8887
|
+
"props": [
|
|
8888
|
+
{
|
|
8889
|
+
"description": "One FormField per condition, in reading order. Each direct child is one grid cell; a range (「日付」 From〜To, 「金額」 「下限〜上限」) is ONE FormField holding both controls.",
|
|
8890
|
+
"name": "children",
|
|
8891
|
+
"required": true,
|
|
8892
|
+
"type": "ReactNode"
|
|
8893
|
+
},
|
|
8894
|
+
{
|
|
8895
|
+
"defaultValue": "{ base: 1, md: 2, lg: 3 }",
|
|
8896
|
+
"description": "Grid columns per container breakpoint — one column on a phone, aligned columns above. Conditions never spread to the edges.",
|
|
8897
|
+
"name": "columns",
|
|
8898
|
+
"type": "ResponsiveGridColumnsProp"
|
|
8899
|
+
},
|
|
8900
|
+
{
|
|
8901
|
+
"description": "Conditions shown while collapsed (antd `defaultFormItemsNumber`). Default: the widest `columns` step, i.e. the first row.",
|
|
8902
|
+
"name": "visibleFields",
|
|
8903
|
+
"type": "number"
|
|
8904
|
+
},
|
|
8905
|
+
{
|
|
8906
|
+
"description": "Controlled: whether the conditions past `visibleFields` are shown.",
|
|
8907
|
+
"name": "open",
|
|
8908
|
+
"type": "boolean"
|
|
8909
|
+
},
|
|
8910
|
+
{
|
|
8911
|
+
"defaultValue": "false",
|
|
8912
|
+
"description": "Uncontrolled initial state — collapsed to the first row by default.",
|
|
8913
|
+
"name": "defaultOpen",
|
|
8914
|
+
"type": "boolean"
|
|
8915
|
+
},
|
|
8916
|
+
{
|
|
8917
|
+
"description": "Fires when the 「詳細条件を開く」 / 「閉じる」 toggle is pressed.",
|
|
8918
|
+
"name": "onOpenChange",
|
|
8919
|
+
"type": "(open: boolean) => void"
|
|
8920
|
+
},
|
|
8921
|
+
{
|
|
8922
|
+
"description": "Search (「検索」) — the native form submit. Call `preventDefault()` and copy the draft conditions into the applied query; the form never searches on each keystroke.",
|
|
8923
|
+
"name": "onSubmit",
|
|
8924
|
+
"type": "FormEventHandler<HTMLFormElement>"
|
|
8925
|
+
},
|
|
8926
|
+
{
|
|
8927
|
+
"description": "Clear (「クリア」). The button renders only when this is set.",
|
|
8928
|
+
"name": "onReset",
|
|
8929
|
+
"type": "() => void"
|
|
8930
|
+
},
|
|
8931
|
+
{
|
|
8932
|
+
"description": "The conditions the CURRENT results were searched with, shown after the localized 「検索条件」 label in the status line (e.g. 「国「籍」: 「ベトナム」 / 「状態」: 「実習」中」).",
|
|
8933
|
+
"name": "summary",
|
|
8934
|
+
"type": "ReactNode"
|
|
8935
|
+
},
|
|
8936
|
+
{
|
|
8937
|
+
"description": "Localized, pluralized result count in the same `role=\"status\"` line.",
|
|
8938
|
+
"name": "resultCount",
|
|
8939
|
+
"type": "number"
|
|
8940
|
+
},
|
|
8941
|
+
{
|
|
8942
|
+
"description": "Puts the Search button in its loading state while the query runs.",
|
|
8943
|
+
"name": "loading",
|
|
8944
|
+
"type": "boolean"
|
|
8945
|
+
},
|
|
8946
|
+
{
|
|
8947
|
+
"description": "Overrides the localized Search (「検索」) text.",
|
|
8948
|
+
"name": "searchLabel",
|
|
8949
|
+
"type": "ReactNode"
|
|
8950
|
+
},
|
|
8951
|
+
{
|
|
8952
|
+
"description": "Overrides the localized Clear (「クリア」) text.",
|
|
8953
|
+
"name": "resetLabel",
|
|
8954
|
+
"type": "ReactNode"
|
|
8955
|
+
},
|
|
8956
|
+
{
|
|
8957
|
+
"description": "Accessible name of the `search` landmark (default: localized 'Search conditions').",
|
|
8958
|
+
"name": "label",
|
|
8959
|
+
"type": "string"
|
|
8960
|
+
},
|
|
8961
|
+
{
|
|
8962
|
+
"description": "Passed to the underlying Form: `layout=\"vertical\"` (default) is label-top; `layout=\"horizontal\"` + `labelWidth` is the label-left table style of Japanese business systems. Set `controlWidth` once here so Selects fill their column.",
|
|
8963
|
+
"name": "layout / labelWidth / controlWidth / labelAlign",
|
|
8964
|
+
"type": "Form props"
|
|
8965
|
+
}
|
|
8966
|
+
],
|
|
8967
|
+
"related": [
|
|
8968
|
+
"Toolbar — FilterBar, the live filter strip; use it for a few filters applied as the user changes them.",
|
|
8969
|
+
"Form — the layout container SearchForm renders (`role=\"search\"`); its `layout`, `labelWidth` and `controlWidth` pass through.",
|
|
8970
|
+
"FormField — one per condition; a range is one FormField holding two controls.",
|
|
8971
|
+
"DataTable — the results under the form."
|
|
8972
|
+
],
|
|
8973
|
+
"rules": [
|
|
8974
|
+
23,
|
|
8975
|
+
51
|
|
8976
|
+
],
|
|
8977
|
+
"storyPath": "navigation/search-form.tsx",
|
|
8978
|
+
"tagline": "Submit-driven 「検索条件フォーム」 (Ant Design ProComponents QueryFilter): a `search` landmark holding a grid of labelled conditions aligned in columns, Search 「検索」 + Clear 「クリア」, the conditions past the first row behind ONE 「詳細条件を開く」 toggle that names how many are hidden, and a 「検索条件」 summary + result count in a role=\"status\" line after the search. Use it instead of a live FilterBar when the conditions are many, the query is heavy, or users expect an explicit 「検索」 button (rule #51 decision table).",
|
|
8979
|
+
"usage": [
|
|
8980
|
+
"DO pick the shape from rule #51's decision table: a few filters applied instantly → FilterBar; many conditions / a heavy query / users who expect an explicit 「検索」 button → SearchForm; arbitrary field × operator conditions saved as views → pattern `condition-builder`.",
|
|
8981
|
+
"DO put one FormField per condition as a direct child. Ranges (「入国日」 From〜To, 「金額」 「下限〜上限」) are ONE FormField with both controls; 「部分一致」 / 「完全一致」 is a RadioGroup inside the name field.",
|
|
8982
|
+
"DO keep a draft and an applied copy of the conditions: inputs edit the draft, onSubmit copies it to the applied query, onReset clears both. Pass the applied conditions as `summary` and the hit count as `resultCount`.",
|
|
8983
|
+
"DO NOT wrap it in another Form, and do not rebuild it as a Form + ResponsiveGrid + Collapsible by hand — the collapse, the hidden-count toggle, the search landmark and the status line are what this component owns. Hidden conditions stay mounted, so a typed value survives collapsing.",
|
|
8984
|
+
"A SearchForm (or any `<Form role=\"search\" onSubmit>`) above a table is NOT flagged by ui-audit `form-on-list-page` or `hand-rolled-filter-bar`; a submitting create form without role=\"search\" still is."
|
|
8985
|
+
],
|
|
8986
|
+
"useCases": [
|
|
8987
|
+
"「検索条件フォーム」 on a Japanese business-system list (「基幹系」 / 「業務システム」): 6–12 conditions, 「検索」 and 「クリア」 buttons, 「詳細条件を開く」.",
|
|
8988
|
+
"Search screens over a heavy query where searching on every keystroke would be too expensive.",
|
|
8989
|
+
"Label-left (「表形式」) or label-top search panels above a DataTable, with the applied conditions summarized after the search."
|
|
8872
8990
|
]
|
|
8873
8991
|
},
|
|
8874
8992
|
{
|
package/agent/index.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
3
|
"anti-ai-tells": 35,
|
|
4
|
-
"components":
|
|
5
|
-
"patterns":
|
|
4
|
+
"components": 166,
|
|
5
|
+
"patterns": 28,
|
|
6
6
|
"rules": 44,
|
|
7
|
-
"tokens":
|
|
7
|
+
"tokens": 2232,
|
|
8
8
|
"vocabulary": 14
|
|
9
9
|
},
|
|
10
10
|
"design": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"file": "components-index.json",
|
|
19
|
-
"note": "
|
|
19
|
+
"note": "50 KB — name + group + tagline for all 166. 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.4.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": 1903,
|
|
65
65
|
"foundation": 216,
|
|
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.4.0"
|
|
72
72
|
}
|
package/agent/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @godxjp/ui
|
|
2
2
|
|
|
3
|
-
> A Japanese-enterprise React design system:
|
|
4
|
-
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.
|
|
3
|
+
> A Japanese-enterprise React design system: 166 components, 2232 design tokens,
|
|
4
|
+
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.4.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@33.
|
|
7
|
+
(`npx @godxjp/ui-mcp@33.4.0`). It is searchable and version-locked. These files exist for agents
|
|
8
8
|
that can only fetch URLs.
|
|
9
9
|
|
|
10
10
|
## Start
|
|
@@ -15,11 +15,11 @@ that can only fetch URLs.
|
|
|
15
15
|
|
|
16
16
|
## Catalog
|
|
17
17
|
|
|
18
|
-
- [patterns-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/patterns-index.json):
|
|
19
|
-
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json):
|
|
18
|
+
- [patterns-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/patterns-index.json): 28 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.
|
|
19
|
+
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 50 KB — all 166 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
|
|
20
20
|
- [components/<Name>.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–37 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.
|
|
21
21
|
- [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.3 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
|
|
22
|
-
- [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` — 216 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 113 `semantic` roles,
|
|
22
|
+
- [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` — 216 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 113 `semantic` roles, 1903 `component` knobs.
|
|
23
23
|
- [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.
|
|
24
24
|
- [rules.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json): 44 cardinal rules.
|
|
25
25
|
- [anti-ai-tells.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/anti-ai-tells.json): 35 shapes that make generated UI look generated, each with its fix.
|
|
@@ -27,7 +27,7 @@ that can only fetch URLs.
|
|
|
27
27
|
## Pinning
|
|
28
28
|
|
|
29
29
|
Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
|
|
30
|
-
the tag: `.../godx-jp/godxjp-ui/v33.
|
|
30
|
+
the tag: `.../godx-jp/godxjp-ui/v33.4.0/agent/...`. A catalog that does not match the installed
|
|
31
31
|
package describes props that are absent, or hides props that are present, and says nothing either way.
|
|
32
32
|
|
|
33
33
|
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.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"aliases": [
|
|
3
|
+
"絞り込み条件",
|
|
4
|
+
"条件ビルダー",
|
|
5
|
+
"filter-builder",
|
|
6
|
+
"query-builder",
|
|
7
|
+
"kintone filter",
|
|
8
|
+
"kintone-filter",
|
|
9
|
+
"salesforce list view filter",
|
|
10
|
+
"salesforce-filter",
|
|
11
|
+
"filter logic",
|
|
12
|
+
"list-view-filter"
|
|
13
|
+
],
|
|
14
|
+
"code": "// Live examples: docs page navigation/search-form (m = builder, o = list view with the builder in a\n// docked panel). Research: kintone's filter rows are field / operator / value (「次のいずれかを\n// 含む」, ≦ …) combined with 「すべての条件を満たす」, conditions added with 「絞り込み条件を\n// 追加」; the kintone query language joins conditions with and / or and groups them with\n// parentheses, sorted by order by. Salesforce numbers each filter (Filter 1, 2, …) and combines\n// them with AND / OR / NOT, e.g. (1 AND 2) OR 3; list views are switched from a picker, pinned\n// as the default, and edited in a Filters panel (Add Filter → field / operator / value).\n//\n// A COMPOSITION, not a component: only one of the design systems the library follows names a\n// condition builder, the operators are domain policy, and every piece is an existing primitive.\nimport { useRef, useState } from \"react\";\nimport { Minus, Plus } from \"lucide-react\";\nimport { Badge } from \"@godxjp/ui/data-display\";\nimport { Form, FormField, Input, NumberInput, RadioGroup, Select } from \"@godxjp/ui/data-entry\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Flex, ResponsiveGrid } from \"@godxjp/ui/layout\";\n\ntype Kind = \"text\" | \"choice\" | \"number\";\ntype Field = { value: string; label: string; kind: Kind; options?: { value: string; label: string }[] };\ntype Row = { id: number; field: string; operator: string; value: string };\nexport type Conditions = { match: \"all\" | \"any\" | \"custom\"; logic: string; rows: Row[] };\n\nconst OPERATORS: Record<Kind, { value: string; label: string }[]> = {\n text: [{ value: \"contains\", label: \"次のキーワードを含む\" }, { value: \"notContains\", label: \"次のキーワードを含まない\" }, { value: \"eq\", label: \"=\" }, { value: \"neq\", label: \"≠\" }],\n choice: [{ value: \"in\", label: \"次のいずれかを含む\" }, { value: \"notIn\", label: \"次のいずれも含まない\" }],\n number: [{ value: \"eq\", label: \"=\" }, { value: \"neq\", label: \"≠\" }, { value: \"gte\", label: \"≥(以上)\" }, { value: \"lte\", label: \"≤(以下)\" }],\n};\n\n/** Numbered-row logic: \"1 AND (2 OR 3)\", \"NOT 2\". null = malformed → show the field error. */\nexport function compileLogic(expr: string, count: number): ((hits: boolean[]) => boolean) | null {\n const tokens = expr.toUpperCase().match(/\\d+|AND|OR|NOT|\\(|\\)|\\S/g) ?? [];\n let pos = 0;\n type Node = (h: boolean[]) => boolean;\n const or = (): Node | null => { let l = and(); while (l && tokens[pos] === \"OR\") { pos++; const r = and(); if (!r) return null; const a = l; l = (h) => a(h) || r(h); } return l; };\n const and = (): Node | null => { let l = not(); while (l && tokens[pos] === \"AND\") { pos++; const r = not(); if (!r) return null; const a = l; l = (h) => a(h) && r(h); } return l; };\n const not = (): Node | null => {\n if (tokens[pos] === \"NOT\") { pos++; const i = not(); return i ? (h) => !i(h) : null; }\n if (tokens[pos] === \"(\") { pos++; const i = or(); if (!i || tokens[pos] !== \")\") return null; pos++; return i; }\n const n = Number(tokens[pos]);\n if (!Number.isInteger(n) || n < 1 || n > count) return null;\n pos++; return (h) => h[n - 1] ?? false;\n };\n const tree = tokens.length ? or() : null;\n return tree && pos === tokens.length ? tree : null;\n}\n\nexport function ConditionBuilder({ fields, value, onApply }: {\n fields: Field[]; value: Conditions; onApply: (c: Conditions) => void;\n}) {\n const [draft, setDraft] = useState(value);\n const nextId = useRef(Math.max(0, ...value.rows.map((r) => r.id)) + 1);\n const valid = draft.match !== \"custom\" || compileLogic(draft.logic, draft.rows.length) !== null;\n const patch = (id: number, p: Partial<Row>) => setDraft((c) => ({ ...c, rows: c.rows.map((r) => (r.id === id ? { ...r, ...p } : r)) }));\n const kindOf = (f: string) => fields.find((x) => x.value === f)?.kind ?? \"text\";\n /** kintone's + adds a blank condition right after its own row; null appends one. */\n const insertAfter = (rows: Row[], afterId: number | null): Row[] => {\n const blank: Row = { id: nextId.current++, field: fields[0].value, operator: OPERATORS[fields[0].kind][0].value, value: \"\" };\n const at = afterId === null ? rows.length : rows.findIndex((r) => r.id === afterId) + 1;\n return [...rows.slice(0, at), blank, ...rows.slice(at)];\n };\n\n // role=\"search\" + a real submit: neither form-on-list-page nor hand-rolled-filter-bar flags it.\n return (\n <Form role=\"search\" aria-label=\"絞り込み条件\" controlWidth=\"100%\"\n onSubmit={(e) => { e.preventDefault(); if (valid) onApply(draft); }}>\n <Flex direction=\"col\" gap=\"md\">\n <FormField label=\"条件の組み合わせ\">\n <RadioGroup aria-label=\"条件の組み合わせ\" orientation=\"horizontal\" value={draft.match}\n options={[{ value: \"all\", label: \"すべての条件を満たす\" }, { value: \"any\", label: \"いずれかの条件を満たす\" }, { value: \"custom\", label: \"条件式を指定\" }]}\n onValueChange={(v) => setDraft((c) => ({ ...c, match: v as Conditions[\"match\"] }))} />\n </FormField>\n {draft.rows.map((row, i) => {\n const field = fields.find((f) => f.value === row.field);\n const n = i + 1;\n return (\n // ONE line per condition, kintone-style: [No.] [field] [operator] [value] [+][−].\n // Container grid: 24 tracks from lg (field 6, operator 5 — narrower but wide enough for\n // 「次のキーワードを含まない」 — value 10), 12 at md (the operator gets the extra track so its\n // longest label is not cut). Below md (a docked panel, a phone) it is 4 tracks and\n // `dense` backfills the +/− cell beside the number: No. and buttons on the first line,\n // field / operator / value stacked below. align=\"center\" = antd Row align=\"middle\".\n <Flex key={row.id} direction=\"col\" role=\"group\" aria-label={\"条件 \" + n}>\n <ResponsiveGrid columns={{ base: 4, md: 12, lg: 24 }} gap=\"sm\" align=\"center\" dense>\n <ResponsiveGrid.Item span={{ base: 1, md: 1, lg: 1 }}>\n <Badge variant=\"outline\">{n}</Badge>\n </ResponsiveGrid.Item>\n <ResponsiveGrid.Item span={{ base: 4, md: 3, lg: 6 }}>\n <Select aria-label={\"条件 \" + n + \" · フィールド\"} options={fields} value={row.field}\n onValueChange={(f: string) => patch(row.id, { field: f, operator: OPERATORS[kindOf(f)][0].value, value: \"\" })} />\n </ResponsiveGrid.Item>\n <ResponsiveGrid.Item span={{ base: 4, md: 4, lg: 5 }}>\n <Select aria-label={\"条件 \" + n + \" · 演算子\"} options={OPERATORS[kindOf(row.field)]} value={row.operator}\n onValueChange={(op: string) => patch(row.id, { operator: op })} />\n </ResponsiveGrid.Item>\n <ResponsiveGrid.Item span={{ base: 4, md: 3, lg: 10 }}>\n {field?.kind === \"choice\" ? (\n <Select aria-label={\"条件 \" + n + \" · 値\"} options={field.options ?? []} value={row.value}\n onValueChange={(v: string) => patch(row.id, { value: v })} />\n ) : field?.kind === \"number\" ? (\n <NumberInput aria-label={\"条件 \" + n + \" · 値\"} value={row.value === \"\" ? null : Number(row.value)}\n onValueChange={(v) => patch(row.id, { value: v == null ? \"\" : String(v) })} />\n ) : (\n <Input aria-label={\"条件 \" + n + \" · 値\"} value={row.value} onChange={(e) => patch(row.id, { value: e.target.value })} />\n )}\n </ResponsiveGrid.Item>\n <ResponsiveGrid.Item span={{ base: 3, md: 1, lg: 2 }}>\n <Flex gap=\"xs\" justify=\"end\">\n <Button type=\"button\" variant=\"ghost\" size=\"icon-sm\" aria-label={\"条件 \" + n + \" の後に条件を追加\"}\n onClick={() => setDraft((c) => ({ ...c, rows: insertAfter(c.rows, row.id) }))}>\n <Plus aria-hidden=\"true\" />\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"icon-sm\" aria-label={\"条件 \" + n + \" を削除\"}\n onClick={() => setDraft((c) => ({ ...c, rows: c.rows.filter((r) => r.id !== row.id) }))}>\n <Minus aria-hidden=\"true\" />\n </Button>\n </Flex>\n </ResponsiveGrid.Item>\n </ResponsiveGrid>\n </Flex>\n );\n })}\n {/* With every row removed there is no + left to press: offer one. */}\n {draft.rows.length === 0 && (\n <Flex>\n <Button type=\"button\" variant=\"outline\" size=\"sm\"\n onClick={() => setDraft((c) => ({ ...c, rows: insertAfter(c.rows, null) }))}>\n <Plus aria-hidden=\"true\" />条件を追加\n </Button>\n </Flex>\n )}\n {draft.match === \"custom\" && (\n <FormField label=\"条件式\" id=\"cb-logic\" helper=\"番号付きの条件を AND・OR・NOT と括弧で組み合わせます(例: 1 AND (2 OR 3))。\"\n error={valid ? undefined : \"存在する条件の番号、AND・OR・NOT、対応の取れた括弧だけを使ってください。\"}>\n <Input id=\"cb-logic\" value={draft.logic} onChange={(e) => setDraft((c) => ({ ...c, logic: e.target.value }))} />\n </FormField>\n )}\n <Flex gap=\"sm\" justify=\"end\" wrap>\n <Button type=\"button\" variant=\"ghost\" onClick={() => setDraft(value)}>変更を元に戻す</Button>\n <Button type=\"submit\" disabled={!valid}>適用</Button>\n </Flex>\n </Flex>\n </Form>\n );\n}\n// Add 並び順 (Select + Segmented 昇順/降順) and \"save as view\" (Input + Button) as two more\n// FormFields when the screen keeps saved views. Docked beside the list (Salesforce list view):\n// <SplitPane asideWidth=\"lg\" aside={open ? <Card>…<ConditionBuilder/>…</Card> : null}> with a\n// counted Filters toggle in FilterBar's actions; below 1024px the same builder opens in a Sheet.",
|
|
15
|
+
"name": "condition-builder",
|
|
16
|
+
"tagline": "Condition builder — numbered rows of field / operator / value, 「すべての条件を満たす」 / 「いずれかの条件を満たす」 / custom logic `1 AND (2 OR 3)`, add/remove rows, 「並び順」, save as a named view; applied on SUBMIT inside a role=\"search\" Form. Composition, not a component. Docked beside the list it is the Salesforce list-view filter panel.",
|
|
17
|
+
"tags": [
|
|
18
|
+
"filter",
|
|
19
|
+
"condition",
|
|
20
|
+
"builder",
|
|
21
|
+
"query",
|
|
22
|
+
"logic",
|
|
23
|
+
"and",
|
|
24
|
+
"or",
|
|
25
|
+
"kintone",
|
|
26
|
+
"salesforce",
|
|
27
|
+
"saved view",
|
|
28
|
+
"絞り込み",
|
|
29
|
+
"list"
|
|
30
|
+
]
|
|
31
|
+
}
|