@godxjp/ui 31.28.1 → 31.28.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/START-HERE.md +4 -4
- package/agent/components/Command.json +1 -1
- package/agent/components/Prose.json +1 -1
- package/agent/components/Select.json +2 -2
- package/agent/components.json +4 -4
- package/agent/index.json +4 -4
- package/agent/llms.txt +5 -5
- package/agent/tokens.json +8 -2
- package/dist/components/data-entry/search-select.js +149 -156
- package/dist/contracts/measurement.json +1 -1
- package/dist/props/components/data-entry.prop.d.ts +3 -2
- package/dist/styles/control.css +9 -1
- package/dist/styles/data-display-layout.css +25 -10
- package/dist/styles/focus-ring.css +2 -1
- package/dist/styles/layers.json +1 -1
- package/dist/tokens/components/control.css +3 -1
- package/package.json +2 -2
package/agent/START-HERE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
You are about to write code against a design system you did not author. This file is the whole
|
|
4
4
|
contract. Read it before you write JSX.
|
|
5
5
|
|
|
6
|
-
**This catalog describes `@godxjp/ui` 31.28.
|
|
6
|
+
**This catalog describes `@godxjp/ui` 31.28.3.** 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.
|
|
@@ -56,10 +56,10 @@ Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `li
|
|
|
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` —
|
|
59
|
+
4. `tokens.json` — 2156 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
|
-
|
|
62
|
+
1833 `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` | 112 | 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` |
|
|
148
|
+
| `component` | 1833 | 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.
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"defaultValue": "false",
|
|
58
|
-
"description": "antd List `split`. Draws the list as ONE ruled box: group/list padding 0, rows full-bleed to the panel edge with a square highlight, a hairline border-block-end between rows and none after the last visible row, row content aligned with CommandInput's leading glyph. Sets `data-split` on the root; the parts need no prop. Tokens: --command-list-split-padding, --command-list-split-inset, --command-item-divider-width, --command-item-divider-color. Select
|
|
58
|
+
"description": "antd List `split`. Draws the list as ONE ruled box: group/list padding 0, rows full-bleed to the panel edge with a square highlight, a hairline border-block-end between rows and none after the last visible row, row content aligned with CommandInput's leading glyph. Sets `data-split` on the root; the parts need no prop. Tokens: --command-list-split-padding, --command-list-split-inset, --command-item-divider-width, --command-item-divider-color. Select does NOT render its popup split (it is a picker; compact unruled rows).",
|
|
59
59
|
"name": "split",
|
|
60
60
|
"type": "boolean"
|
|
61
61
|
},
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"DO import from `@godxjp/ui/data-display`: `import { Prose } from \"@godxjp/ui/data-display\";`",
|
|
52
52
|
"DO wrap the OUTPUT of your Markdown pipeline (react-markdown + remark-gfm + rehype-sanitize), a sanitised CMS string via `dangerouslySetInnerHTML`, or plain JSX. Prose styles descendants: h1..h4, p, ul/ol/li, blockquote, code, pre, table/th/td, a, img, hr.",
|
|
53
53
|
"DO keep soft line breaks the pipeline's job (remark-breaks or the renderer's `breaks` option). Prose does not turn single newlines into `<br>`; it is typography, not parsing.",
|
|
54
|
-
"DON'T restate the heading scale, table cell measures or list rhythm with `[&_h1]:text-lg [&_td]:border …` utilities: they copy token values and never follow a retune. Prose reads --heading-h1..h4, --table-cell-padding-* and --prose-* directly.",
|
|
54
|
+
"DON'T restate the heading scale, table cell measures or list rhythm with `[&_h1]:text-lg [&_td]:border …` utilities: they copy token values and never follow a retune. Prose reads --heading-h1..h4, --table-cell-padding-* and --prose-* directly. Its long-form scale keeps every level h1–h6 at or above body size: h1–h3 step down in size, h4–h6 sit at body size in bold (h6 muted), so a host that shifts body headings down one level (page title = the one h1) keeps readable section titles.",
|
|
55
55
|
"DO tune the link through --prose-link-color and --prose-link-decoration-line (gh#717). The ink defaults to hsl(var(--primary)) resolved at the anchor, so a scoped [data-tenant] re-tint reaches it; the resting underline defaults to `underline` and is a SEPARATE knob from --text-link-decoration-line, because Prose is running text and there the underline is a WCAG 1.4.1 requirement, not a taste.",
|
|
56
56
|
"DO style a state your own renderer knows about by marking the anchor and selecting it: `a[data-unresolved]` (a wiki link whose target does not exist yet), then re-declare --prose-link-color inside that selector. Prose writes data-* on its ROOT ONLY — never on a descendant — so every data-* on an `a` inside it is yours and stays selectable. That is a promise held by a test (prose-link-717.test.tsx), not a coincidence; there is no prop for it, the same way `TableRow data-expanded-row` is an attribute rather than an API.",
|
|
57
57
|
"DON'T sanitise inside Prose: it renders whatever HTML it is given. Sanitise before (rehype-sanitize, or the server).",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"name": "Select",
|
|
14
14
|
"props": [
|
|
15
15
|
{
|
|
16
|
-
"description": "Select several options; value/defaultValue become string arrays. `tags` additionally ACCEPTS what was typed (a value that is not in the list), which is antd's own split between the two. `tags` needs no `options` (`<Select mode=\"tags\" />` works) and only an explicit `disabled` disables it (gh#1063). While text is typed the tags list reads create row, then an exact (case-folded) match, then options, then held values — the first enabled row is the Enter target, never a held tag (gh#1064). The popup
|
|
16
|
+
"description": "Select several options; value/defaultValue become string arrays. `tags` additionally ACCEPTS what was typed (a value that is not in the list), which is antd's own split between the two. `tags` needs no `options` (`<Select mode=\"tags\" />` works) and only an explicit `disabled` disables it (gh#1063). While text is typed the tags list reads create row, then an exact (case-folded) match, then options, then held values — the first enabled row is the Enter target, never a held tag (gh#1064). The popup is a picker: compact unruled rows (--search-select-option-line-height), a list capped at --search-select-list-max-height (16rem, antd listHeight 256) that scrolls inside, the search as the panel header line with no ring of its own, and the picked rows marked by a check (menuItemSelectedIcon default) + weight while the row background means pointer/keyboard position only. A facet list that wants ruled rows composes `Command split` (gh#699) itself.",
|
|
17
17
|
"name": "mode",
|
|
18
18
|
"type": "\"multiple\" | \"tags\""
|
|
19
19
|
},
|
|
@@ -262,7 +262,7 @@
|
|
|
262
262
|
"type": "(option: SearchSelectOptionProp, info: { index: number }) => React.ReactNode"
|
|
263
263
|
},
|
|
264
264
|
{
|
|
265
|
-
"description": "antd `menuItemSelectedIcon` — a decorative mark on the picked row.
|
|
265
|
+
"description": "antd `menuItemSelectedIcon` — a decorative mark on the picked row. In mode multiple / tags it defaults to a check (as in antd), because there the row background means pointer/keyboard position, not selection; pass `null` to drop it. A single-value Select shows none by default.",
|
|
266
266
|
"name": "menuItemSelectedIcon",
|
|
267
267
|
"type": "React.ReactNode"
|
|
268
268
|
},
|
package/agent/components.json
CHANGED
|
@@ -5393,7 +5393,7 @@
|
|
|
5393
5393
|
"DO import from `@godxjp/ui/data-display`: `import { Prose } from \"@godxjp/ui/data-display\";`",
|
|
5394
5394
|
"DO wrap the OUTPUT of your Markdown pipeline (react-markdown + remark-gfm + rehype-sanitize), a sanitised CMS string via `dangerouslySetInnerHTML`, or plain JSX. Prose styles descendants: h1..h4, p, ul/ol/li, blockquote, code, pre, table/th/td, a, img, hr.",
|
|
5395
5395
|
"DO keep soft line breaks the pipeline's job (remark-breaks or the renderer's `breaks` option). Prose does not turn single newlines into `<br>`; it is typography, not parsing.",
|
|
5396
|
-
"DON'T restate the heading scale, table cell measures or list rhythm with `[&_h1]:text-lg [&_td]:border …` utilities: they copy token values and never follow a retune. Prose reads --heading-h1..h4, --table-cell-padding-* and --prose-* directly.",
|
|
5396
|
+
"DON'T restate the heading scale, table cell measures or list rhythm with `[&_h1]:text-lg [&_td]:border …` utilities: they copy token values and never follow a retune. Prose reads --heading-h1..h4, --table-cell-padding-* and --prose-* directly. Its long-form scale keeps every level h1–h6 at or above body size: h1–h3 step down in size, h4–h6 sit at body size in bold (h6 muted), so a host that shifts body headings down one level (page title = the one h1) keeps readable section titles.",
|
|
5397
5397
|
"DO tune the link through --prose-link-color and --prose-link-decoration-line (gh#717). The ink defaults to hsl(var(--primary)) resolved at the anchor, so a scoped [data-tenant] re-tint reaches it; the resting underline defaults to `underline` and is a SEPARATE knob from --text-link-decoration-line, because Prose is running text and there the underline is a WCAG 1.4.1 requirement, not a taste.",
|
|
5398
5398
|
"DO style a state your own renderer knows about by marking the anchor and selecting it: `a[data-unresolved]` (a wiki link whose target does not exist yet), then re-declare --prose-link-color inside that selector. Prose writes data-* on its ROOT ONLY — never on a descendant — so every data-* on an `a` inside it is yours and stays selectable. That is a promise held by a test (prose-link-717.test.tsx), not a coincidence; there is no prop for it, the same way `TableRow data-expanded-row` is an attribute rather than an API.",
|
|
5399
5399
|
"DON'T sanitise inside Prose: it renders whatever HTML it is given. Sanitise before (rehype-sanitize, or the server).",
|
|
@@ -6658,7 +6658,7 @@
|
|
|
6658
6658
|
"name": "Select",
|
|
6659
6659
|
"props": [
|
|
6660
6660
|
{
|
|
6661
|
-
"description": "Select several options; value/defaultValue become string arrays. `tags` additionally ACCEPTS what was typed (a value that is not in the list), which is antd's own split between the two. `tags` needs no `options` (`<Select mode=\"tags\" />` works) and only an explicit `disabled` disables it (gh#1063). While text is typed the tags list reads create row, then an exact (case-folded) match, then options, then held values — the first enabled row is the Enter target, never a held tag (gh#1064). The popup
|
|
6661
|
+
"description": "Select several options; value/defaultValue become string arrays. `tags` additionally ACCEPTS what was typed (a value that is not in the list), which is antd's own split between the two. `tags` needs no `options` (`<Select mode=\"tags\" />` works) and only an explicit `disabled` disables it (gh#1063). While text is typed the tags list reads create row, then an exact (case-folded) match, then options, then held values — the first enabled row is the Enter target, never a held tag (gh#1064). The popup is a picker: compact unruled rows (--search-select-option-line-height), a list capped at --search-select-list-max-height (16rem, antd listHeight 256) that scrolls inside, the search as the panel header line with no ring of its own, and the picked rows marked by a check (menuItemSelectedIcon default) + weight while the row background means pointer/keyboard position only. A facet list that wants ruled rows composes `Command split` (gh#699) itself.",
|
|
6662
6662
|
"name": "mode",
|
|
6663
6663
|
"type": "\"multiple\" | \"tags\""
|
|
6664
6664
|
},
|
|
@@ -6907,7 +6907,7 @@
|
|
|
6907
6907
|
"type": "(option: SearchSelectOptionProp, info: { index: number }) => React.ReactNode"
|
|
6908
6908
|
},
|
|
6909
6909
|
{
|
|
6910
|
-
"description": "antd `menuItemSelectedIcon` — a decorative mark on the picked row.
|
|
6910
|
+
"description": "antd `menuItemSelectedIcon` — a decorative mark on the picked row. In mode multiple / tags it defaults to a check (as in antd), because there the row background means pointer/keyboard position, not selection; pass `null` to drop it. A single-value Select shows none by default.",
|
|
6911
6911
|
"name": "menuItemSelectedIcon",
|
|
6912
6912
|
"type": "React.ReactNode"
|
|
6913
6913
|
},
|
|
@@ -10901,7 +10901,7 @@
|
|
|
10901
10901
|
},
|
|
10902
10902
|
{
|
|
10903
10903
|
"defaultValue": "false",
|
|
10904
|
-
"description": "antd List `split`. Draws the list as ONE ruled box: group/list padding 0, rows full-bleed to the panel edge with a square highlight, a hairline border-block-end between rows and none after the last visible row, row content aligned with CommandInput's leading glyph. Sets `data-split` on the root; the parts need no prop. Tokens: --command-list-split-padding, --command-list-split-inset, --command-item-divider-width, --command-item-divider-color. Select
|
|
10904
|
+
"description": "antd List `split`. Draws the list as ONE ruled box: group/list padding 0, rows full-bleed to the panel edge with a square highlight, a hairline border-block-end between rows and none after the last visible row, row content aligned with CommandInput's leading glyph. Sets `data-split` on the root; the parts need no prop. Tokens: --command-list-split-padding, --command-list-split-inset, --command-item-divider-width, --command-item-divider-color. Select does NOT render its popup split (it is a picker; compact unruled rows).",
|
|
10905
10905
|
"name": "split",
|
|
10906
10906
|
"type": "boolean"
|
|
10907
10907
|
},
|
package/agent/index.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"components": 184,
|
|
5
5
|
"patterns": 21,
|
|
6
6
|
"rules": 50,
|
|
7
|
-
"tokens":
|
|
7
|
+
"tokens": 2156,
|
|
8
8
|
"vocabulary": 14
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
@@ -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.28.
|
|
51
|
+
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.28.3/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":
|
|
58
|
+
"component": 1833,
|
|
59
59
|
"foundation": 211,
|
|
60
60
|
"semantic": 112
|
|
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.28.
|
|
65
|
+
"version": "31.28.3"
|
|
66
66
|
}
|
package/agent/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @godxjp/ui
|
|
2
2
|
|
|
3
|
-
> A Japanese-enterprise React design system: 184 components,
|
|
4
|
-
> 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.28.
|
|
3
|
+
> A Japanese-enterprise React design system: 184 components, 2156 design tokens,
|
|
4
|
+
> 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.28.3.
|
|
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.28.
|
|
7
|
+
(`npx @godxjp/ui-mcp@31.28.3`). It is searchable and version-locked. These files exist for agents
|
|
8
8
|
that can only fetch URLs.
|
|
9
9
|
|
|
10
10
|
## Start
|
|
@@ -18,7 +18,7 @@ that can only fetch URLs.
|
|
|
18
18
|
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 51 KB — all 184 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
|
|
19
19
|
- [components/<Name>.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.3 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), 112 `semantic` roles,
|
|
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), 112 `semantic` roles, 1833 `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.28.
|
|
29
|
+
the tag: `.../godx-jp/godxjp-ui/v31.28.3/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
|
@@ -4032,10 +4032,10 @@
|
|
|
4032
4032
|
"value": "var(--space-1)"
|
|
4033
4033
|
},
|
|
4034
4034
|
{
|
|
4035
|
-
"description": "antd `listHeight` writes this per instance; `none` lets the panel's own available height win.",
|
|
4035
|
+
"description": "antd `listHeight` writes this per instance; `none` lets the panel's own available height win. antd listHeight 256px; the list scrolls inside",
|
|
4036
4036
|
"name": "--search-select-list-max-height",
|
|
4037
4037
|
"tier": "component",
|
|
4038
|
-
"value": "
|
|
4038
|
+
"value": "16rem"
|
|
4039
4039
|
},
|
|
4040
4040
|
{
|
|
4041
4041
|
"description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
|
|
@@ -4049,6 +4049,12 @@
|
|
|
4049
4049
|
"tier": "component",
|
|
4050
4050
|
"value": "var(--space-2)"
|
|
4051
4051
|
},
|
|
4052
|
+
{
|
|
4053
|
+
"description": "Picker rows read compact (~34px), like Linear / GitHub label pickers, not body-text rows.",
|
|
4054
|
+
"name": "--search-select-option-line-height",
|
|
4055
|
+
"tier": "component",
|
|
4056
|
+
"value": "var(--line-height-tight)"
|
|
4057
|
+
},
|
|
4052
4058
|
{
|
|
4053
4059
|
"description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
|
|
4054
4060
|
"name": "--search-select-option-font-size",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { ChevronsUpDown, Loader2, X } from "lucide-react";
|
|
4
|
+
import { Check, ChevronsUpDown, Loader2, X } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { useFieldIdentity, useFieldNameFallback } from "../../lib/field-a11y.js";
|
|
7
7
|
import { isImeComposing } from "../../lib/ime.js";
|
|
@@ -90,6 +90,7 @@ function SearchSelect(props) {
|
|
|
90
90
|
);
|
|
91
91
|
const multiple = props.mode === "multiple" || props.mode === "tags";
|
|
92
92
|
const tagsMode = props.mode === "tags";
|
|
93
|
+
const rowSelectedIcon = menuItemSelectedIcon === void 0 ? multiple ? /* @__PURE__ */ jsx(Check, { className: "size-[var(--menu-icon-size)]" }) : null : menuItemSelectedIcon;
|
|
93
94
|
const maxTagTextLength = props.mode !== void 0 ? props.maxTagTextLength : void 0;
|
|
94
95
|
const tokenSeparators = props.mode !== void 0 ? props.tokenSeparators : void 0;
|
|
95
96
|
const tagRender = props.mode !== void 0 ? props.tagRender : void 0;
|
|
@@ -566,163 +567,155 @@ function SearchSelect(props) {
|
|
|
566
567
|
style: typeof popupMatchSelectWidth === "number" ? {
|
|
567
568
|
"--search-select-panel-inline-size": `${popupMatchSelectWidth}px`
|
|
568
569
|
} : void 0,
|
|
569
|
-
children: /* @__PURE__ */ jsx(PopupRender, { render: popupRender, children: /* @__PURE__ */ jsxs(
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
"
|
|
584
|
-
|
|
585
|
-
value: query,
|
|
586
|
-
onChange: (event) => {
|
|
587
|
-
const next = event.target.value;
|
|
588
|
-
if (multiple && !composingRef.current && tokenSeparators?.length && tokenSeparators.some((separator) => next.includes(separator))) {
|
|
589
|
-
selectTokens(next);
|
|
590
|
-
setQuery("");
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
setQuery(next);
|
|
594
|
-
},
|
|
595
|
-
onPaste: (event) => {
|
|
596
|
-
if (!multiple || !tokenSeparators?.length) return;
|
|
597
|
-
const text = event.clipboardData.getData("text");
|
|
598
|
-
if (!tokenSeparators.some((separator) => text.includes(separator))) return;
|
|
599
|
-
event.preventDefault();
|
|
600
|
-
selectTokens(text);
|
|
601
|
-
setQuery("");
|
|
602
|
-
},
|
|
603
|
-
onKeyDown,
|
|
604
|
-
onCompositionStart: () => {
|
|
605
|
-
composingRef.current = true;
|
|
606
|
-
},
|
|
607
|
-
onCompositionEnd: () => {
|
|
608
|
-
composingRef.current = false;
|
|
609
|
-
},
|
|
610
|
-
placeholder: searchPlaceholder ?? t("dataEntry.searchSelect.search"),
|
|
611
|
-
className: "ui-search-select-search-input"
|
|
570
|
+
children: /* @__PURE__ */ jsx(PopupRender, { render: popupRender, children: /* @__PURE__ */ jsxs(Command, { value, shouldFilter: false, className: "ui-search-select-command", children: [
|
|
571
|
+
/* @__PURE__ */ jsx("div", { className: "ui-search-select-search", children: /* @__PURE__ */ jsx(
|
|
572
|
+
Input,
|
|
573
|
+
{
|
|
574
|
+
autoFocus: true,
|
|
575
|
+
"aria-controls": listId,
|
|
576
|
+
"aria-autocomplete": "list",
|
|
577
|
+
"aria-activedescendant": activeOptionId,
|
|
578
|
+
"aria-label": searchPlaceholder ?? t("dataEntry.searchSelect.search"),
|
|
579
|
+
value: query,
|
|
580
|
+
onChange: (event) => {
|
|
581
|
+
const next = event.target.value;
|
|
582
|
+
if (multiple && !composingRef.current && tokenSeparators?.length && tokenSeparators.some((separator) => next.includes(separator))) {
|
|
583
|
+
selectTokens(next);
|
|
584
|
+
setQuery("");
|
|
585
|
+
return;
|
|
612
586
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
587
|
+
setQuery(next);
|
|
588
|
+
},
|
|
589
|
+
onPaste: (event) => {
|
|
590
|
+
if (!multiple || !tokenSeparators?.length) return;
|
|
591
|
+
const text = event.clipboardData.getData("text");
|
|
592
|
+
if (!tokenSeparators.some((separator) => text.includes(separator))) return;
|
|
593
|
+
event.preventDefault();
|
|
594
|
+
selectTokens(text);
|
|
595
|
+
setQuery("");
|
|
596
|
+
},
|
|
597
|
+
onKeyDown,
|
|
598
|
+
onCompositionStart: () => {
|
|
599
|
+
composingRef.current = true;
|
|
600
|
+
},
|
|
601
|
+
onCompositionEnd: () => {
|
|
602
|
+
composingRef.current = false;
|
|
603
|
+
},
|
|
604
|
+
placeholder: searchPlaceholder ?? t("dataEntry.searchSelect.search"),
|
|
605
|
+
className: "ui-search-select-search-input"
|
|
606
|
+
}
|
|
607
|
+
) }),
|
|
608
|
+
/* @__PURE__ */ jsxs(
|
|
609
|
+
"div",
|
|
610
|
+
{
|
|
611
|
+
id: listId,
|
|
612
|
+
role: "listbox",
|
|
613
|
+
"aria-multiselectable": multiple || void 0,
|
|
614
|
+
"aria-busy": loading,
|
|
615
|
+
className: "ui-search-select-list",
|
|
616
|
+
style: listHeight === void 0 ? void 0 : {
|
|
617
|
+
"--search-select-list-max-height": `${listHeight}px`
|
|
618
|
+
},
|
|
619
|
+
onScroll: (event) => {
|
|
620
|
+
onPopupScroll?.(event);
|
|
621
|
+
onScroll(event);
|
|
622
|
+
},
|
|
623
|
+
children: [
|
|
624
|
+
grouped.map((group) => {
|
|
625
|
+
const rows = group.items.map(({ option, index }) => /* @__PURE__ */ jsxs(
|
|
626
|
+
"div",
|
|
627
|
+
{
|
|
628
|
+
id: optionDomId(option.value),
|
|
629
|
+
role: "option",
|
|
630
|
+
"data-testid": optionTestId(option.value),
|
|
631
|
+
"aria-selected": isSelected(option.value),
|
|
632
|
+
"aria-disabled": option.disabled || atMaxCount && !isSelected(option.value) || void 0,
|
|
633
|
+
"data-disabled": option.disabled || atMaxCount && !isSelected(option.value) || void 0,
|
|
634
|
+
className: cn(
|
|
635
|
+
"ui-command-item",
|
|
636
|
+
// The background means pointer/keyboard POSITION. In a multi-value list the
|
|
637
|
+
// selection is the check mark plus weight, so the selected row and the active
|
|
638
|
+
// row never look alike. A single-value pick closes on click, so it keeps the
|
|
639
|
+
// plain SelectItem's persistent fill.
|
|
640
|
+
isSelected(option.value) && (multiple ? "font-medium" : "bg-accent text-foreground font-medium"),
|
|
641
|
+
activeIndex === index && "bg-accent text-accent-foreground"
|
|
642
|
+
),
|
|
643
|
+
onMouseEnter: () => !option.disabled && setActiveIndex(index),
|
|
644
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
645
|
+
onClick: () => select(option),
|
|
646
|
+
children: [
|
|
647
|
+
option === createOption ? (
|
|
648
|
+
// gh#1052: the create row SAYS it creates — antd shows the bare text, which
|
|
649
|
+
// reads like an existing option.
|
|
650
|
+
/* @__PURE__ */ jsx("div", { className: "ui-search-select-option-body", "data-slot": "select-create", children: /* @__PURE__ */ jsx("span", { className: "ui-search-select-option-label", children: createLabel ? createLabel(option.value) : t("dataEntry.searchSelect.create", { value: option.value }) }) })
|
|
651
|
+
) : optionRender ? /* @__PURE__ */ jsx("div", { className: "ui-search-select-option-slot", children: optionRender(option, { index }) }) : renderOption ? /* @__PURE__ */ jsx("div", { className: "ui-search-select-option-slot", children: renderOption(option) }) : /* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-body", children: [
|
|
652
|
+
option.icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", "aria-hidden": "true", children: option.icon }) : null,
|
|
653
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-text", children: [
|
|
654
|
+
/* @__PURE__ */ jsx("span", { className: "ui-search-select-option-label", children: option.label }),
|
|
655
|
+
option.sublabel ? /* @__PURE__ */ jsx("span", { className: "ui-search-select-option-sublabel", children: option.sublabel }) : null
|
|
656
|
+
] })
|
|
657
|
+
] }),
|
|
658
|
+
rowSelectedIcon && isSelected(option.value) ? /* @__PURE__ */ jsx(
|
|
659
|
+
"span",
|
|
660
|
+
{
|
|
661
|
+
"data-slot": "search-select-selected-icon",
|
|
662
|
+
className: "ui-search-select-selected-icon",
|
|
663
|
+
"aria-hidden": "true",
|
|
664
|
+
children: rowSelectedIcon
|
|
665
|
+
}
|
|
666
|
+
) : null
|
|
667
|
+
]
|
|
668
|
+
},
|
|
669
|
+
option.value
|
|
670
|
+
));
|
|
671
|
+
return group.heading ? /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: rows }, group.heading) : /* @__PURE__ */ jsx(React.Fragment, { children: rows }, "__ungrouped");
|
|
672
|
+
}),
|
|
673
|
+
loading ? /* @__PURE__ */ jsxs("div", { role: "status", className: "ui-search-select-status", children: [
|
|
674
|
+
/* @__PURE__ */ jsx(Loader2, { className: "ui-search-select-spinner animate-spin", "aria-hidden": "true" }),
|
|
675
|
+
loadingMessage ?? t("dataEntry.searchSelect.loading")
|
|
676
|
+
] }) : error ? renderError ? renderError({
|
|
677
|
+
message: errorMessage ?? t("dataEntry.searchSelect.error"),
|
|
678
|
+
// Retry always reloads from the first page (a defined, predictable recovery —
|
|
679
|
+
// NOT a resume of a failed page-N append, which would need to re-derive state).
|
|
680
|
+
retry: () => void fetchPage(1, debouncedQuery, false)
|
|
681
|
+
}) : /* @__PURE__ */ jsx(
|
|
682
|
+
"div",
|
|
683
|
+
{
|
|
684
|
+
role: "option",
|
|
685
|
+
"aria-disabled": "true",
|
|
686
|
+
"aria-selected": false,
|
|
687
|
+
className: "ui-search-select-placeholder",
|
|
688
|
+
"data-tone": "destructive",
|
|
689
|
+
children: errorMessage ?? t("dataEntry.searchSelect.error")
|
|
690
|
+
}
|
|
691
|
+
) : displayOptions.length === 0 ? /* @__PURE__ */ jsx(
|
|
692
|
+
"div",
|
|
693
|
+
{
|
|
694
|
+
role: "option",
|
|
695
|
+
"aria-disabled": "true",
|
|
696
|
+
"aria-selected": false,
|
|
697
|
+
className: "ui-search-select-placeholder",
|
|
698
|
+
children: notFoundContent ?? emptyMessage ?? t("dataEntry.searchSelect.empty")
|
|
699
|
+
}
|
|
700
|
+
) : null
|
|
701
|
+
]
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
renderLoadMore && hasMore ? /* @__PURE__ */ jsx(
|
|
705
|
+
"div",
|
|
706
|
+
{
|
|
707
|
+
className: "ui-search-select-footer",
|
|
708
|
+
onKeyDown: (event) => event.stopPropagation(),
|
|
709
|
+
children: renderLoadMore({
|
|
710
|
+
hasMore,
|
|
711
|
+
loading,
|
|
712
|
+
loadMore: () => {
|
|
713
|
+
if (!loading) void fetchPage(page + 1, debouncedQuery, true);
|
|
707
714
|
}
|
|
708
|
-
)
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
className: "ui-search-select-footer",
|
|
713
|
-
onKeyDown: (event) => event.stopPropagation(),
|
|
714
|
-
children: renderLoadMore({
|
|
715
|
-
hasMore,
|
|
716
|
-
loading,
|
|
717
|
-
loadMore: () => {
|
|
718
|
-
if (!loading) void fetchPage(page + 1, debouncedQuery, true);
|
|
719
|
-
}
|
|
720
|
-
})
|
|
721
|
-
}
|
|
722
|
-
) : null
|
|
723
|
-
]
|
|
724
|
-
}
|
|
725
|
-
) })
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
) : null
|
|
718
|
+
] }) })
|
|
726
719
|
}
|
|
727
720
|
)
|
|
728
721
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "31.28.
|
|
3
|
+
"version": "31.28.3",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -1446,8 +1446,9 @@ export type SearchSelectBaseProp = {
|
|
|
1446
1446
|
index: number;
|
|
1447
1447
|
}) => React.ReactNode;
|
|
1448
1448
|
/**
|
|
1449
|
-
* Node rendered on the SELECTED row (antd `menuItemSelectedIcon`).
|
|
1450
|
-
*
|
|
1449
|
+
* Node rendered on the SELECTED row (antd `menuItemSelectedIcon`). In `multiple` / `tags` it
|
|
1450
|
+
* defaults to a check mark (as in antd), because the row background there means pointer/keyboard
|
|
1451
|
+
* position, not selection; pass `null` to drop it. A single-value Select shows none by default.
|
|
1451
1452
|
*/
|
|
1452
1453
|
menuItemSelectedIcon?: React.ReactNode;
|
|
1453
1454
|
/** Node shown when the list has nothing to offer (antd `notFoundContent`). Beats `emptyMessage`. */
|
package/dist/styles/control.css
CHANGED
|
@@ -945,6 +945,11 @@
|
|
|
945
945
|
color: hsl(var(--accent-foreground));
|
|
946
946
|
}
|
|
947
947
|
|
|
948
|
+
.ui-search-select-list[aria-multiselectable="true"] .ui-command-item[aria-selected="true"] {
|
|
949
|
+
background: transparent;
|
|
950
|
+
color: inherit;
|
|
951
|
+
}
|
|
952
|
+
|
|
948
953
|
.ui-command-item[data-disabled="true"] {
|
|
949
954
|
pointer-events: none;
|
|
950
955
|
opacity: 0.5;
|
|
@@ -1859,9 +1864,12 @@
|
|
|
1859
1864
|
box-shadow: none;
|
|
1860
1865
|
}
|
|
1861
1866
|
|
|
1867
|
+
.ui-search-select-list .ui-command-item {
|
|
1868
|
+
line-height: var(--search-select-option-line-height);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1862
1871
|
.ui-search-select-list {
|
|
1863
1872
|
max-block-size: var(--search-select-list-max-height);
|
|
1864
|
-
max-block-size: none;
|
|
1865
1873
|
min-block-size: 0;
|
|
1866
1874
|
flex: 1 1 0%;
|
|
1867
1875
|
overflow-y: auto;
|
|
@@ -1309,17 +1309,17 @@
|
|
|
1309
1309
|
margin-block-start: var(--prose-space-block);
|
|
1310
1310
|
}
|
|
1311
1311
|
|
|
1312
|
-
.ui-prose :is(h1, h2, h3, h4) {
|
|
1312
|
+
.ui-prose :is(h1, h2, h3, h4, h5, h6) {
|
|
1313
1313
|
font-weight: var(--font-weight-semibold);
|
|
1314
1314
|
line-height: var(--line-height-tight);
|
|
1315
1315
|
text-wrap: balance;
|
|
1316
1316
|
}
|
|
1317
1317
|
|
|
1318
|
-
.ui-prose > :is(h1, h2, h3, h4) + * {
|
|
1318
|
+
.ui-prose > :is(h1, h2, h3, h4, h5, h6) + * {
|
|
1319
1319
|
margin-block-start: var(--prose-space-block);
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
1322
|
-
.ui-prose > * + :is(h1, h2, h3, h4) {
|
|
1322
|
+
.ui-prose > * + :is(h1, h2, h3, h4, h5, h6) {
|
|
1323
1323
|
margin-block-start: var(--prose-heading-space-block-start);
|
|
1324
1324
|
}
|
|
1325
1325
|
|
|
@@ -1341,14 +1341,23 @@
|
|
|
1341
1341
|
}
|
|
1342
1342
|
|
|
1343
1343
|
.ui-prose h3 {
|
|
1344
|
-
font-size: var(--heading-h3, var(--font-size-
|
|
1344
|
+
font-size: var(--heading-h3, calc(var(--prose-font-size) * var(--font-size-ratio)));
|
|
1345
1345
|
}
|
|
1346
1346
|
|
|
1347
1347
|
.ui-prose h4 {
|
|
1348
|
-
font-size: var(
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1348
|
+
font-size: var(--heading-h4, var(--prose-font-size));
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
.ui-prose :is(h5, h6) {
|
|
1352
|
+
font-size: var(--prose-font-size);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.ui-prose :is(h4, h5, h6) {
|
|
1356
|
+
font-weight: var(--font-weight-bold);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.ui-prose h6 {
|
|
1360
|
+
color: hsl(var(--muted-foreground));
|
|
1352
1361
|
}
|
|
1353
1362
|
|
|
1354
1363
|
.ui-prose :is(ul, ol) {
|
|
@@ -1434,8 +1443,14 @@
|
|
|
1434
1443
|
padding-block: var(--callout-space-inset-block);
|
|
1435
1444
|
padding-inline: var(--callout-space-inset-inline);
|
|
1436
1445
|
border-inline-start: var(--callout-rail-width) solid hsl(var(--info));
|
|
1437
|
-
border-start-end-radius: var(
|
|
1438
|
-
|
|
1446
|
+
border-start-end-radius: var(
|
|
1447
|
+
--callout-radius,
|
|
1448
|
+
var(--alert-radius, var(--card-radius, calc(var(--radius) * var(--radius-ratio))))
|
|
1449
|
+
);
|
|
1450
|
+
border-end-end-radius: var(
|
|
1451
|
+
--callout-radius,
|
|
1452
|
+
var(--alert-radius, var(--card-radius, calc(var(--radius) * var(--radius-ratio))))
|
|
1453
|
+
);
|
|
1439
1454
|
background-color: var(--surface-info, hsl(var(--info) / var(--alert-bg-alpha)));
|
|
1440
1455
|
}
|
|
1441
1456
|
|
|
@@ -212,7 +212,8 @@
|
|
|
212
212
|
--focus-ring-width-component: calc(var(--focus-ring-weight) * var(--focus-outline));
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
.ui-command-input:focus-visible
|
|
215
|
+
.ui-command-input:focus-visible,
|
|
216
|
+
.ui-search-select-search-input:focus-visible {
|
|
216
217
|
--focus-field-shadow: none;
|
|
217
218
|
--focus-ring-weight: 0px;
|
|
218
219
|
outline: none;
|
package/dist/styles/layers.json
CHANGED
|
@@ -262,9 +262,11 @@
|
|
|
262
262
|
--search-select-panel-viewport-inset: var(--space-6);
|
|
263
263
|
--search-select-list-space-inset: var(--space-1);
|
|
264
264
|
|
|
265
|
-
--search-select-list-max-height:
|
|
265
|
+
--search-select-list-max-height: 16rem;
|
|
266
266
|
--search-select-footer-space-inset: var(--space-1);
|
|
267
267
|
--search-select-option-space-gap: var(--space-2);
|
|
268
|
+
|
|
269
|
+
--search-select-option-line-height: var(--line-height-tight);
|
|
268
270
|
--search-select-option-font-size: var(--font-size-sm, var(--font-size-base));
|
|
269
271
|
--search-select-option-sublabel-font-size: var(
|
|
270
272
|
--font-size-xs,
|