@godxjp/ui 31.2.0 → 31.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/agent/START-HERE.md +6 -6
- package/agent/components/AuthExpiryProvider.json +40 -0
- package/agent/components/DataState.json +3 -1
- package/agent/components/InfiniteQueryState.json +1 -1
- package/agent/components/Tabs.json +5 -0
- package/agent/components-index.json +5 -0
- package/agent/components.json +49 -2
- package/agent/index.json +7 -7
- package/agent/llms.txt +6 -6
- package/agent/patterns/async-data-state.json +1 -1
- package/agent/patterns.json +1 -1
- package/agent/tokens.json +39 -3
- package/dist/components/admin/index.d.ts +2 -0
- package/dist/components/admin/index.js +2 -0
- package/dist/components/data-entry/upload.js +12 -1
- package/dist/components/feedback/alert.d.ts +4 -0
- package/dist/components/feedback/alert.js +29 -8
- package/dist/components/feedback/auth-expiry.d.ts +30 -0
- package/dist/components/feedback/auth-expiry.js +64 -0
- package/dist/components/feedback/index.d.ts +2 -0
- package/dist/components/feedback/index.js +2 -0
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +57 -10
- package/dist/components/query/data-state.js +9 -0
- package/dist/components/query/index.d.ts +2 -0
- package/dist/components/query/index.js +2 -0
- package/dist/components/query/infinite-query-state.js +9 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +3 -0
- package/dist/i18n/messages/ja.json +3 -0
- package/dist/i18n/messages/vi.json +3 -0
- package/dist/props/components/feedback.prop.d.ts +13 -0
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/navigation.prop.d.ts +19 -1
- package/dist/props/registry.d.ts +11 -2
- package/dist/props/registry.js +13 -0
- package/dist/styles/alert-layout.css +16 -12
- package/dist/styles/control.css +18 -2
- package/dist/styles/data-entry-layout.css +4 -0
- package/dist/styles/form-layout.css +47 -5
- package/dist/styles/layers.json +35 -1
- package/dist/styles/layout.css +2 -1
- package/dist/tokens/components/feedback.css +2 -0
- package/dist/tokens/components/segmented.css +4 -0
- package/dist/tokens/semantic/layout.css +2 -0
- package/docs/FRAME-COVERAGE-REPORT.md +4 -2
- package/docs/data-entry/segmented.tsx +2 -0
- package/docs/feedback/auth-expiry.tsx +104 -0
- package/docs/i18n/messages/en.json +6 -0
- package/docs/i18n/messages/ja.json +6 -0
- package/docs/i18n/messages/vi.json +6 -0
- package/docs/navigation/tabs.tsx +43 -0
- package/docs/query/data-state.tsx +84 -12
- package/docs/showcase/table-view-tabs.tsx +15 -13
- package/docs/themes/flat.css +4 -0
- package/docs/themes/glassmorphism.css +2 -0
- package/docs/themes/neubrutalism.css +6 -0
- 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.
|
|
6
|
+
**This catalog describes `@godxjp/ui` 31.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.
|
|
@@ -47,7 +47,7 @@ Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `li
|
|
|
47
47
|
task is a task** — "build a settings page", "confirm a destructive delete", "a list page with
|
|
48
48
|
filters" — start HERE, not at the components. Then fetch `patterns/<name>.json` for complete,
|
|
49
49
|
copy-paste-ready code. A component index answers "does X exist"; it cannot answer "build Y".
|
|
50
|
-
1. `components-index.json` —
|
|
50
|
+
1. `components-index.json` — 47 KB, all 176 components as name + group +
|
|
51
51
|
tagline. Read this when you already know the SHAPE you need. Each entry may carry `absorbed`:
|
|
52
52
|
names that **do not exist** and map to it — `Combobox`, `Autocomplete`, `CountrySelect` and
|
|
53
53
|
`SearchSelect` are all `Select`. If you are about to hand-roll something, search this field
|
|
@@ -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` — 2080 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
|
+
1765 `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.
|
|
@@ -144,8 +144,8 @@ has stopped following the brand.
|
|
|
144
144
|
| `tier` | count | what it is | set it? |
|
|
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
|
-
| `semantic` |
|
|
148
|
-
| `component` |
|
|
147
|
+
| `semantic` | 104 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
|
|
148
|
+
| `component` | 1765 | 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.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { AuthExpiryProvider } from \"@godxjp/ui/query\";\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n return (\n <AuthExpiryProvider\n onAuthExpired={() => {\n const returnTo = encodeURIComponent(window.location.href);\n window.location.assign(`/auth/login?return_to=${returnTo}`);\n }}\n >\n {children}\n </AuthExpiryProvider>\n );\n}",
|
|
3
|
+
"group": "feedback",
|
|
4
|
+
"importPath": "@godxjp/ui/query",
|
|
5
|
+
"name": "AuthExpiryProvider",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Called ONCE per expiry — any number of simultaneous 401s share one call; re-armed after every auth-errored view has gone. Typically `window.location.assign(loginUrl(returnTo = location.href))`. Return a promise for a silent refresh (then invalidate queries); a throw/rejection brings back the sign-in alert, whose button calls this again.",
|
|
9
|
+
"name": "onAuthExpired",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "(context: { error: unknown }) => void | Promise<void>"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "The app.",
|
|
15
|
+
"name": "children",
|
|
16
|
+
"type": "ReactNode"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"related": [
|
|
20
|
+
"DataState / InfiniteQueryState / AlertMutationFeedback / AlertQueryError — the surfaces that consult this provider.",
|
|
21
|
+
"classifyQueryError — decides what is auth-class (401, or a status-less 'unauthenticated / access token invalid / token expired' message).",
|
|
22
|
+
"ErrorSurface — the whole-page error surface; a session expiry is neither a page error nor an inline one."
|
|
23
|
+
],
|
|
24
|
+
"rules": [],
|
|
25
|
+
"storyPath": "query/DataState.stories.tsx",
|
|
26
|
+
"tagline": "App-root handler for an expired session (401 / invalid or expired token): every DataState / InfiniteQueryState / AlertMutationFeedback / AlertQueryError calls it automatically, once, and shows a neutral pending state instead of an error alert. Also exported from @godxjp/ui.",
|
|
27
|
+
"usage": [
|
|
28
|
+
"DO: mount it ONCE near the root, inside the QueryClientProvider/AppProvider, and redirect to the IdP with the current URL as the return target. The IdP returns immediately while its own session is alive — the SSO norm (Google/Microsoft apps, OIDC SPA guidance): no page-level error for an expired session.",
|
|
29
|
+
"DO: expect the surfaces to keep their `skeleton` (DataState/InfiniteQueryState) or a small muted status line (AlertMutationFeedback/AlertQueryError) with `role=\"status\"` announcing 'redirecting to sign-in' — never a destructive alert.",
|
|
30
|
+
"DON'T: wire `onAuthError` on every DataState as the session strategy — that renders an alert and waits for a click. It is the no-provider fallback only.",
|
|
31
|
+
"DON'T: use it for 403 — forbidden is a permission state, not an expired session; it still renders the access-guidance alert.",
|
|
32
|
+
"DO: when there is no provider the 401 fallback is a neutral (tone=default, role=status) sign-in alert capped at `--query-auth-alert-max-inline-size` (36rem, `none` = full width) — backward compatible, but proportionate."
|
|
33
|
+
],
|
|
34
|
+
"useCases": [
|
|
35
|
+
"An SSO app (GoDX ID / OIDC) whose access token expired while the tab was idle: the first 401 redirects to the IdP and back to the same URL, no error panel flashes.",
|
|
36
|
+
"A dashboard with five DataStates that all 401 in the same tick: exactly one redirect.",
|
|
37
|
+
"A form submit (`AlertMutationFeedback`) that 401s: a muted 'redirecting to sign-in' line instead of a red alert.",
|
|
38
|
+
"A silent token refresh: return a promise that refreshes and then `queryClient.invalidateQueries()`; the views recover from skeleton without ever showing an error."
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"type": "boolean"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
|
-
"description": "
|
|
31
|
+
"description": "Per-instance sign-in button for 401 / expired-token errors when NO `AuthExpiryProvider` is mounted (clicked by the user, never auto-invoked). Prefer `AuthExpiryProvider onAuthExpired` at the app root, which handles every 401 automatically and once (gh#1022).",
|
|
32
32
|
"name": "onAuthError",
|
|
33
33
|
"type": "() => void"
|
|
34
34
|
},
|
|
@@ -64,6 +64,8 @@
|
|
|
64
64
|
"DO: provide `empty` + `isEmpty` together when the data can legitimately return 0 items — e.g. `isEmpty={(d) => d.items.length === 0}` paired with `empty={<EmptyState title=\"…\" />}`. Omitting `empty` means an empty array still falls through to `children`, silently rendering a blank table.",
|
|
65
65
|
"DON'T: wrap DataState in your own conditional — e.g. `{query.isSuccess && <DataState …>}`. DataState IS the conditional; the outer guard is redundant and breaks the retry/refetch skeleton.",
|
|
66
66
|
"DON'T: use DataState for `useInfiniteQuery` results. The `query` prop type is `UseQueryResult<T>`, not `UseInfiniteQueryResult`. Use `InfiniteQueryState` (from `@godxjp/ui/query`) instead, which accepts `flatten` and renders a load-more footer.",
|
|
67
|
+
"DO: mount `AuthExpiryProvider onAuthExpired={…}` once at the app root (SSO apps: redirect to the IdP with the current URL as return target). A 401 then never paints an alert: the handler runs automatically, once for any number of simultaneous 401s, and DataState keeps its `skeleton` with a polite live region announcing the redirect (gh#1022).",
|
|
68
|
+
"DON'T: rely on per-page `onAuthError` for session expiry — it is only a click-to-sign-in button in the fallback alert (no provider). An expired session is an app-wide concern, not a page error.",
|
|
67
69
|
"DO: classify errors by cause. Use session renewal/sign-in for 401, access guidance for 403, contextual correction for domain errors, and opt into showRetry only for transient network/5xx errors.",
|
|
68
70
|
"DO: pass prerequisite for enabled:false queries. Pending + fetchStatus idle is unstarted, not loading, and never renders a skeleton.",
|
|
69
71
|
"DO: rely on the localized, cause-specific error message — the raw backend/token/stack text is never shown. For a domain-specific message (e.g. a 422 field error) pass a custom errorRenderer.",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"DO: Import from `@godxjp/ui/query` (not `@godxjp/ui`). Use the bundled `flattenItemPages` helper for any API that returns `{ items: T[] }` pages — it handles `undefined` data safely. Custom page shapes require a custom `flatten` function.",
|
|
43
43
|
"DO: Always pass `skeleton` (e.g. `<SkeletonTable />` or `<SkeletonStat />`). It shows on initial `isPending`, on refetch-after-error, and whenever `data` is absent. Never show a blank area while loading.",
|
|
44
44
|
"DO: Pass `empty` (an `<EmptyState>` node) to handle the zero-results case — without it the children render-prop is called with an empty array and you get a silent blank screen. Provide a custom `isEmpty` only when `TFlat` is not an array.",
|
|
45
|
-
"DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; 401
|
|
45
|
+
"DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; a 401 is handled by the app-root `AuthExpiryProvider` (auto, once, skeleton + live region) or, without one, by the `onAuthError` sign-in button; raw backend/token text is never rendered.",
|
|
46
46
|
"DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Override only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
|
|
47
47
|
"DON'T: Use `InfiniteQueryState` for a `useQuery` result — it expects `UseInfiniteQueryResult` shape (`pages`, `hasNextPage`, `fetchNextPage`, `isFetchingNextPage`). For regular `useQuery` use `DataState` instead.",
|
|
48
48
|
"DON'T: Confuse the two generics: `TPage` is the raw page shape from the API, `TFlat` is what `flatten` returns (usually `TItem[]`). The `children` render-prop receives `TFlat`, not `TPage`. Pass `isEmpty` if `TFlat` is not a plain array so empty detection works correctly."
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"name": "onValueChange",
|
|
25
25
|
"type": "(value: string) => void"
|
|
26
26
|
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Strip-only mode (gh#1021): the tabs drive content OUTSIDE the strip. No tabpanel is rendered, and the selected tab's `aria-controls` points at this id, which is the consumer's own region (give it `role=\"region\"` and a name). Content there is never remounted on a tab switch, so a typed draft survives. With the compound form it applies to triggers without a declared `TabsContent` (a declared panel wins). A pure filter with NO region to point at is not a tab strip; use `Segmented`. The root's `aria-label`/`aria-labelledby` name the `role=\"tablist\"` (gh#1020), and a compound `TabsTrigger` takes `count`/`overflowCount`/`showZero`/`countLabel`, drawn exactly like `items[].count`.",
|
|
29
|
+
"name": "controls",
|
|
30
|
+
"type": "IdProp"
|
|
31
|
+
},
|
|
27
32
|
{
|
|
28
33
|
"defaultValue": "\"default\"",
|
|
29
34
|
"description": "Trigger-strip appearance — this is Ant Design's `type` under the library's own `variant` vocabulary. `default` is the pill strip (antd has no equivalent). `line` is UNDERLINE-ONLY: the selected trigger gets no ring or card border at all — only the token-owned 2px primary bar (--tabs-indicator-{background,size,offset}) — so the `:focus-visible` keyboard ring stays visible and clearly distinct from selection. `card` gives each tab a boxed face on a rail (--tabs-card-*). `editable-card` is `card` plus the add button and per-tab remove shortcut, and needs `onEdit` to do anything. With `items`, the variant is forwarded to the list; when composing manually, pass the same value to `<TabsList variant=\"line\">`.",
|
|
@@ -321,6 +321,11 @@
|
|
|
321
321
|
"name": "DataState",
|
|
322
322
|
"tagline": "TanStack Query lifecycle widget — skeleton / error / empty / success for one useQuery block. Import from @godxjp/ui/query."
|
|
323
323
|
},
|
|
324
|
+
{
|
|
325
|
+
"group": "feedback",
|
|
326
|
+
"name": "AuthExpiryProvider",
|
|
327
|
+
"tagline": "App-root handler for an expired session (401 / invalid or expired token): every DataState / InfiniteQueryState / AlertMutationFeedback / AlertQueryError calls it automatically, once, and shows a neutral pending state instead of an error alert. Also exported from @godxjp/ui."
|
|
328
|
+
},
|
|
324
329
|
{
|
|
325
330
|
"group": "data-display",
|
|
326
331
|
"name": "InfiniteQueryState",
|
package/agent/components.json
CHANGED
|
@@ -5220,7 +5220,7 @@
|
|
|
5220
5220
|
"type": "boolean"
|
|
5221
5221
|
},
|
|
5222
5222
|
{
|
|
5223
|
-
"description": "
|
|
5223
|
+
"description": "Per-instance sign-in button for 401 / expired-token errors when NO `AuthExpiryProvider` is mounted (clicked by the user, never auto-invoked). Prefer `AuthExpiryProvider onAuthExpired` at the app root, which handles every 401 automatically and once (gh#1022).",
|
|
5224
5224
|
"name": "onAuthError",
|
|
5225
5225
|
"type": "() => void"
|
|
5226
5226
|
},
|
|
@@ -5256,6 +5256,8 @@
|
|
|
5256
5256
|
"DO: provide `empty` + `isEmpty` together when the data can legitimately return 0 items — e.g. `isEmpty={(d) => d.items.length === 0}` paired with `empty={<EmptyState title=\"…\" />}`. Omitting `empty` means an empty array still falls through to `children`, silently rendering a blank table.",
|
|
5257
5257
|
"DON'T: wrap DataState in your own conditional — e.g. `{query.isSuccess && <DataState …>}`. DataState IS the conditional; the outer guard is redundant and breaks the retry/refetch skeleton.",
|
|
5258
5258
|
"DON'T: use DataState for `useInfiniteQuery` results. The `query` prop type is `UseQueryResult<T>`, not `UseInfiniteQueryResult`. Use `InfiniteQueryState` (from `@godxjp/ui/query`) instead, which accepts `flatten` and renders a load-more footer.",
|
|
5259
|
+
"DO: mount `AuthExpiryProvider onAuthExpired={…}` once at the app root (SSO apps: redirect to the IdP with the current URL as return target). A 401 then never paints an alert: the handler runs automatically, once for any number of simultaneous 401s, and DataState keeps its `skeleton` with a polite live region announcing the redirect (gh#1022).",
|
|
5260
|
+
"DON'T: rely on per-page `onAuthError` for session expiry — it is only a click-to-sign-in button in the fallback alert (no provider). An expired session is an app-wide concern, not a page error.",
|
|
5259
5261
|
"DO: classify errors by cause. Use session renewal/sign-in for 401, access guidance for 403, contextual correction for domain errors, and opt into showRetry only for transient network/5xx errors.",
|
|
5260
5262
|
"DO: pass prerequisite for enabled:false queries. Pending + fetchStatus idle is unstarted, not loading, and never renders a skeleton.",
|
|
5261
5263
|
"DO: rely on the localized, cause-specific error message — the raw backend/token/stack text is never shown. For a domain-specific message (e.g. a 422 field error) pass a custom errorRenderer.",
|
|
@@ -5269,6 +5271,46 @@
|
|
|
5269
5271
|
"Any page using `useQuery` where the empty state and loading state are visually different — DataState enforces the correct visual for each phase without scattered `if` statements across the component tree."
|
|
5270
5272
|
]
|
|
5271
5273
|
},
|
|
5274
|
+
{
|
|
5275
|
+
"example": "import { AuthExpiryProvider } from \"@godxjp/ui/query\";\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n return (\n <AuthExpiryProvider\n onAuthExpired={() => {\n const returnTo = encodeURIComponent(window.location.href);\n window.location.assign(`/auth/login?return_to=${returnTo}`);\n }}\n >\n {children}\n </AuthExpiryProvider>\n );\n}",
|
|
5276
|
+
"group": "feedback",
|
|
5277
|
+
"importPath": "@godxjp/ui/query",
|
|
5278
|
+
"name": "AuthExpiryProvider",
|
|
5279
|
+
"props": [
|
|
5280
|
+
{
|
|
5281
|
+
"description": "Called ONCE per expiry — any number of simultaneous 401s share one call; re-armed after every auth-errored view has gone. Typically `window.location.assign(loginUrl(returnTo = location.href))`. Return a promise for a silent refresh (then invalidate queries); a throw/rejection brings back the sign-in alert, whose button calls this again.",
|
|
5282
|
+
"name": "onAuthExpired",
|
|
5283
|
+
"required": true,
|
|
5284
|
+
"type": "(context: { error: unknown }) => void | Promise<void>"
|
|
5285
|
+
},
|
|
5286
|
+
{
|
|
5287
|
+
"description": "The app.",
|
|
5288
|
+
"name": "children",
|
|
5289
|
+
"type": "ReactNode"
|
|
5290
|
+
}
|
|
5291
|
+
],
|
|
5292
|
+
"related": [
|
|
5293
|
+
"DataState / InfiniteQueryState / AlertMutationFeedback / AlertQueryError — the surfaces that consult this provider.",
|
|
5294
|
+
"classifyQueryError — decides what is auth-class (401, or a status-less 'unauthenticated / access token invalid / token expired' message).",
|
|
5295
|
+
"ErrorSurface — the whole-page error surface; a session expiry is neither a page error nor an inline one."
|
|
5296
|
+
],
|
|
5297
|
+
"rules": [],
|
|
5298
|
+
"storyPath": "query/DataState.stories.tsx",
|
|
5299
|
+
"tagline": "App-root handler for an expired session (401 / invalid or expired token): every DataState / InfiniteQueryState / AlertMutationFeedback / AlertQueryError calls it automatically, once, and shows a neutral pending state instead of an error alert. Also exported from @godxjp/ui.",
|
|
5300
|
+
"usage": [
|
|
5301
|
+
"DO: mount it ONCE near the root, inside the QueryClientProvider/AppProvider, and redirect to the IdP with the current URL as the return target. The IdP returns immediately while its own session is alive — the SSO norm (Google/Microsoft apps, OIDC SPA guidance): no page-level error for an expired session.",
|
|
5302
|
+
"DO: expect the surfaces to keep their `skeleton` (DataState/InfiniteQueryState) or a small muted status line (AlertMutationFeedback/AlertQueryError) with `role=\"status\"` announcing 'redirecting to sign-in' — never a destructive alert.",
|
|
5303
|
+
"DON'T: wire `onAuthError` on every DataState as the session strategy — that renders an alert and waits for a click. It is the no-provider fallback only.",
|
|
5304
|
+
"DON'T: use it for 403 — forbidden is a permission state, not an expired session; it still renders the access-guidance alert.",
|
|
5305
|
+
"DO: when there is no provider the 401 fallback is a neutral (tone=default, role=status) sign-in alert capped at `--query-auth-alert-max-inline-size` (36rem, `none` = full width) — backward compatible, but proportionate."
|
|
5306
|
+
],
|
|
5307
|
+
"useCases": [
|
|
5308
|
+
"An SSO app (GoDX ID / OIDC) whose access token expired while the tab was idle: the first 401 redirects to the IdP and back to the same URL, no error panel flashes.",
|
|
5309
|
+
"A dashboard with five DataStates that all 401 in the same tick: exactly one redirect.",
|
|
5310
|
+
"A form submit (`AlertMutationFeedback`) that 401s: a muted 'redirecting to sign-in' line instead of a red alert.",
|
|
5311
|
+
"A silent token refresh: return a promise that refreshes and then `queryClient.invalidateQueries()`; the views recover from skeleton without ever showing an error."
|
|
5312
|
+
]
|
|
5313
|
+
},
|
|
5272
5314
|
{
|
|
5273
5315
|
"example": "import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { InfiniteQueryState, flattenItemPages } from \"@godxjp/ui/query\";\n\ntype Activity = { id: string; label: string };\n\n// `flattenItemPages` constrains the page to `{ items: TItem[] }`, so the query must be typed:\n// an untyped one makes the page `unknown`, which cannot satisfy that constraint.\nconst q = useInfiniteQuery<{ items: Activity[]; cursor?: string }>({\n queryKey: [\"activity\"],\n queryFn: fetchActivityPage,\n initialPageParam: undefined,\n getNextPageParam: (last) => last.cursor,\n});\n\n<InfiniteQueryState query={q} skeleton={<SkeletonRows />} flatten={flattenItemPages} isEmpty={(it) => it.length === 0}>\n {(items) => items.map((a) => <ActivityRow key={a.id} activity={a} />)}\n</InfiniteQueryState>",
|
|
5274
5316
|
"group": "data-display",
|
|
@@ -5313,7 +5355,7 @@
|
|
|
5313
5355
|
"DO: Import from `@godxjp/ui/query` (not `@godxjp/ui`). Use the bundled `flattenItemPages` helper for any API that returns `{ items: T[] }` pages — it handles `undefined` data safely. Custom page shapes require a custom `flatten` function.",
|
|
5314
5356
|
"DO: Always pass `skeleton` (e.g. `<SkeletonTable />` or `<SkeletonStat />`). It shows on initial `isPending`, on refetch-after-error, and whenever `data` is absent. Never show a blank area while loading.",
|
|
5315
5357
|
"DO: Pass `empty` (an `<EmptyState>` node) to handle the zero-results case — without it the children render-prop is called with an empty array and you get a silent blank screen. Provide a custom `isEmpty` only when `TFlat` is not an array.",
|
|
5316
|
-
"DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; 401
|
|
5358
|
+
"DO: Let errors remain cause-aware. Retry is automatic only for classified transient/network/5xx failures. Unknown errors do not get a blind retry unless `showRetry` or `onRetry` is explicitly supplied; a 401 is handled by the app-root `AuthExpiryProvider` (auto, once, skeleton + live region) or, without one, by the `onAuthError` sign-in button; raw backend/token text is never rendered.",
|
|
5317
5359
|
"DON'T: Hand-roll a load-more button. The component renders a default centered outline Button when `hasNextPage` is true. Override only via `loadMore` (custom node) or `showLoadMore={false}` (hide entirely). Never call `query.fetchNextPage()` outside the component for pagination.",
|
|
5318
5360
|
"DON'T: Use `InfiniteQueryState` for a `useQuery` result — it expects `UseInfiniteQueryResult` shape (`pages`, `hasNextPage`, `fetchNextPage`, `isFetchingNextPage`). For regular `useQuery` use `DataState` instead.",
|
|
5319
5361
|
"DON'T: Confuse the two generics: `TPage` is the raw page shape from the API, `TFlat` is what `flatten` returns (usually `TItem[]`). The `children` render-prop receives `TFlat`, not `TPage`. Pass `isEmpty` if `TFlat` is not a plain array so empty detection works correctly."
|
|
@@ -7838,6 +7880,11 @@
|
|
|
7838
7880
|
"name": "onValueChange",
|
|
7839
7881
|
"type": "(value: string) => void"
|
|
7840
7882
|
},
|
|
7883
|
+
{
|
|
7884
|
+
"description": "Strip-only mode (gh#1021): the tabs drive content OUTSIDE the strip. No tabpanel is rendered, and the selected tab's `aria-controls` points at this id, which is the consumer's own region (give it `role=\"region\"` and a name). Content there is never remounted on a tab switch, so a typed draft survives. With the compound form it applies to triggers without a declared `TabsContent` (a declared panel wins). A pure filter with NO region to point at is not a tab strip; use `Segmented`. The root's `aria-label`/`aria-labelledby` name the `role=\"tablist\"` (gh#1020), and a compound `TabsTrigger` takes `count`/`overflowCount`/`showZero`/`countLabel`, drawn exactly like `items[].count`.",
|
|
7885
|
+
"name": "controls",
|
|
7886
|
+
"type": "IdProp"
|
|
7887
|
+
},
|
|
7841
7888
|
{
|
|
7842
7889
|
"defaultValue": "\"default\"",
|
|
7843
7890
|
"description": "Trigger-strip appearance — this is Ant Design's `type` under the library's own `variant` vocabulary. `default` is the pill strip (antd has no equivalent). `line` is UNDERLINE-ONLY: the selected trigger gets no ring or card border at all — only the token-owned 2px primary bar (--tabs-indicator-{background,size,offset}) — so the `:focus-visible` keyboard ring stays visible and clearly distinct from selection. `card` gives each tab a boxed face on a rail (--tabs-card-*). `editable-card` is `card` plus the add button and per-tab remove shortcut, and needs `onEdit` to do anything. With `items`, the variant is forwarded to the list; when composing manually, pass the same value to `<TabsList variant=\"line\">`.",
|
package/agent/index.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"counts": {
|
|
3
3
|
"anti-ai-tells": 26,
|
|
4
|
-
"components":
|
|
4
|
+
"components": 176,
|
|
5
5
|
"patterns": 21,
|
|
6
6
|
"rules": 50,
|
|
7
|
-
"tokens":
|
|
7
|
+
"tokens": 2080,
|
|
8
8
|
"vocabulary": 14
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"file": "components-index.json",
|
|
13
|
-
"note": "
|
|
13
|
+
"note": "47 KB — name + group + tagline for all 176. FETCH THIS FIRST, then fetch only the components you chose.",
|
|
14
14
|
"url": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
@@ -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.
|
|
51
|
+
"pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.4.0/agent/index.json"
|
|
52
52
|
},
|
|
53
53
|
"source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
|
|
54
54
|
"start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
|
|
55
55
|
"tokenTiers": {
|
|
56
56
|
"component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
|
|
57
57
|
"counts": {
|
|
58
|
-
"component":
|
|
58
|
+
"component": 1765,
|
|
59
59
|
"foundation": 211,
|
|
60
|
-
"semantic":
|
|
60
|
+
"semantic": 104
|
|
61
61
|
},
|
|
62
62
|
"foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
|
|
63
63
|
"semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
|
|
64
64
|
},
|
|
65
|
-
"version": "31.
|
|
65
|
+
"version": "31.4.0"
|
|
66
66
|
}
|
package/agent/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @godxjp/ui
|
|
2
2
|
|
|
3
|
-
> A Japanese-enterprise React design system:
|
|
4
|
-
> 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.
|
|
3
|
+
> A Japanese-enterprise React design system: 176 components, 2080 design tokens,
|
|
4
|
+
> 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.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@31.
|
|
7
|
+
(`npx @godxjp/ui-mcp@31.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,10 +15,10 @@ that can only fetch URLs.
|
|
|
15
15
|
## Catalog
|
|
16
16
|
|
|
17
17
|
- [patterns-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/patterns-index.json): 21 whole-task patterns (name, tagline, tags). Start here when the task is a TASK — "build a settings page" — then fetch `patterns/<name>.json` for complete code.
|
|
18
|
-
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json):
|
|
18
|
+
- [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 47 KB — all 176 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–35 KB, median 6 KB). Read the index, then fetch only the ones you chose — this is the selective route, and the reason you do not need the blob.
|
|
20
20
|
- [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.2 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
|
|
21
|
-
- [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand),
|
|
21
|
+
- [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 104 `semantic` roles, 1765 `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.
|
|
29
|
+
the tag: `.../godx-jp/godxjp-ui/v31.4.0/agent/...`. A catalog that does not match the installed
|
|
30
30
|
package describes props that are absent, or hides props that are present, and says nothing either way.
|
|
31
31
|
|
|
32
32
|
Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"data-state",
|
|
5
5
|
"query-states"
|
|
6
6
|
],
|
|
7
|
-
"code": "import { DataState, classifyQueryError } from \"@godxjp/ui/query\";\nimport { EmptyState } from \"@godxjp/ui/data-display\";\nimport { SkeletonTable } from \"@godxjp/ui/feedback\";\nimport { Building2, Inbox } from \"lucide-react\";\n\n// ONE primary state renders at a time; DataState makes them mutually exclusive:\n// prerequisite → skeleton(loading) → data → empty → error (never two at once).\n//\n// enabled:false is PREREQUISITE/idle, NOT loading. TanStack reports isPending while a query is\n// disabled, but fetchStatus stays \"idle\" (no request in flight) — DataState renders the prerequisite\n// slot, so a disabled query shows an instruction, NOT an endless skeleton.\n// const query = useQuery({ queryKey: [\"members\", orgId], queryFn, enabled: Boolean(orgId) });\n//\n// A background refetch over EXISTING data keeps the content on screen (no skeleton flash) and\n// announces the busy state politely — stale/placeholder refresh is handled for you.\n//\n// Errors are classified by CAUSE, not blanket-retried:\n// auth (401) →
|
|
7
|
+
"code": "import { DataState, classifyQueryError } from \"@godxjp/ui/query\";\nimport { EmptyState } from \"@godxjp/ui/data-display\";\nimport { SkeletonTable } from \"@godxjp/ui/feedback\";\nimport { Building2, Inbox } from \"lucide-react\";\n\n// ONE primary state renders at a time; DataState makes them mutually exclusive:\n// prerequisite → skeleton(loading) → data → empty → error (never two at once).\n//\n// enabled:false is PREREQUISITE/idle, NOT loading. TanStack reports isPending while a query is\n// disabled, but fetchStatus stays \"idle\" (no request in flight) — DataState renders the prerequisite\n// slot, so a disabled query shows an instruction, NOT an endless skeleton.\n// const query = useQuery({ queryKey: [\"members\", orgId], queryFn, enabled: Boolean(orgId) });\n//\n// A background refetch over EXISTING data keeps the content on screen (no skeleton flash) and\n// announces the busy state politely — stale/placeholder refresh is handled for you.\n//\n// Errors are classified by CAUSE, not blanket-retried:\n// auth (401) → handled app-wide by <AuthExpiryProvider onAuthExpired> at the root:\n// auto-redirect to sign-in ONCE, skeleton stays (NOT a retry, NOT an alert)\n// forbidden (403) → permission message + access path (no retry)\n// notFound (404) → contextual not-found (no retry)\n// validation (400/422)→ corrective guidance (no retry)\n// transient (408/429/5xx/network) → Retry offered automatically\n// unknown → neutral; opt into Retry via showRetry/onRetry only if it can help\n// App root (once): <AuthExpiryProvider onAuthExpired={() => redirectToSignIn(location.href)}>\nexport function MembersPanel({ query, orgId }: { query: any; orgId?: string }) {\n return (\n <DataState\n query={query}\n prerequisite={<EmptyState icon={Building2} variant=\"section\" title=\"組織を選択してください\"\n description=\"メンバーを表示するには、上のセレクタで組織を選びます。\" />}\n skeleton={<SkeletonTable rows={8} columns={4} />}\n empty={<EmptyState icon={Inbox} variant=\"section\" title=\"メンバーがいません\"\n description=\"この組織にはまだメンバーが登録されていません。\" />}\n isEmpty={(data) => data.items.length === 0}\n >\n {(data) => <MemberTable items={data.items} />}\n </DataState>\n );\n}\n\n// Need a bespoke error surface? Pass errorRenderer and branch on the classified category — the\n// default detail is always a localized message (never raw token / endpoint / stack text):\n// errorRenderer={(error, retry) => {\n// const { category } = classifyQueryError(error);\n// if (category === \"forbidden\") return <NoAccess />;\n// if (category === \"transient\") return <Retryable onRetry={retry} />;\n// return <GenericError />;\n// }}\n// RULE: pagination/footer chrome NEVER renders outside the populated-data branch (see data-table-page).",
|
|
8
8
|
"name": "async-data-state",
|
|
9
9
|
"tagline": "The full async state machine — ONE primary state at a time: prerequisite, disabled-vs-loading, stale refresh, populated, real-empty, and cause-aware error (401/403/404/422/transient) with correct recovery.",
|
|
10
10
|
"tags": [
|
package/agent/patterns.json
CHANGED
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"data-state",
|
|
183
183
|
"query-states"
|
|
184
184
|
],
|
|
185
|
-
"code": "import { DataState, classifyQueryError } from \"@godxjp/ui/query\";\nimport { EmptyState } from \"@godxjp/ui/data-display\";\nimport { SkeletonTable } from \"@godxjp/ui/feedback\";\nimport { Building2, Inbox } from \"lucide-react\";\n\n// ONE primary state renders at a time; DataState makes them mutually exclusive:\n// prerequisite → skeleton(loading) → data → empty → error (never two at once).\n//\n// enabled:false is PREREQUISITE/idle, NOT loading. TanStack reports isPending while a query is\n// disabled, but fetchStatus stays \"idle\" (no request in flight) — DataState renders the prerequisite\n// slot, so a disabled query shows an instruction, NOT an endless skeleton.\n// const query = useQuery({ queryKey: [\"members\", orgId], queryFn, enabled: Boolean(orgId) });\n//\n// A background refetch over EXISTING data keeps the content on screen (no skeleton flash) and\n// announces the busy state politely — stale/placeholder refresh is handled for you.\n//\n// Errors are classified by CAUSE, not blanket-retried:\n// auth (401) →
|
|
185
|
+
"code": "import { DataState, classifyQueryError } from \"@godxjp/ui/query\";\nimport { EmptyState } from \"@godxjp/ui/data-display\";\nimport { SkeletonTable } from \"@godxjp/ui/feedback\";\nimport { Building2, Inbox } from \"lucide-react\";\n\n// ONE primary state renders at a time; DataState makes them mutually exclusive:\n// prerequisite → skeleton(loading) → data → empty → error (never two at once).\n//\n// enabled:false is PREREQUISITE/idle, NOT loading. TanStack reports isPending while a query is\n// disabled, but fetchStatus stays \"idle\" (no request in flight) — DataState renders the prerequisite\n// slot, so a disabled query shows an instruction, NOT an endless skeleton.\n// const query = useQuery({ queryKey: [\"members\", orgId], queryFn, enabled: Boolean(orgId) });\n//\n// A background refetch over EXISTING data keeps the content on screen (no skeleton flash) and\n// announces the busy state politely — stale/placeholder refresh is handled for you.\n//\n// Errors are classified by CAUSE, not blanket-retried:\n// auth (401) → handled app-wide by <AuthExpiryProvider onAuthExpired> at the root:\n// auto-redirect to sign-in ONCE, skeleton stays (NOT a retry, NOT an alert)\n// forbidden (403) → permission message + access path (no retry)\n// notFound (404) → contextual not-found (no retry)\n// validation (400/422)→ corrective guidance (no retry)\n// transient (408/429/5xx/network) → Retry offered automatically\n// unknown → neutral; opt into Retry via showRetry/onRetry only if it can help\n// App root (once): <AuthExpiryProvider onAuthExpired={() => redirectToSignIn(location.href)}>\nexport function MembersPanel({ query, orgId }: { query: any; orgId?: string }) {\n return (\n <DataState\n query={query}\n prerequisite={<EmptyState icon={Building2} variant=\"section\" title=\"組織を選択してください\"\n description=\"メンバーを表示するには、上のセレクタで組織を選びます。\" />}\n skeleton={<SkeletonTable rows={8} columns={4} />}\n empty={<EmptyState icon={Inbox} variant=\"section\" title=\"メンバーがいません\"\n description=\"この組織にはまだメンバーが登録されていません。\" />}\n isEmpty={(data) => data.items.length === 0}\n >\n {(data) => <MemberTable items={data.items} />}\n </DataState>\n );\n}\n\n// Need a bespoke error surface? Pass errorRenderer and branch on the classified category — the\n// default detail is always a localized message (never raw token / endpoint / stack text):\n// errorRenderer={(error, retry) => {\n// const { category } = classifyQueryError(error);\n// if (category === \"forbidden\") return <NoAccess />;\n// if (category === \"transient\") return <Retryable onRetry={retry} />;\n// return <GenericError />;\n// }}\n// RULE: pagination/footer chrome NEVER renders outside the populated-data branch (see data-table-page).",
|
|
186
186
|
"name": "async-data-state",
|
|
187
187
|
"tagline": "The full async state machine — ONE primary state at a time: prerequisite, disabled-vs-loading, stale refresh, populated, real-empty, and cause-aware error (401/403/404/422/transient) with correct recovery.",
|
|
188
188
|
"tags": [
|
package/agent/tokens.json
CHANGED
|
@@ -1625,6 +1625,12 @@
|
|
|
1625
1625
|
"tier": "semantic",
|
|
1626
1626
|
"value": "none"
|
|
1627
1627
|
},
|
|
1628
|
+
{
|
|
1629
|
+
"description": "The widest share of the header row the actions (`extra`) keep on ONE row before they wrap (gh#1025); the title keeps the rest. `initial` — default 60% at the call site.",
|
|
1630
|
+
"name": "--page-header-extra-max-measure",
|
|
1631
|
+
"tier": "semantic",
|
|
1632
|
+
"value": "initial"
|
|
1633
|
+
},
|
|
1628
1634
|
{
|
|
1629
1635
|
"description": "PageContainer `toolbar` — the fixed chrome band between the header and the (scrolling) body. ALL THREE knobs are quiet by default (rule #44): background — `transparent`, i.e. the band shows the page ground exactly as it did before the knob existed. It is bound at `:root` and NOT `initial`, unlike the divider below, because the default is a plain CSS keyword rather than another role token: there is nothing for a scoped [data-tenant]/.dark override to re-resolve, so the `initial` + call-site-fallback dance would buy nothing and only hide the default from anyone reading this file. A service that wants the band to read as its own surface — the design that asked for this knob puts a chat channel's workflow rail on the card ground — sets `--page-toolbar-background: hsl(var(--card));` ONCE in its theme. Never `className=\"bg-card\"` at the call site: that is hand-laid page chrome, it paints only under `variant=\"flush\"` gutters correctly by accident, and it cannot be re-themed per tenant. pad-block 0 — the band's ONLY breathing room, and the only one it should ever have. The band is chrome, so it sits FLUSH against the header above and the body below (src/styles/layout.css cancels the container's --page-band-gap from the band itself); there is deliberately no outside space to tune, because a ruled, painted band floating in a void divides nothing. A service that opts into the background or the divider gives the band its inset HERE (`--page-toolbar-pad-block: var(--space-2)`) instead of padding the strip at the call site. Still 0 by default: a transparent band is not a surface and has no inside to breathe, and under `fill` every pixel of it comes straight off the scroll viewport the slot exists to protect. divider — declared `initial` so the CALL SITE can fall back to --page-header-divider (`var(--page-toolbar-divider, var(--page-header-divider))`). One theme declaration therefore rules the whole page chrome consistently, and a scoped [data-tenant]/.dark override of the header divider still reaches the band — a `:root` binding would freeze it at the :root value (docs/TOKENS.md · \"Role-mirror knobs MUST be `initial`\"). Set `--page-toolbar-divider: none` to silence just this band.",
|
|
1630
1636
|
"name": "--page-toolbar-background",
|
|
@@ -6983,6 +6989,12 @@
|
|
|
6983
6989
|
"tier": "component",
|
|
6984
6990
|
"value": "var(--space-stack-sm)"
|
|
6985
6991
|
},
|
|
6992
|
+
{
|
|
6993
|
+
"description": "The sign-in fallback a query surface paints for an expired session when no AuthExpiryProvider handles it (gh#1022). An expired session is an expected, recoverable condition, so it is capped at a reading measure instead of spanning the page body its DataState wraps. `none` restores the full-width alert.",
|
|
6994
|
+
"name": "--query-auth-alert-max-inline-size",
|
|
6995
|
+
"tier": "component",
|
|
6996
|
+
"value": "36rem"
|
|
6997
|
+
},
|
|
6986
6998
|
{
|
|
6987
6999
|
"description": "TOOLTIP — the transient label surface. Every constant here was a Tailwind literal baked into the component (`max-w-xs px-2 py-1 rounded-md text-xs shadow-md`), so a service could not retune tooltip density or measure without forking the component (rule #45). Defaults reproduce the previous look exactly, so adopting this changes nothing until a theme opts in.",
|
|
6988
7000
|
"name": "--tooltip-max-width",
|
|
@@ -9378,7 +9390,7 @@
|
|
|
9378
9390
|
"value": "initial"
|
|
9379
9391
|
},
|
|
9380
9392
|
{
|
|
9381
|
-
"description": "Counter pill beside the label (gh#602). Geometry matches Button/Toggle count pills;
|
|
9393
|
+
"description": "Counter pill beside the label (gh#602). Geometry matches Button/Toggle count pills; COLOUR follows `Tabs` (gh#1019): resting = `--background` / `--muted-foreground`, byte-identical to a resting `.ui-tabs-count` on a CARD tab — the Tabs rule for a pill whose host surface is `--muted`, which the Segmented track is (a `--muted` pill there is 1.00:1, shape gone). The selected segment's pill = `--primary` / `--primary-foreground`, identical to the active tab's. gh#602 had painted EVERY pill primary — four identical blobs that dropped the one thing a count pill in a one-of-N control can say, \"this is the chosen one\". The resting number is held to SC 1.4.3 (≥ 4.5:1 in both themes, measured in segmented-count-tabs-convention-1019.test.ts); it is not the selection indicator, so its fill only has to stand off the track, not reach 3:1. antd has no `count` on SegmentedItemType; this knob exists because every faceted filter in the wild needs one and nesting Badge in `label` was the only API until now.",
|
|
9382
9394
|
"name": "--segmented-count-min-width",
|
|
9383
9395
|
"tier": "component",
|
|
9384
9396
|
"value": "var(--button-count-min-width)"
|
|
@@ -9408,17 +9420,41 @@
|
|
|
9408
9420
|
"value": "var(--space-1)"
|
|
9409
9421
|
},
|
|
9410
9422
|
{
|
|
9411
|
-
"description": "
|
|
9423
|
+
"description": "default = hsl(var(--background)) at the call site",
|
|
9412
9424
|
"name": "--segmented-count-background",
|
|
9413
9425
|
"tier": "component",
|
|
9414
9426
|
"value": "initial"
|
|
9415
9427
|
},
|
|
9416
9428
|
{
|
|
9417
|
-
"description": "
|
|
9429
|
+
"description": "default = hsl(var(--muted-foreground)) at the call site",
|
|
9418
9430
|
"name": "--segmented-count-color",
|
|
9419
9431
|
"tier": "component",
|
|
9420
9432
|
"value": "initial"
|
|
9421
9433
|
},
|
|
9434
|
+
{
|
|
9435
|
+
"description": "default = hsl(var(--primary)) at the call site",
|
|
9436
|
+
"name": "--segmented-count-selected-background",
|
|
9437
|
+
"tier": "component",
|
|
9438
|
+
"value": "initial"
|
|
9439
|
+
},
|
|
9440
|
+
{
|
|
9441
|
+
"description": "default = 100% at the call site",
|
|
9442
|
+
"name": "--segmented-count-selected-background-alpha",
|
|
9443
|
+
"tier": "component",
|
|
9444
|
+
"value": "initial"
|
|
9445
|
+
},
|
|
9446
|
+
{
|
|
9447
|
+
"description": "default = hsl(var(--primary-foreground)) at the call site",
|
|
9448
|
+
"name": "--segmented-count-selected-color",
|
|
9449
|
+
"tier": "component",
|
|
9450
|
+
"value": "initial"
|
|
9451
|
+
},
|
|
9452
|
+
{
|
|
9453
|
+
"description": "default = var(--stroke-hairline) at the call site",
|
|
9454
|
+
"name": "--segmented-count-forced-outline-width",
|
|
9455
|
+
"tier": "component",
|
|
9456
|
+
"value": "initial"
|
|
9457
|
+
},
|
|
9422
9458
|
{
|
|
9423
9459
|
"description": "Rule weight, both orientations and both halves of a labelled rule. default = var(--stroke-hairline), resolved at the call site (gh#906)",
|
|
9424
9460
|
"name": "--separator-rule-size",
|
|
@@ -10,6 +10,8 @@ export { Field } from "../data-entry/field.js";
|
|
|
10
10
|
export { Descriptions } from "../data-display/descriptions.js";
|
|
11
11
|
export { SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat } from "../feedback/skeleton.js";
|
|
12
12
|
export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, } from "../feedback/alert.js";
|
|
13
|
+
export { AuthExpiryProvider } from "../feedback/auth-expiry.js";
|
|
14
|
+
export type { AuthExpiryProviderProps } from "../feedback/auth-expiry.js";
|
|
13
15
|
export { SearchInput } from "../data-entry/search-input.js";
|
|
14
16
|
export { Upload, collectUploadCommitActions, createUploadItem, useUploadDraft, } from "../data-entry/upload.js";
|
|
15
17
|
export { Cascader } from "../data-entry/cascader.js";
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
AlertActions,
|
|
15
15
|
AlertQueryError
|
|
16
16
|
} from "../feedback/alert.js";
|
|
17
|
+
import { AuthExpiryProvider } from "../feedback/auth-expiry.js";
|
|
17
18
|
import { SearchInput } from "../data-entry/search-input.js";
|
|
18
19
|
import {
|
|
19
20
|
Upload,
|
|
@@ -48,6 +49,7 @@ export {
|
|
|
48
49
|
AlertDialog,
|
|
49
50
|
AlertQueryError,
|
|
50
51
|
AlertTitle,
|
|
52
|
+
AuthExpiryProvider,
|
|
51
53
|
Badge,
|
|
52
54
|
Cascader,
|
|
53
55
|
Descriptions,
|
|
@@ -497,6 +497,7 @@ function Upload({
|
|
|
497
497
|
disabled,
|
|
498
498
|
onClick: openPicker,
|
|
499
499
|
"aria-label": triggerAriaLabel,
|
|
500
|
+
className: "ui-upload-trigger",
|
|
500
501
|
children: [
|
|
501
502
|
/* @__PURE__ */ jsx(
|
|
502
503
|
TriggerIcon,
|
|
@@ -553,7 +554,17 @@ function Upload({
|
|
|
553
554
|
hiddenInput,
|
|
554
555
|
liveRegion,
|
|
555
556
|
rejection && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-error-strong", children: rejection }),
|
|
556
|
-
/* @__PURE__ */ jsx(
|
|
557
|
+
/* @__PURE__ */ jsx(
|
|
558
|
+
Button,
|
|
559
|
+
{
|
|
560
|
+
type: "button",
|
|
561
|
+
disabled,
|
|
562
|
+
variant: triggerVariant,
|
|
563
|
+
onClick: openPicker,
|
|
564
|
+
className: "ui-upload-trigger",
|
|
565
|
+
children: children ?? t("dataEntry.upload.addImage")
|
|
566
|
+
}
|
|
567
|
+
),
|
|
557
568
|
list
|
|
558
569
|
] });
|
|
559
570
|
}
|
|
@@ -35,6 +35,10 @@ export declare const AlertActions: React.ForwardRefExoticComponent<React.HTMLAtt
|
|
|
35
35
|
* offer Retry (`onRetry`); permission/not-found/validation offer neither by default.
|
|
36
36
|
* - **Legacy mode** (no `category`, e.g. mutation/infinite feedback): shows the cleaned domain
|
|
37
37
|
* message (`humanError`) + optional Retry — form-submit corrective guidance stays visible.
|
|
38
|
+
* - **Under an `AuthExpiryProvider`** an auth-class error (either mode) is never painted as an
|
|
39
|
+
* alert: the provider's `onAuthExpired` runs once and this renders a small polite status
|
|
40
|
+
* ("redirecting to sign-in") instead. If the handler fails, the sign-in alert comes back with its
|
|
41
|
+
* button wired to the provider (gh#1022).
|
|
38
42
|
*/
|
|
39
43
|
export declare function AlertQueryError({ error, category, onRetry, onAuthAction, className, }: AlertQueryErrorProp): React.JSX.Element;
|
|
40
44
|
export declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|