@godxjp/ui 33.13.0 → 33.15.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/DataTable.json +2 -1
- package/agent/components/Persona.json +66 -0
- package/agent/components/Table.json +1 -1
- package/agent/components/Upload.json +1 -1
- package/agent/components-index.json +5 -0
- package/agent/components.json +70 -3
- package/agent/index.json +7 -7
- package/agent/llms.txt +7 -7
- package/agent/tokens.json +61 -1
- package/dist/components/data-display/data-table.js +51 -10
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/persona.d.ts +11 -0
- package/dist/components/data-display/persona.js +34 -0
- package/dist/components/data-entry/upload.js +9 -3
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +0 -1
- package/dist/i18n/messages/ja.json +0 -1
- package/dist/i18n/messages/vi.json +0 -1
- package/dist/props/components/data-display.prop.d.ts +10 -0
- package/dist/props/registry.d.ts +5 -0
- package/dist/props/registry.js +5 -0
- package/dist/styles/data-display-layout.css +58 -0
- package/dist/styles/layers.json +6 -1
- package/dist/styles/table-layout.css +38 -24
- package/dist/styles.css +1 -1
- package/dist/tokens/base.css +2 -0
- package/dist/tokens/components/persona.css +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ npm i @godxjp/ui
|
|
|
18
18
|
|
|
19
19
|
<!-- agent-catalog-counts:start -->
|
|
20
20
|
|
|
21
|
-
The catalog —
|
|
21
|
+
The catalog — 170 components, 2251 tokens, 44 cardinal rules — is published in two forms from one
|
|
22
22
|
source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
|
|
23
23
|
<!-- agent-catalog-counts:end -->
|
|
24
24
|
|
|
@@ -53,10 +53,10 @@ static files straight from this public repo — no hosting, no deploy step:
|
|
|
53
53
|
| [`agent/llms.txt`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/llms.txt) | 3 KB | the [llms.txt](https://llmstxt.org/) entry point |
|
|
54
54
|
| [`agent/DESIGN.md`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md) | 18 KB | the visual spec: tokens, colour scarcity, admin screen anatomy, do/don't |
|
|
55
55
|
| [`agent/index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json) | 3 KB | manifest: version, counts, every file's URL |
|
|
56
|
-
| [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 53 KB | all
|
|
57
|
-
| [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1 KB–
|
|
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) |
|
|
56
|
+
| [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 53 KB | all 170 as name + group + tagline — **fetch this first** |
|
|
57
|
+
| [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1 KB–38 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 170 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
|
|
59
|
+
| [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 860 KB | all 2251 design tokens, their values and why they exist |
|
|
60
60
|
| [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
|
|
61
61
|
| [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 23 KB | the 44 cardinal rules |
|
|
62
62
|
<!-- prettier-ignore-end -->
|
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.15.0.** If the project you are editing has a different
|
|
7
7
|
version in its `package.json`, read the pinned catalog for THAT version instead
|
|
8
8
|
(`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
|
|
9
9
|
not exist yet; older, and it hides props that do. Neither failure announces itself.
|
|
@@ -52,19 +52,19 @@ screen anatomy, the visual target the rules below do not give you. Then fetch in
|
|
|
52
52
|
task is a task** — "build a settings page", "confirm a destructive delete", "a list page with
|
|
53
53
|
filters" — start HERE, not at the components. Then fetch `patterns/<name>.json` for complete,
|
|
54
54
|
copy-paste-ready code. A component index answers "does X exist"; it cannot answer "build Y".
|
|
55
|
-
1. `components-index.json` — 53 KB, all
|
|
55
|
+
1. `components-index.json` — 53 KB, all 170 components as name + group +
|
|
56
56
|
tagline. Read this when you already know the SHAPE you need. Each entry may carry `absorbed`:
|
|
57
57
|
names that **do not exist** and map to it — `Combobox`, `Autocomplete`, `CountrySelect` and
|
|
58
58
|
`SearchSelect` are all `Select`. If you are about to hand-roll something, search this field
|
|
59
59
|
first; it exists because that is the mistake.
|
|
60
|
-
2. `components/<Name>.json` — one file per component (1 KB–
|
|
60
|
+
2. `components/<Name>.json` — one file per component (1 KB–38 KB, median 6 KB), carrying its props,
|
|
61
61
|
its `importPath`, and its examples. Fetch only the handful you picked in step 1.
|
|
62
62
|
3. `rules.json` — 44 cardinal rules. The ones about raw HTML and hardcoded colour are not
|
|
63
63
|
style advice.
|
|
64
|
-
4. `tokens.json` —
|
|
64
|
+
4. `tokens.json` — 2251 design tokens, each tagged with its `tier`. **If you were handed a
|
|
65
65
|
brand, read the 218 `foundation` entries first** — `--primary`, `--background`,
|
|
66
66
|
`--radius`, `--font-size-base` are the handful everything else derives from. The
|
|
67
|
-
|
|
67
|
+
1920 `component` entries are per-part knobs; reach for one only when a role is
|
|
68
68
|
right everywhere except one component.
|
|
69
69
|
5. `anti-ai-tells.json` — 35 shapes that make generated UI look generated, each with the
|
|
70
70
|
fix. Read before you reach for a gradient hero or a wall of coloured chips.
|
|
@@ -150,7 +150,7 @@ has stopped following the brand.
|
|
|
150
150
|
|---|---|---|---|
|
|
151
151
|
| `foundation` | 218 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** The default `--primary` is a neutral placeholder: set it (`:root { --primary: <H> <S>% <L>%; }`, HSL components, no `hsl()` wrapper) or import a theme preset such as `@godxjp/ui/themes/godx.css` |
|
|
152
152
|
| `semantic` | 113 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
|
|
153
|
-
| `component` |
|
|
153
|
+
| `component` | 1920 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
|
|
154
154
|
|
|
155
155
|
A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
|
|
156
156
|
value, so the real default is computed where the element paints it. Set it and yours wins.
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
"defaultValue": "'default'",
|
|
89
|
-
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue:
|
|
89
|
+
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: at EVERY width the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*: primary 18% · secondary 22% · meta 12% · actions a 3.5rem floor, the unmarked column takes the rest) under table-layout: fixed, and below collapseBelow they switch to the compact tier (24% · 22% · 20% · 2.75rem) — collapseBelow picks the TIER, it does not turn the measures on (gh#1408). Cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
|
|
90
90
|
"name": "preset",
|
|
91
91
|
"type": "'default' | 'action-collection' | 'stacked-record-collection'"
|
|
92
92
|
},
|
|
@@ -238,6 +238,7 @@
|
|
|
238
238
|
"storyPath": "data-display/data-table/index.tsx",
|
|
239
239
|
"tagline": "The one TanStack-powered compound admin list — sticky header, sorting, global search, column visibility ('set view'), bulk selection, BOTH cursor and numbered pagination, density, and built-in empty/loading states. Keep the SIMPLE `data` + lean `columns` (ColumnDef) API for the common case; opt into the full grid chrome via the compound parts. Internally driven by @tanstack/react-table (a real dependency). Lives on @godxjp/ui/data-display only (it is NOT on the runtime-neutral root/admin barrel because it pulls TanStack). Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout.",
|
|
240
240
|
"usage": [
|
|
241
|
+
"Ellipsis cells expose full rendered text for default/custom renderers, including stacked layout. Renderer-owned descendant titles or aria-describedby tooltips suppress a duplicate cell title (#1406).",
|
|
241
242
|
"DO use `striped` on dense list tables (many columns, a row the eye must follow across the width). To stripe EVERY Table and DataTable in a service, set it ONCE in the theme — `:root { --table-row-striped-alpha: 100%; }` — instead of passing `striped` at each call site; `striped={false}` then opts one table out. Retint with `--table-row-striped-background`, never with a `rowClassName` utility or `:nth-child` page CSS (those count DOM rows, so an expanded detail row shifts every stripe after it).",
|
|
242
243
|
"DO pass loading={isFetching} during data fetches — it renders a loading row in the table body and suppresses the empty state. Never show a spinner outside DataTable while the table is visible.",
|
|
243
244
|
"DO NOT add a data.length===0 conditional around DataTable. When data is empty and loading is false, the built-in EmptyState renders automatically. Pass empty={<EmptyState title='...'/>} only when you need a custom message.",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"docPath": "data-display/persona.tsx",
|
|
3
|
+
"example": "import { Persona } from \"@godxjp/ui/data-display\";\n\n<Persona name={member.name} description={member.email} size=\"sm\" />",
|
|
4
|
+
"group": "data-display",
|
|
5
|
+
"importPath": "@godxjp/ui/data-display",
|
|
6
|
+
"name": "Persona",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"description": "Full primary label, retained in native title and accessible text; dir=auto preserves its beginning in RTL.",
|
|
10
|
+
"name": "name",
|
|
11
|
+
"required": true,
|
|
12
|
+
"type": "string"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"description": "Optional full secondary label (email/handle), independently truncated and titled with dir=auto.",
|
|
16
|
+
"name": "description",
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "Optional image URL; Avatar owns loading and fallback.",
|
|
21
|
+
"name": "avatarSrc",
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "Fallback content; defaults to the first Unicode code point of name. Avatar is decorative beside the full name.",
|
|
26
|
+
"name": "avatarFallback",
|
|
27
|
+
"type": "ReactNode"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"description": "Optional noninteractive badge beside the name, outside its truncation box.",
|
|
31
|
+
"name": "trailing",
|
|
32
|
+
"type": "ReactNode"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"defaultValue": "\"md\"",
|
|
36
|
+
"description": "Avatar control tier and name type scale; secondary text stays xs.",
|
|
37
|
+
"name": "size",
|
|
38
|
+
"type": "\"sm\" | \"md\""
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"description": "Root class; native span attributes and ref are forwarded.",
|
|
42
|
+
"name": "className",
|
|
43
|
+
"type": "string"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"related": [
|
|
47
|
+
"Avatar — mark only",
|
|
48
|
+
"ListRow — padded short-list row with actions",
|
|
49
|
+
"AccountChip — signed-in account control"
|
|
50
|
+
],
|
|
51
|
+
"rules": [
|
|
52
|
+
6,
|
|
53
|
+
45
|
|
54
|
+
],
|
|
55
|
+
"storyPath": "data-display/persona.tsx",
|
|
56
|
+
"tagline": "Compact person identity: fixed avatar, independently truncated name and muted secondary line, optional inline badge.",
|
|
57
|
+
"usage": [
|
|
58
|
+
"Use inside DataTable renderers and stacked rows. Persona owns no row padding, navigation, account action or business state.",
|
|
59
|
+
"Pass localized labels and badge content. Retune --persona-* tokens; avatar sizing uses existing --avatar-* tokens.",
|
|
60
|
+
"Both full labels stay in the accessibility tree and native titles. Keep trailing content noninteractive."
|
|
61
|
+
],
|
|
62
|
+
"useCases": [
|
|
63
|
+
"Member lists",
|
|
64
|
+
"Assignee and owner cells"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"defaultValue": "\"default\"",
|
|
72
|
-
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a
|
|
72
|
+
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
|
|
73
73
|
"name": "preset",
|
|
74
74
|
"type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
|
|
75
75
|
},
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"props": [
|
|
7
7
|
{
|
|
8
8
|
"defaultValue": "\"dropzone\"",
|
|
9
|
-
"description": "Controls the visual rendering mode. dropzone = large dashed drop area + file list; button = compact outline button + file list; picture-card = grid of 96×96 image thumbnails; picture = single image preview with change/remove actions; avatar = circular single-image picker; avatar-crop = avatar with an in-dialog crop step before the item is staged.",
|
|
9
|
+
"description": "Controls the visual rendering mode. dropzone = large dashed drop area + file list, named by its VISIBLE hint (「ここにドラッグ&ドロップ、またはクリックして選択」 — WCAG 2.5.3 label-in-name, gh#1412; a FormField label leads it, a caller aria-label replaces it); button = compact outline button + file list; picture-card = grid of 96×96 image thumbnails; picture = single image preview with change/remove actions; avatar = circular single-image picker; avatar-crop = avatar with an in-dialog crop step before the item is staged.",
|
|
10
10
|
"name": "variant",
|
|
11
11
|
"type": "\"dropzone\" | \"button\" | \"picture-card\" | \"picture\" | \"avatar\" | \"avatar-crop\""
|
|
12
12
|
},
|
|
@@ -263,6 +263,11 @@
|
|
|
263
263
|
"name": "FeatureList",
|
|
264
264
|
"tagline": "A list of STATEMENTS, each with a leading state glyph (included ✓ / limited − / excluded ✗), an optional wrapping description, and the glyph aligned to the first text line."
|
|
265
265
|
},
|
|
266
|
+
{
|
|
267
|
+
"group": "data-display",
|
|
268
|
+
"name": "Persona",
|
|
269
|
+
"tagline": "Compact person identity: fixed avatar, independently truncated name and muted secondary line, optional inline badge."
|
|
270
|
+
},
|
|
266
271
|
{
|
|
267
272
|
"group": "data-display",
|
|
268
273
|
"name": "ListRow",
|
package/agent/components.json
CHANGED
|
@@ -3489,7 +3489,7 @@
|
|
|
3489
3489
|
},
|
|
3490
3490
|
{
|
|
3491
3491
|
"defaultValue": "'default'",
|
|
3492
|
-
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue:
|
|
3492
|
+
"description": "Named collection contract — the SAME preset the Table primitive owns, forwarded to the table DataTable renders. 'default' emits NO attribute and matches no selector, so an existing DataTable is byte-identical. 'action-collection' is the canonical dense approval/action queue: at EVERY width the desktop INTRINSIC column widths give way to the token-owned column-PRIORITY measures (--table-action-collection-*: primary 18% · secondary 22% · meta 12% · actions a 3.5rem floor, the unmarked column takes the rest) under table-layout: fixed, and below collapseBelow they switch to the compact tier (24% · 22% · 20% · 2.75rem) — collapseBelow picks the TIER, it does not turn the measures on (gh#1408). Cells wrap, and the bordered surface drops its --table-surface-min-inline-size floor — so requester · target · reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll. Mark each column with `priority` on its ColumnDef. Semantics are untouched (no display change, no role rewriting, no card swap), so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. Measured: table 1182 / 766 / 388px at 1440 / 1024 / 390, document scrollWidth === clientWidth at every width, LTR and RTL. 'stacked-record-collection' is the other direction, for a WIDE, HETEROGENEOUS record set that has no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Each cell then carries its column's header inline above the value, DERIVED from the same ColumnDef.header the <th> uses — so the card cannot drift from the table, and a column with a deliberately empty header names itself with ariaLabel (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The labels are aria-hidden: the real <th> is still in the DOM and remains the accessible-name source, so screen-reader table navigation is unchanged at every width. Reach for this instead of building a parallel Card tree beside the table; two trees for one dataset means two sets of labels to keep in sync.",
|
|
3493
3493
|
"name": "preset",
|
|
3494
3494
|
"type": "'default' | 'action-collection' | 'stacked-record-collection'"
|
|
3495
3495
|
},
|
|
@@ -3641,6 +3641,7 @@
|
|
|
3641
3641
|
"storyPath": "data-display/data-table/index.tsx",
|
|
3642
3642
|
"tagline": "The one TanStack-powered compound admin list — sticky header, sorting, global search, column visibility ('set view'), bulk selection, BOTH cursor and numbered pagination, density, and built-in empty/loading states. Keep the SIMPLE `data` + lean `columns` (ColumnDef) API for the common case; opt into the full grid chrome via the compound parts. Internally driven by @tanstack/react-table (a real dependency). Lives on @godxjp/ui/data-display only (it is NOT on the runtime-neutral root/admin barrel because it pulls TanStack). Keyboard focus uses the shared inset ring in clipping surfaces; scrollports reveal partially clipped keyboard targets without changing their layout.",
|
|
3643
3643
|
"usage": [
|
|
3644
|
+
"Ellipsis cells expose full rendered text for default/custom renderers, including stacked layout. Renderer-owned descendant titles or aria-describedby tooltips suppress a duplicate cell title (#1406).",
|
|
3644
3645
|
"DO use `striped` on dense list tables (many columns, a row the eye must follow across the width). To stripe EVERY Table and DataTable in a service, set it ONCE in the theme — `:root { --table-row-striped-alpha: 100%; }` — instead of passing `striped` at each call site; `striped={false}` then opts one table out. Retint with `--table-row-striped-background`, never with a `rowClassName` utility or `:nth-child` page CSS (those count DOM rows, so an expanded detail row shifts every stripe after it).",
|
|
3645
3646
|
"DO pass loading={isFetching} during data fetches — it renders a loading row in the table body and suppresses the empty state. Never show a spinner outside DataTable while the table is visible.",
|
|
3646
3647
|
"DO NOT add a data.length===0 conditional around DataTable. When data is empty and loading is false, the built-in EmptyState renders automatically. Pass empty={<EmptyState title='...'/>} only when you need a custom message.",
|
|
@@ -4361,6 +4362,72 @@
|
|
|
4361
4362
|
"A capability or compatibility list — supported / partially supported / unsupported."
|
|
4362
4363
|
]
|
|
4363
4364
|
},
|
|
4365
|
+
{
|
|
4366
|
+
"docPath": "data-display/persona.tsx",
|
|
4367
|
+
"example": "import { Persona } from \"@godxjp/ui/data-display\";\n\n<Persona name={member.name} description={member.email} size=\"sm\" />",
|
|
4368
|
+
"group": "data-display",
|
|
4369
|
+
"importPath": "@godxjp/ui/data-display",
|
|
4370
|
+
"name": "Persona",
|
|
4371
|
+
"props": [
|
|
4372
|
+
{
|
|
4373
|
+
"description": "Full primary label, retained in native title and accessible text; dir=auto preserves its beginning in RTL.",
|
|
4374
|
+
"name": "name",
|
|
4375
|
+
"required": true,
|
|
4376
|
+
"type": "string"
|
|
4377
|
+
},
|
|
4378
|
+
{
|
|
4379
|
+
"description": "Optional full secondary label (email/handle), independently truncated and titled with dir=auto.",
|
|
4380
|
+
"name": "description",
|
|
4381
|
+
"type": "string"
|
|
4382
|
+
},
|
|
4383
|
+
{
|
|
4384
|
+
"description": "Optional image URL; Avatar owns loading and fallback.",
|
|
4385
|
+
"name": "avatarSrc",
|
|
4386
|
+
"type": "string"
|
|
4387
|
+
},
|
|
4388
|
+
{
|
|
4389
|
+
"description": "Fallback content; defaults to the first Unicode code point of name. Avatar is decorative beside the full name.",
|
|
4390
|
+
"name": "avatarFallback",
|
|
4391
|
+
"type": "ReactNode"
|
|
4392
|
+
},
|
|
4393
|
+
{
|
|
4394
|
+
"description": "Optional noninteractive badge beside the name, outside its truncation box.",
|
|
4395
|
+
"name": "trailing",
|
|
4396
|
+
"type": "ReactNode"
|
|
4397
|
+
},
|
|
4398
|
+
{
|
|
4399
|
+
"defaultValue": "\"md\"",
|
|
4400
|
+
"description": "Avatar control tier and name type scale; secondary text stays xs.",
|
|
4401
|
+
"name": "size",
|
|
4402
|
+
"type": "\"sm\" | \"md\""
|
|
4403
|
+
},
|
|
4404
|
+
{
|
|
4405
|
+
"description": "Root class; native span attributes and ref are forwarded.",
|
|
4406
|
+
"name": "className",
|
|
4407
|
+
"type": "string"
|
|
4408
|
+
}
|
|
4409
|
+
],
|
|
4410
|
+
"related": [
|
|
4411
|
+
"Avatar — mark only",
|
|
4412
|
+
"ListRow — padded short-list row with actions",
|
|
4413
|
+
"AccountChip — signed-in account control"
|
|
4414
|
+
],
|
|
4415
|
+
"rules": [
|
|
4416
|
+
6,
|
|
4417
|
+
45
|
|
4418
|
+
],
|
|
4419
|
+
"storyPath": "data-display/persona.tsx",
|
|
4420
|
+
"tagline": "Compact person identity: fixed avatar, independently truncated name and muted secondary line, optional inline badge.",
|
|
4421
|
+
"usage": [
|
|
4422
|
+
"Use inside DataTable renderers and stacked rows. Persona owns no row padding, navigation, account action or business state.",
|
|
4423
|
+
"Pass localized labels and badge content. Retune --persona-* tokens; avatar sizing uses existing --avatar-* tokens.",
|
|
4424
|
+
"Both full labels stay in the accessibility tree and native titles. Keep trailing content noninteractive."
|
|
4425
|
+
],
|
|
4426
|
+
"useCases": [
|
|
4427
|
+
"Member lists",
|
|
4428
|
+
"Assignee and owner cells"
|
|
4429
|
+
]
|
|
4430
|
+
},
|
|
4364
4431
|
{
|
|
4365
4432
|
"example": "import { Card, CardContent, CardHeader, CardTitle, ListRow, Badge } from \"@godxjp/ui/data-display\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Smartphone } from \"lucide-react\";\nimport { Flex } from \"@godxjp/ui/layout\";\n\n// The three scenarios below are three CARDS on one page, so they are wrapped in a Flex — the gap\n// between sibling cards belongs to the stack, never to the cards (audit: sibling-cards-need-flex).\n<Flex direction=\"col\" gap=\"lg\">\n<Card>\n <CardHeader>\n <CardTitle>アクティブなセッション</CardTitle>\n </CardHeader>\n <CardContent flush>\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"iPhone 15 · Tokyo\"\n description=\"最終アクセス 2分前\"\n trailing={<Badge status=\"active\" />}\n />\n <ListRow\n leading={<Smartphone aria-hidden=\"true\" className=\"size-4\" />}\n title=\"MacBook Pro · Osaka\"\n description=\"最終アクセス 3日前\"\n trailing={<Button size=\"xs\" variant=\"outline\">ログアウト</Button>}\n />\n </CardContent>\n</Card>\n\n// Notifications — unread dot + emphasized surface, wrapping title, two inline actions\n<Card>\n <CardContent flush>\n <ListRow\n unread\n align=\"start\"\n overflow=\"wrap\"\n title=\"組織「グローバル・トランスフォーメーション推進本部」への招待が届いています\"\n description=\"2026-07-30 09:12 JST\"\n trailing={\n <>\n <Button size=\"xs\" variant=\"ghost\">既読にする</Button>\n <Button size=\"xs\" variant=\"outline\">開く</Button>\n </>\n }\n />\n <ListRow unread={false} align=\"start\" overflow=\"wrap\" title=\"請求書が発行されました\" description=\"2026-07-28 18:40 JST\" />\n </CardContent>\n</Card>\n\n{/* A list of LINKS — `as=\"li\"` gives the list item, `asChild` gives the whole-row link, and the\n item carries the divider. Never wrap the row in your own li, and never reach for a raw ul:\n `marker=\"none\"` is the semantic list container (no bullet, no indent, gap token). */}\n<Card>\n <CardContent flush>\n <Flex as=\"ul\" marker=\"none\" direction=\"col\" gap=\"none\">\n {projects.map((project) => (\n <ListRow key={project.key} as=\"li\" asChild title={project.name} description={project.key}>\n <Link href={`/projects/${project.key}`} />\n </ListRow>\n ))}\n </Flex>\n </CardContent>\n</Card>\n</Flex>",
|
|
4366
4433
|
"group": "data-display",
|
|
@@ -5629,7 +5696,7 @@
|
|
|
5629
5696
|
},
|
|
5630
5697
|
{
|
|
5631
5698
|
"defaultValue": "\"default\"",
|
|
5632
|
-
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a
|
|
5699
|
+
"description": "Named collection contract. \"default\" emits no attribute and keeps the plain table. \"action-collection\" is the canonical dense approval/action queue: the desktop INTRINSIC column widths (which make a five-column queue wider than its card and force a horizontal scroll at 390) are replaced by table-layout: fixed plus the token-owned column PRIORITY measures (--table-action-collection-*), and cells wrap. Mark each column with `priority` on its TableHead AND its TableCell. Semantics are untouched — no display change, no role rewriting, no card transformation — so header association, aria-sort and screen-reader table navigation are identical at 390 and 1440. \"stacked-record-collection\" is the other direction, for a WIDE, HETEROGENEOUS record set with no sensible narrow column measure: below collapseBelow the <thead> hides and every <tr> becomes a key-value record with a single separator between records (--table-stacked-collection-*). Give each TableCell a `label` — its column header — because the <th> association it normally reads from is the thing that just went away; DataTable derives that label from ColumnDef.header for you (gh#864). In card mode the row inset defaults to the live --card-space-inset, sharing the CardTitle keyline at every density and in RTL (override --table-stacked-collection-card-padding-x independently). The CARD owns the inline inset: a folded cell has no inline padding of its own (--table-stacked-collection-cell-padding-x, default 0), and an EMPTY cell (content null) prints no label and drops out of the card (gh#1102). Every folded cell reads from the START: a column's `align`/`numeric` end or centre alignment belongs to the table, not the card, so a count sits under its label like every other field (gh#1310). The label is aria-hidden and the real <th> stays in the DOM, so the accessible name and table navigation are unchanged.",
|
|
5633
5700
|
"name": "preset",
|
|
5634
5701
|
"type": "\"default\" | \"action-collection\" | \"stacked-record-collection\""
|
|
5635
5702
|
},
|
|
@@ -10315,7 +10382,7 @@
|
|
|
10315
10382
|
"props": [
|
|
10316
10383
|
{
|
|
10317
10384
|
"defaultValue": "\"dropzone\"",
|
|
10318
|
-
"description": "Controls the visual rendering mode. dropzone = large dashed drop area + file list; button = compact outline button + file list; picture-card = grid of 96×96 image thumbnails; picture = single image preview with change/remove actions; avatar = circular single-image picker; avatar-crop = avatar with an in-dialog crop step before the item is staged.",
|
|
10385
|
+
"description": "Controls the visual rendering mode. dropzone = large dashed drop area + file list, named by its VISIBLE hint (「ここにドラッグ&ドロップ、またはクリックして選択」 — WCAG 2.5.3 label-in-name, gh#1412; a FormField label leads it, a caller aria-label replaces it); button = compact outline button + file list; picture-card = grid of 96×96 image thumbnails; picture = single image preview with change/remove actions; avatar = circular single-image picker; avatar-crop = avatar with an in-dialog crop step before the item is staged.",
|
|
10319
10386
|
"name": "variant",
|
|
10320
10387
|
"type": "\"dropzone\" | \"button\" | \"picture-card\" | \"picture\" | \"avatar\" | \"avatar-crop\""
|
|
10321
10388
|
},
|
package/agent/index.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
3
|
"anti-ai-tells": 35,
|
|
4
|
-
"components":
|
|
4
|
+
"components": 170,
|
|
5
5
|
"patterns": 28,
|
|
6
6
|
"rules": 44,
|
|
7
|
-
"tokens":
|
|
7
|
+
"tokens": 2251,
|
|
8
8
|
"vocabulary": 15
|
|
9
9
|
},
|
|
10
10
|
"design": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/DESIGN.md",
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"file": "components-index.json",
|
|
19
|
-
"note": "53 KB — name + group + tagline for all
|
|
19
|
+
"note": "53 KB — name + group + tagline for all 170. FETCH THIS FIRST, then fetch only the components you chose.",
|
|
20
20
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"file": "components/<Name>.json",
|
|
24
|
-
"note": "One file per component (1 KB–
|
|
24
|
+
"note": "One file per component (1 KB–38 KB, median 6 KB), each carrying its importPath. This is the selective route: read the index, then fetch only what you need instead of the 1.3 MB blob.",
|
|
25
25
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/<Name>.json"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
@@ -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.15.0/agent/index.json"
|
|
58
58
|
},
|
|
59
59
|
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
|
|
60
60
|
"start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
|
|
61
61
|
"tokenTiers": {
|
|
62
62
|
"component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
|
|
63
63
|
"counts": {
|
|
64
|
-
"component":
|
|
64
|
+
"component": 1920,
|
|
65
65
|
"foundation": 218,
|
|
66
66
|
"semantic": 113
|
|
67
67
|
},
|
|
68
68
|
"foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
|
|
69
69
|
"semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
|
|
70
70
|
},
|
|
71
|
-
"version": "33.
|
|
71
|
+
"version": "33.15.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: 170 components, 2251 design tokens,
|
|
4
|
+
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.15.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.15.0`). It is searchable and version-locked. These files exist for agents
|
|
8
8
|
that can only fetch URLs.
|
|
9
9
|
|
|
10
10
|
## Start
|
|
@@ -16,10 +16,10 @@ that can only fetch URLs.
|
|
|
16
16
|
## Catalog
|
|
17
17
|
|
|
18
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): 53 KB — all
|
|
20
|
-
- [components/<Name>.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–
|
|
19
|
+
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 53 KB — all 170 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
|
|
20
|
+
- [components/<Name>.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–38 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` — 218 `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` — 218 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 113 `semantic` roles, 1920 `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.15.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.
|
package/agent/tokens.json
CHANGED
|
@@ -10073,6 +10073,66 @@
|
|
|
10073
10073
|
"tier": "component",
|
|
10074
10074
|
"value": "var(--space-2)"
|
|
10075
10075
|
},
|
|
10076
|
+
{
|
|
10077
|
+
"description": "Default var(--space-2).",
|
|
10078
|
+
"name": "--persona-avatar-gap",
|
|
10079
|
+
"tier": "component",
|
|
10080
|
+
"value": "initial"
|
|
10081
|
+
},
|
|
10082
|
+
{
|
|
10083
|
+
"description": "Default zero, set at the call site.",
|
|
10084
|
+
"name": "--persona-label-gap",
|
|
10085
|
+
"tier": "component",
|
|
10086
|
+
"value": "initial"
|
|
10087
|
+
},
|
|
10088
|
+
{
|
|
10089
|
+
"description": "Default var(--space-1).",
|
|
10090
|
+
"name": "--persona-trailing-gap",
|
|
10091
|
+
"tier": "component",
|
|
10092
|
+
"value": "initial"
|
|
10093
|
+
},
|
|
10094
|
+
{
|
|
10095
|
+
"description": "Identity geometry. Initial spacing/type defaults resolve against the local density.",
|
|
10096
|
+
"name": "--persona-label-line-height",
|
|
10097
|
+
"tier": "component",
|
|
10098
|
+
"value": "1.5"
|
|
10099
|
+
},
|
|
10100
|
+
{
|
|
10101
|
+
"description": "Default var(--font-size-sm).",
|
|
10102
|
+
"name": "--persona-name-font-size",
|
|
10103
|
+
"tier": "component",
|
|
10104
|
+
"value": "initial"
|
|
10105
|
+
},
|
|
10106
|
+
{
|
|
10107
|
+
"description": "Default var(--font-size-xs).",
|
|
10108
|
+
"name": "--persona-name-font-size-sm",
|
|
10109
|
+
"tier": "component",
|
|
10110
|
+
"value": "initial"
|
|
10111
|
+
},
|
|
10112
|
+
{
|
|
10113
|
+
"description": "Default var(--font-weight-medium).",
|
|
10114
|
+
"name": "--persona-name-font-weight",
|
|
10115
|
+
"tier": "component",
|
|
10116
|
+
"value": "initial"
|
|
10117
|
+
},
|
|
10118
|
+
{
|
|
10119
|
+
"description": "Default var(--font-size-xs).",
|
|
10120
|
+
"name": "--persona-description-font-size",
|
|
10121
|
+
"tier": "component",
|
|
10122
|
+
"value": "initial"
|
|
10123
|
+
},
|
|
10124
|
+
{
|
|
10125
|
+
"description": "Role mirror: hsl(var(--foreground)).",
|
|
10126
|
+
"name": "--persona-name-color",
|
|
10127
|
+
"tier": "component",
|
|
10128
|
+
"value": "initial"
|
|
10129
|
+
},
|
|
10130
|
+
{
|
|
10131
|
+
"description": "Role mirror: hsl(var(--muted-foreground)).",
|
|
10132
|
+
"name": "--persona-description-color",
|
|
10133
|
+
"tier": "component",
|
|
10134
|
+
"value": "initial"
|
|
10135
|
+
},
|
|
10076
10136
|
{
|
|
10077
10137
|
"description": "Separator layout thickness; focus paint must not reserve extra space.",
|
|
10078
10138
|
"name": "--resizable-handle-size",
|
|
@@ -12534,7 +12594,7 @@
|
|
|
12534
12594
|
"value": "var(--space-stack-sm)"
|
|
12535
12595
|
},
|
|
12536
12596
|
{
|
|
12537
|
-
"description": "default = Card inset
|
|
12597
|
+
"description": "default = Card inset, resolved at the row",
|
|
12538
12598
|
"name": "--table-stacked-collection-card-padding-x",
|
|
12539
12599
|
"tier": "component",
|
|
12540
12600
|
"value": "initial"
|
|
@@ -80,6 +80,33 @@ import {
|
|
|
80
80
|
} from "../../lib/control-styles.js";
|
|
81
81
|
import { isDevelopment } from "../../lib/dev.js";
|
|
82
82
|
import { pageSizeOptionsWith } from "../navigation/pagination-utils.js";
|
|
83
|
+
function recoverCellText(cell) {
|
|
84
|
+
if (!cell) return;
|
|
85
|
+
const part = truncatedPart(cell);
|
|
86
|
+
const value = part && part !== cell ? part : cell.querySelector(".ui-table-stacked-collection-value") ?? cell;
|
|
87
|
+
const selector = '[title]:not([title=""]), [aria-describedby]';
|
|
88
|
+
const carrier = value.closest(selector);
|
|
89
|
+
const recovery = carrier && carrier !== cell ? carrier : value.querySelector(selector);
|
|
90
|
+
if (recovery) {
|
|
91
|
+
if (cell.dataset.cellTitle !== void 0) {
|
|
92
|
+
cell.removeAttribute("title");
|
|
93
|
+
delete cell.dataset.cellTitle;
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!cell.title || cell.dataset.cellTitle !== void 0) {
|
|
98
|
+
const content = value.cloneNode(true);
|
|
99
|
+
content.querySelectorAll('[aria-hidden="true"], [hidden], .sr-only, [role="tooltip"]').forEach((node) => node.remove());
|
|
100
|
+
const text = (content.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
101
|
+
if (text) {
|
|
102
|
+
if (cell.title !== text) cell.title = text;
|
|
103
|
+
cell.dataset.cellTitle = "";
|
|
104
|
+
} else {
|
|
105
|
+
cell.removeAttribute("title");
|
|
106
|
+
delete cell.dataset.cellTitle;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
83
110
|
function resolveColumnHideBelow(col) {
|
|
84
111
|
if (col.hideBelow) return col.hideBelow;
|
|
85
112
|
if (col.hiddenOnMobile) return "md";
|
|
@@ -913,27 +940,40 @@ DataTable.Content = function DataTableContent() {
|
|
|
913
940
|
observer.disconnect();
|
|
914
941
|
};
|
|
915
942
|
}, [fixedSignature]);
|
|
916
|
-
const
|
|
943
|
+
const ellipsisEnabled = visibleColumns.some((col) => col.ellipsis);
|
|
944
|
+
const hasEllipsis = !stacked && ellipsisEnabled;
|
|
945
|
+
React.useLayoutEffect(() => {
|
|
946
|
+
const root = scrollRef.current;
|
|
947
|
+
if (!root || !ellipsisEnabled) return;
|
|
948
|
+
const sync = () => {
|
|
949
|
+
for (const cell of root.querySelectorAll(
|
|
950
|
+
"tbody td.ui-data-table-ellipsis"
|
|
951
|
+
))
|
|
952
|
+
recoverCellText(cell);
|
|
953
|
+
};
|
|
954
|
+
sync();
|
|
955
|
+
const observer = new MutationObserver(sync);
|
|
956
|
+
observer.observe(root, {
|
|
957
|
+
subtree: true,
|
|
958
|
+
childList: true,
|
|
959
|
+
characterData: true,
|
|
960
|
+
attributes: true,
|
|
961
|
+
attributeFilter: ["title", "aria-describedby"]
|
|
962
|
+
});
|
|
963
|
+
return () => observer.disconnect();
|
|
964
|
+
});
|
|
917
965
|
const markTruncated = React.useCallback(() => {
|
|
918
966
|
const el = scrollRef.current;
|
|
919
967
|
if (!el) return;
|
|
920
968
|
for (const cell of el.querySelectorAll("tbody td.ui-data-table-ellipsis")) {
|
|
921
969
|
if (cell.contains(document.activeElement)) continue;
|
|
922
|
-
|
|
970
|
+
recoverCellText(cell);
|
|
923
971
|
const part = truncatedPart(cell);
|
|
924
972
|
if (part) {
|
|
925
973
|
cell.dataset.truncated = "";
|
|
926
|
-
if ((!cell.title || ownTitle) && (part === cell || !part.title)) {
|
|
927
|
-
cell.title = (part.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
928
|
-
cell.dataset.truncatedTitle = "";
|
|
929
|
-
}
|
|
930
974
|
if (!cell.querySelector(TRUNCATION_FOCUSABLE)) cell.tabIndex = 0;
|
|
931
975
|
} else if (cell.dataset.truncated !== void 0) {
|
|
932
976
|
delete cell.dataset.truncated;
|
|
933
|
-
if (ownTitle) {
|
|
934
|
-
cell.removeAttribute("title");
|
|
935
|
-
delete cell.dataset.truncatedTitle;
|
|
936
|
-
}
|
|
937
977
|
cell.removeAttribute("tabindex");
|
|
938
978
|
}
|
|
939
979
|
}
|
|
@@ -1380,6 +1420,7 @@ DataTable.Content = function DataTableContent() {
|
|
|
1380
1420
|
return /* @__PURE__ */ jsx(
|
|
1381
1421
|
TableCell,
|
|
1382
1422
|
{
|
|
1423
|
+
ref: col.ellipsis ? recoverCellText : void 0,
|
|
1383
1424
|
priority: col.priority,
|
|
1384
1425
|
"data-align": col.align,
|
|
1385
1426
|
title,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Persona } from "./persona.js";
|
|
1
2
|
import { Badge, StatusBadge } from "./badge.js";
|
|
2
3
|
import { Legend } from "./legend.js";
|
|
3
4
|
import { Swatch } from "./swatch.js";
|
|
@@ -78,6 +79,7 @@ export {
|
|
|
78
79
|
Legend,
|
|
79
80
|
ListRow,
|
|
80
81
|
PermissionMatrix,
|
|
82
|
+
Persona,
|
|
81
83
|
Popover,
|
|
82
84
|
PopoverAnchor,
|
|
83
85
|
PopoverContent,
|