@godxjp/ui 33.4.1 → 33.5.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 +1 -1
- package/agent/components/AppProvider.json +6 -0
- package/agent/components/Form.json +1 -1
- package/agent/components/FormField.json +1 -1
- package/agent/components/TranslatableInput.json +3 -2
- package/agent/components/TranslatableTextarea.json +2 -2
- package/agent/components.json +13 -6
- package/agent/index.json +2 -2
- package/agent/llms.txt +3 -3
- package/dist/app/app-provider.d.ts +1 -1
- package/dist/app/app-provider.js +9 -0
- package/dist/components/data-entry/form-field.js +1 -1
- package/dist/components/data-entry/translatable-input.d.ts +2 -2
- package/dist/components/data-entry/translatable-input.js +6 -3
- package/dist/contracts/measurement.json +1 -1
- package/dist/props/components/app.prop.d.ts +11 -0
- package/dist/props/components/data-entry.prop.d.ts +7 -3
- package/dist/styles/card-layout.css +4 -3
- package/dist/styles/control.css +3 -2
- package/dist/styles/data-display-layout.css +1 -1
- package/dist/styles/form-layout.css +22 -2
- package/dist/styles/layers.json +1 -1
- package/dist/styles/shell-layout.css +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/agent/START-HERE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
You are about to write code against a design system you did not author. This file is the whole
|
|
4
4
|
contract. Read it before you write JSX.
|
|
5
5
|
|
|
6
|
-
**This catalog describes `@godxjp/ui` 33.
|
|
6
|
+
**This catalog describes `@godxjp/ui` 33.5.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.
|
|
@@ -68,6 +68,11 @@
|
|
|
68
68
|
"name": "persist",
|
|
69
69
|
"type": "boolean | readonly AppPreferenceAxis[]"
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
"description": "The languages the app stores USER CONTENT in, in order, source language first — e.g. `[\"ja\", \"en\", \"vi\"]` for a Japanese-first catalogue (gh#1347). A different fact from the UI locale (`defaultLocale`) and from the registered UI catalogs: a Japanese UI can edit content in three languages, and content may be stored in a language the UI has no catalog for. Every `TranslatableInput` / `TranslatableTextarea` without its own `locales` takes its tabs from this list and opens on its first entry; a field's `locales` / `defaultLocale` still override. Omit (or `[]`) to keep the field default (`getRegisteredLocales()`, first tab = the UI locale). App configuration: not persisted, no setter. Read it from `useAppContext().contentLocales` (`undefined` when unset).",
|
|
73
|
+
"name": "contentLocales",
|
|
74
|
+
"type": "readonly string[]"
|
|
75
|
+
},
|
|
71
76
|
{
|
|
72
77
|
"defaultValue": "false",
|
|
73
78
|
"description": "OFF by default and deliberately opt-in: `name` decides what a native <form> submit sends, so switching it on for every consumer of a shared package would make apps start posting new keys to their backend on a library upgrade. Turn it on for apps that need native form posts or a screen-automation (RPA) contract on their controls. The inert `data-field` companion attribute is emitted regardless; a `name` written on the control itself always wins. NOT persisted to localStorage: it is the app's configuration, not a user choice.",
|
|
@@ -100,6 +105,7 @@
|
|
|
100
105
|
"DO set `defaultTimezone='system'` together with `systemTimezone={serverTimezone}` when your backend knows the legal entity's canonical timezone (e.g. 'Asia/Ho_Chi_Minh'). Use `defaultTimezone='browser'` (the default) only when you want the user's browser clock. Do NOT pass a raw IANA string to `defaultTimezone` if the user may be in a different zone — use the named aliases.",
|
|
101
106
|
"DO wire `onLocaleChange`, `onTimezoneChange`, `onTimeFormatChange`, `onDateFormatChange` to persist changes server-side (e.g. patch user profile via Inertia router) in addition to the automatic localStorage write. These callbacks fire after state is set, so the new value is already reflected in context.",
|
|
102
107
|
"DO set `emitFieldNames` on AppProvider when the app is driven by screen automation (RPA) or posts native forms — every control under a FormField then carries a real `name` taken from the field key, and legacy automation that addressed controls by `name` keeps working after a rewrite. Leave it off (the default) otherwise: it changes what a native submit sends. The `data-field` attribute is emitted either way, so e2e selectors do not depend on this flag.",
|
|
108
|
+
"DO pass `contentLocales` when the app edits translatable content (`TranslatableInput` / `TranslatableTextarea`) — `contentLocales={[\"ja\", \"en\", \"vi\"]}` — so the tab order and the source-language first tab are stated once, not repeated as `locales` on every field. It is NOT the UI language list: leave `defaultLocale` / `registerLocale` to the UI.",
|
|
103
109
|
"DO restrict the timezone dropdown by passing `timezoneOptions={APP_TIMEZONE_PRESET}` (an exported constant) to AppProvider — every `AppSettingPicker kind=\"timezone\"` inherits this restricted list from context. Without it the picker offers the full IANA list (~600 entries)."
|
|
104
110
|
],
|
|
105
111
|
"useCases": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"type": "number | string"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
"description": "Cap the control width (number→px). Omit to fill the column.",
|
|
19
|
+
"description": "Cap the control width (number→px). Omit to fill the column. Caps the control only — the helper and error still span the field column.",
|
|
20
20
|
"name": "controlWidth",
|
|
21
21
|
"type": "number | string"
|
|
22
22
|
},
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"type": "number | string"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
|
-
"description": "Override the Form's control width for this field.",
|
|
67
|
+
"description": "Override the Form's control width for this field. Caps the control only — the helper and error still span the field column.",
|
|
68
68
|
"name": "controlWidth",
|
|
69
69
|
"type": "number | string"
|
|
70
70
|
},
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"type": "(value: TranslatableValue) => void"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
"description": "The BCP-47 tags the field edits, in tab order. Default: every locale registered with the app (`getRegisteredLocales()` — built-in `vi`/`en`/`ja` plus `registerLocale`). Tabs are named by `Intl.DisplayNames` in the reader's UI language; the tags stay the keys.",
|
|
23
|
+
"description": "The BCP-47 tags the field edits, in tab order. Default: AppProvider `contentLocales` when set (gh#1347 — state the content languages once at the root instead of on every field), else every locale registered with the app (`getRegisteredLocales()` — built-in `vi`/`en`/`ja` plus `registerLocale`). Tabs are named by `Intl.DisplayNames` in the reader's UI language; the tags stay the keys.",
|
|
24
24
|
"name": "locales",
|
|
25
25
|
"type": "readonly string[]"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
"description": "The locale tab selected first. Default: the AppProvider's active locale when it is in `locales`, else the fallback locale.",
|
|
28
|
+
"description": "The locale tab selected first. Default: the first of AppProvider `contentLocales` (the source language) when set and in `locales`, else the AppProvider's active locale when it is in `locales`, else the fallback locale.",
|
|
29
29
|
"name": "defaultLocale",
|
|
30
30
|
"type": "string"
|
|
31
31
|
},
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"storyPath": "data-entry/translatable-input.tsx",
|
|
78
78
|
"tagline": "ONE text field holding a value per locale (`{ ja, en, vi }`), switched by locale tabs above it (gh#1336). For user content stored per locale — a product name, a category label, a menu description (Astrotomic / Spatie `*_translations`). Locales, first tab and fallback come from AppProvider; an empty locale shows the fallback locale's text as its placeholder; a locale with an error is marked on its tab. Every other `Input` prop applies to the one control.",
|
|
79
79
|
"usage": [
|
|
80
|
+
"DO set the app's content languages ONCE on the root provider — `<AppProvider contentLocales={[\"ja\", \"en\", \"vi\"]}>` — when every translatable field edits the same list; each field then needs no `locales` and opens on the source language (the first). Pass `locales` on a field only where it differs.",
|
|
80
81
|
"DO wrap it in FormField: the label names the control together with the active locale («商品名 English») and names the tab strip («商品名 Language»), `required`/`helper` reach the control, and the active locale's error renders in FormField's error line.",
|
|
81
82
|
"DO pass the per-locale error map from your validator as `errors` (e.g. Laravel `name.ja` → `errors.ja`). Don't pass the same message to FormField `error` as well: FormField's own `error` wins over the reported one, and it would mark the field invalid on every locale.",
|
|
82
83
|
"DO set `fallbackLocale` to the content's SOURCE language (often `ja`) when it differs from the AppProvider's UI fallback — the placeholder of an untranslated locale then shows the source text to translate from.",
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"type": "(value: TranslatableValue) => void"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
"description": "The BCP-47 tags the field edits, in tab order. Default: the app's registered locales.",
|
|
23
|
+
"description": "The BCP-47 tags the field edits, in tab order. Default: AppProvider `contentLocales` when set, else the app's registered locales.",
|
|
24
24
|
"name": "locales",
|
|
25
25
|
"type": "readonly string[]"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
|
-
"description": "The locale tab selected first. Default: the AppProvider's active locale.",
|
|
28
|
+
"description": "The locale tab selected first. Default: the first of AppProvider `contentLocales` when set and in `locales`, else the AppProvider's active locale.",
|
|
29
29
|
"name": "defaultLocale",
|
|
30
30
|
"type": "string"
|
|
31
31
|
},
|
package/agent/components.json
CHANGED
|
@@ -5713,7 +5713,7 @@
|
|
|
5713
5713
|
"type": "number | string"
|
|
5714
5714
|
},
|
|
5715
5715
|
{
|
|
5716
|
-
"description": "Cap the control width (number→px). Omit to fill the column.",
|
|
5716
|
+
"description": "Cap the control width (number→px). Omit to fill the column. Caps the control only — the helper and error still span the field column.",
|
|
5717
5717
|
"name": "controlWidth",
|
|
5718
5718
|
"type": "number | string"
|
|
5719
5719
|
},
|
|
@@ -5849,7 +5849,7 @@
|
|
|
5849
5849
|
"type": "number | string"
|
|
5850
5850
|
},
|
|
5851
5851
|
{
|
|
5852
|
-
"description": "Override the Form's control width for this field.",
|
|
5852
|
+
"description": "Override the Form's control width for this field. Caps the control only — the helper and error still span the field column.",
|
|
5853
5853
|
"name": "controlWidth",
|
|
5854
5854
|
"type": "number | string"
|
|
5855
5855
|
},
|
|
@@ -7148,12 +7148,12 @@
|
|
|
7148
7148
|
"type": "(value: TranslatableValue) => void"
|
|
7149
7149
|
},
|
|
7150
7150
|
{
|
|
7151
|
-
"description": "The BCP-47 tags the field edits, in tab order. Default: every locale registered with the app (`getRegisteredLocales()` — built-in `vi`/`en`/`ja` plus `registerLocale`). Tabs are named by `Intl.DisplayNames` in the reader's UI language; the tags stay the keys.",
|
|
7151
|
+
"description": "The BCP-47 tags the field edits, in tab order. Default: AppProvider `contentLocales` when set (gh#1347 — state the content languages once at the root instead of on every field), else every locale registered with the app (`getRegisteredLocales()` — built-in `vi`/`en`/`ja` plus `registerLocale`). Tabs are named by `Intl.DisplayNames` in the reader's UI language; the tags stay the keys.",
|
|
7152
7152
|
"name": "locales",
|
|
7153
7153
|
"type": "readonly string[]"
|
|
7154
7154
|
},
|
|
7155
7155
|
{
|
|
7156
|
-
"description": "The locale tab selected first. Default: the AppProvider's active locale when it is in `locales`, else the fallback locale.",
|
|
7156
|
+
"description": "The locale tab selected first. Default: the first of AppProvider `contentLocales` (the source language) when set and in `locales`, else the AppProvider's active locale when it is in `locales`, else the fallback locale.",
|
|
7157
7157
|
"name": "defaultLocale",
|
|
7158
7158
|
"type": "string"
|
|
7159
7159
|
},
|
|
@@ -7205,6 +7205,7 @@
|
|
|
7205
7205
|
"storyPath": "data-entry/translatable-input.tsx",
|
|
7206
7206
|
"tagline": "ONE text field holding a value per locale (`{ ja, en, vi }`), switched by locale tabs above it (gh#1336). For user content stored per locale — a product name, a category label, a menu description (Astrotomic / Spatie `*_translations`). Locales, first tab and fallback come from AppProvider; an empty locale shows the fallback locale's text as its placeholder; a locale with an error is marked on its tab. Every other `Input` prop applies to the one control.",
|
|
7207
7207
|
"usage": [
|
|
7208
|
+
"DO set the app's content languages ONCE on the root provider — `<AppProvider contentLocales={[\"ja\", \"en\", \"vi\"]}>` — when every translatable field edits the same list; each field then needs no `locales` and opens on the source language (the first). Pass `locales` on a field only where it differs.",
|
|
7208
7209
|
"DO wrap it in FormField: the label names the control together with the active locale («商品名 English») and names the tab strip («商品名 Language»), `required`/`helper` reach the control, and the active locale's error renders in FormField's error line.",
|
|
7209
7210
|
"DO pass the per-locale error map from your validator as `errors` (e.g. Laravel `name.ja` → `errors.ja`). Don't pass the same message to FormField `error` as well: FormField's own `error` wins over the reported one, and it would mark the field invalid on every locale.",
|
|
7210
7211
|
"DO set `fallbackLocale` to the content's SOURCE language (often `ja`) when it differs from the AppProvider's UI fallback — the placeholder of an untranslated locale then shows the source text to translate from.",
|
|
@@ -7240,12 +7241,12 @@
|
|
|
7240
7241
|
"type": "(value: TranslatableValue) => void"
|
|
7241
7242
|
},
|
|
7242
7243
|
{
|
|
7243
|
-
"description": "The BCP-47 tags the field edits, in tab order. Default: the app's registered locales.",
|
|
7244
|
+
"description": "The BCP-47 tags the field edits, in tab order. Default: AppProvider `contentLocales` when set, else the app's registered locales.",
|
|
7244
7245
|
"name": "locales",
|
|
7245
7246
|
"type": "readonly string[]"
|
|
7246
7247
|
},
|
|
7247
7248
|
{
|
|
7248
|
-
"description": "The locale tab selected first. Default: the AppProvider's active locale.",
|
|
7249
|
+
"description": "The locale tab selected first. Default: the first of AppProvider `contentLocales` when set and in `locales`, else the AppProvider's active locale.",
|
|
7249
7250
|
"name": "defaultLocale",
|
|
7250
7251
|
"type": "string"
|
|
7251
7252
|
},
|
|
@@ -9059,6 +9060,11 @@
|
|
|
9059
9060
|
"name": "persist",
|
|
9060
9061
|
"type": "boolean | readonly AppPreferenceAxis[]"
|
|
9061
9062
|
},
|
|
9063
|
+
{
|
|
9064
|
+
"description": "The languages the app stores USER CONTENT in, in order, source language first — e.g. `[\"ja\", \"en\", \"vi\"]` for a Japanese-first catalogue (gh#1347). A different fact from the UI locale (`defaultLocale`) and from the registered UI catalogs: a Japanese UI can edit content in three languages, and content may be stored in a language the UI has no catalog for. Every `TranslatableInput` / `TranslatableTextarea` without its own `locales` takes its tabs from this list and opens on its first entry; a field's `locales` / `defaultLocale` still override. Omit (or `[]`) to keep the field default (`getRegisteredLocales()`, first tab = the UI locale). App configuration: not persisted, no setter. Read it from `useAppContext().contentLocales` (`undefined` when unset).",
|
|
9065
|
+
"name": "contentLocales",
|
|
9066
|
+
"type": "readonly string[]"
|
|
9067
|
+
},
|
|
9062
9068
|
{
|
|
9063
9069
|
"defaultValue": "false",
|
|
9064
9070
|
"description": "OFF by default and deliberately opt-in: `name` decides what a native <form> submit sends, so switching it on for every consumer of a shared package would make apps start posting new keys to their backend on a library upgrade. Turn it on for apps that need native form posts or a screen-automation (RPA) contract on their controls. The inert `data-field` companion attribute is emitted regardless; a `name` written on the control itself always wins. NOT persisted to localStorage: it is the app's configuration, not a user choice.",
|
|
@@ -9091,6 +9097,7 @@
|
|
|
9091
9097
|
"DO set `defaultTimezone='system'` together with `systemTimezone={serverTimezone}` when your backend knows the legal entity's canonical timezone (e.g. 'Asia/Ho_Chi_Minh'). Use `defaultTimezone='browser'` (the default) only when you want the user's browser clock. Do NOT pass a raw IANA string to `defaultTimezone` if the user may be in a different zone — use the named aliases.",
|
|
9092
9098
|
"DO wire `onLocaleChange`, `onTimezoneChange`, `onTimeFormatChange`, `onDateFormatChange` to persist changes server-side (e.g. patch user profile via Inertia router) in addition to the automatic localStorage write. These callbacks fire after state is set, so the new value is already reflected in context.",
|
|
9093
9099
|
"DO set `emitFieldNames` on AppProvider when the app is driven by screen automation (RPA) or posts native forms — every control under a FormField then carries a real `name` taken from the field key, and legacy automation that addressed controls by `name` keeps working after a rewrite. Leave it off (the default) otherwise: it changes what a native submit sends. The `data-field` attribute is emitted either way, so e2e selectors do not depend on this flag.",
|
|
9100
|
+
"DO pass `contentLocales` when the app edits translatable content (`TranslatableInput` / `TranslatableTextarea`) — `contentLocales={[\"ja\", \"en\", \"vi\"]}` — so the tab order and the source-language first tab are stated once, not repeated as `locales` on every field. It is NOT the UI language list: leave `defaultLocale` / `registerLocale` to the UI.",
|
|
9094
9101
|
"DO restrict the timezone dropdown by passing `timezoneOptions={APP_TIMEZONE_PRESET}` (an exported constant) to AppProvider — every `AppSettingPicker kind=\"timezone\"` inherits this restricted list from context. Without it the picker offers the full IANA list (~600 entries)."
|
|
9095
9102
|
],
|
|
9096
9103
|
"useCases": [
|
package/agent/index.json
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
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.5.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",
|
|
@@ -68,5 +68,5 @@
|
|
|
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.5.0"
|
|
72
72
|
}
|
package/agent/llms.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @godxjp/ui
|
|
2
2
|
|
|
3
3
|
> A Japanese-enterprise React design system: 166 components, 2232 design tokens,
|
|
4
|
-
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.
|
|
4
|
+
> 44 cardinal rules. This file is the entry point for AI agents. Catalog version 33.5.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.5.0`). It is searchable and version-locked. These files exist for agents
|
|
8
8
|
that can only fetch URLs.
|
|
9
9
|
|
|
10
10
|
## Start
|
|
@@ -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.5.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.
|
|
@@ -4,7 +4,7 @@ import type { AppContextValue, AppProviderProp } from "../props/components/app.p
|
|
|
4
4
|
export type { AppProviderProp, AppContextValue } from "../props/components/app.prop.js";
|
|
5
5
|
/** `AppProvider`'s props (`preset` included since v32, gh#1219 / gh#1220). */
|
|
6
6
|
export type AppProviderProps = AppProviderProp;
|
|
7
|
-
export declare function AppProvider({ children, preset, defaultLocale: defaultLocaleProp, fallbackLocale, defaultTimezone: defaultTimezoneProp, systemTimezone, defaultTimeFormat, defaultDateFormat, timezoneOptions, storageKey, persist, theme: initialTheme, brand: initialBrand, density: initialDensity, fontSize: initialFontSize, scaling: initialScaling, emitFieldNames, onLocaleChange, onTimezoneChange, onTimeFormatChange, onDateFormatChange, onThemeChange, onBrandChange, onDensityChange, onFontSizeChange, onScalingChange, }: AppProviderProps): React.JSX.Element;
|
|
7
|
+
export declare function AppProvider({ children, preset, defaultLocale: defaultLocaleProp, fallbackLocale, defaultTimezone: defaultTimezoneProp, systemTimezone, defaultTimeFormat, defaultDateFormat, timezoneOptions, contentLocales: contentLocalesProp, storageKey, persist, theme: initialTheme, brand: initialBrand, density: initialDensity, fontSize: initialFontSize, scaling: initialScaling, emitFieldNames, onLocaleChange, onTimezoneChange, onTimeFormatChange, onDateFormatChange, onThemeChange, onBrandChange, onDensityChange, onFontSizeChange, onScalingChange, }: AppProviderProps): React.JSX.Element;
|
|
8
8
|
export declare function useAppContext(): AppContextValue;
|
|
9
9
|
/** Returns null outside AppProvider — used by pickers for optional context. */
|
|
10
10
|
export declare function useOptionalAppContext(): AppContextValue | null;
|
package/dist/app/app-provider.js
CHANGED
|
@@ -80,6 +80,7 @@ function AppProvider({
|
|
|
80
80
|
defaultTimeFormat = "locale",
|
|
81
81
|
defaultDateFormat = "locale",
|
|
82
82
|
timezoneOptions,
|
|
83
|
+
contentLocales: contentLocalesProp,
|
|
83
84
|
storageKey = DEFAULT_STORAGE_KEY,
|
|
84
85
|
persist = true,
|
|
85
86
|
theme: initialTheme = "light",
|
|
@@ -133,6 +134,12 @@ function AppProvider({
|
|
|
133
134
|
scaling
|
|
134
135
|
});
|
|
135
136
|
const persistKey = Array.isArray(persist) ? [...persist].sort().join(",") : String(persist);
|
|
137
|
+
const contentLocalesKey = contentLocalesProp?.join(",");
|
|
138
|
+
const contentLocales = React.useMemo(
|
|
139
|
+
() => contentLocalesProp,
|
|
140
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
141
|
+
[contentLocalesKey]
|
|
142
|
+
);
|
|
136
143
|
const persistedAxes = React.useMemo(
|
|
137
144
|
() => resolvePersistedAxes(persist),
|
|
138
145
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -400,6 +407,7 @@ function AppProvider({
|
|
|
400
407
|
dayPickerLocale: getDayPickerLocale(locale),
|
|
401
408
|
requestHeaders,
|
|
402
409
|
timezoneOptions,
|
|
410
|
+
contentLocales,
|
|
403
411
|
theme,
|
|
404
412
|
brand,
|
|
405
413
|
density,
|
|
@@ -425,6 +433,7 @@ function AppProvider({
|
|
|
425
433
|
dateFnsLocale,
|
|
426
434
|
requestHeaders,
|
|
427
435
|
timezoneOptions,
|
|
436
|
+
contentLocales,
|
|
428
437
|
theme,
|
|
429
438
|
brand,
|
|
430
439
|
density,
|
|
@@ -3,8 +3,8 @@ import type { TranslatableValue } from "../../props/components/data-entry.prop.j
|
|
|
3
3
|
export type { TranslatableValue, TranslatableInputProp, TranslatableInputProp as TranslatableInputProps, TranslatableTextareaProp, TranslatableTextareaProp as TranslatableTextareaProps, } from "../../props/components/data-entry.prop.js";
|
|
4
4
|
/**
|
|
5
5
|
* TranslatableInput — ONE text field that holds a value per locale (`{ ja, en, vi }`), switched by
|
|
6
|
-
* locale tabs above it (gh#1336). The locale list
|
|
7
|
-
* `
|
|
6
|
+
* locale tabs above it (gh#1336). The locale list and the first tab come from `AppProvider`
|
|
7
|
+
* `contentLocales` (gh#1347), the fallback from its `fallbackLocale`; each is overridable per field; an empty locale shows the fallback locale's text as
|
|
8
8
|
* its placeholder; a locale with an error is marked on its tab. Every other `Input` prop applies to
|
|
9
9
|
* the single control, and `ref` is that control's `<input>`.
|
|
10
10
|
*/
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { CircleAlert } from "lucide-react";
|
|
5
|
+
import { useOptionalAppContext } from "../../app/app-provider.js";
|
|
5
6
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
7
|
import { getRegisteredLocales } from "../../i18n/locale-tags.js";
|
|
7
8
|
import { mergeAriaIds, useReportFieldError } from "../../lib/field-a11y.js";
|
|
@@ -35,14 +36,16 @@ function TranslatableField({
|
|
|
35
36
|
...rest
|
|
36
37
|
}) {
|
|
37
38
|
const { t, locale: uiLocale, fallbackLocale: appFallbackLocale } = useTranslation();
|
|
39
|
+
const appContentLocales = useOptionalAppContext()?.contentLocales;
|
|
40
|
+
const contentLocales = appContentLocales?.length ? appContentLocales : void 0;
|
|
38
41
|
const locales = React.useMemo(() => {
|
|
39
|
-
const list = localesProp ?? getRegisteredLocales();
|
|
42
|
+
const list = localesProp ?? contentLocales ?? getRegisteredLocales();
|
|
40
43
|
return list.length > 0 ? list : [uiLocale];
|
|
41
|
-
}, [localesProp, uiLocale]);
|
|
44
|
+
}, [localesProp, contentLocales, uiLocale]);
|
|
42
45
|
const pick = (candidates) => candidates.find((code) => code !== void 0 && locales.includes(code)) ?? locales[0];
|
|
43
46
|
const fallbackLocale = pick([fallbackLocaleProp, appFallbackLocale]);
|
|
44
47
|
const [activeState, setActiveState] = React.useState(
|
|
45
|
-
() => pick([defaultLocale, uiLocale, fallbackLocale])
|
|
48
|
+
() => pick([defaultLocale, contentLocales?.[0], uiLocale, fallbackLocale])
|
|
46
49
|
);
|
|
47
50
|
const active = locales.includes(activeState) ? activeState : pick([defaultLocale, fallbackLocale]);
|
|
48
51
|
const [innerValue, setInnerValue] = React.useState(defaultValue ?? NO_VALUE);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "33.
|
|
3
|
+
"version": "33.5.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -46,6 +46,15 @@ export type AppProviderProp = {
|
|
|
46
46
|
* the full IANA list; set to restrict (e.g.
|
|
47
47
|
*/
|
|
48
48
|
timezoneOptions?: readonly AppTimezone[];
|
|
49
|
+
/**
|
|
50
|
+
* The languages the app stores USER CONTENT in (a product name in `ja`/`en`/`vi`), in order —
|
|
51
|
+
* source language first. Not the UI locale and not the registered UI catalogs: a Japanese UI
|
|
52
|
+
* can edit content in ja · en · vi, and content may be stored in a language the UI has no
|
|
53
|
+
* catalog for. Every `TranslatableInput` / `TranslatableTextarea` without its own `locales`
|
|
54
|
+
* takes its tabs from here and selects the first one. Omit to keep the per-field default
|
|
55
|
+
* (`getRegisteredLocales()`, first tab = the UI locale). App configuration, never persisted.
|
|
56
|
+
*/
|
|
57
|
+
contentLocales?: readonly string[];
|
|
49
58
|
/** localStorage key. Default: `godxjp.app`. */
|
|
50
59
|
storageKey?: string;
|
|
51
60
|
/**
|
|
@@ -167,6 +176,8 @@ export type AppContextValue = {
|
|
|
167
176
|
requestHeaders: AppRequestHeaders;
|
|
168
177
|
/** Configured timezone list; `undefined` → full IANA in the timezone-picker recipe. */
|
|
169
178
|
timezoneOptions?: readonly AppTimezone[];
|
|
179
|
+
/** The app's content languages, source first; `undefined` when `contentLocales` is not set. */
|
|
180
|
+
contentLocales?: readonly string[];
|
|
170
181
|
/**
|
|
171
182
|
* Current theme axes (mirror `<html data-*>` / inline `--scaling`). `theme` is the user's
|
|
172
183
|
* CHOICE — it can be `"system"`, which `<html data-theme>` never is.
|
|
@@ -244,11 +244,15 @@ type TranslatableFieldProp = {
|
|
|
244
244
|
/** The WHOLE map after an edit — every other locale's text is carried over untouched. */
|
|
245
245
|
onValueChange?: OnValueChangeProp<TranslatableValue>;
|
|
246
246
|
/**
|
|
247
|
-
* The locales the field edits, in tab order. Default:
|
|
248
|
-
* (`getRegisteredLocales()` — the built-in
|
|
247
|
+
* The locales the field edits, in tab order. Default: the AppProvider's `contentLocales` when
|
|
248
|
+
* set, else every locale registered with the app (`getRegisteredLocales()` — the built-in
|
|
249
|
+
* `vi`/`en`/`ja` plus `registerLocale(...)`).
|
|
249
250
|
*/
|
|
250
251
|
locales?: readonly string[];
|
|
251
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* The locale tab selected first. Default: the first of the AppProvider's `contentLocales` (the
|
|
254
|
+
* source language) when set and in `locales`, else the AppProvider's active locale when it is.
|
|
255
|
+
*/
|
|
252
256
|
defaultLocale?: string;
|
|
253
257
|
/**
|
|
254
258
|
* The locale whose text shows as the placeholder of an EMPTY locale. Default: the AppProvider's
|
|
@@ -705,15 +705,16 @@
|
|
|
705
705
|
.ui-card-bar[data-surface="muted"] {
|
|
706
706
|
background: hsl(var(--muted));
|
|
707
707
|
}
|
|
708
|
+
|
|
708
709
|
.ui-card-bar[data-border="none"] {
|
|
709
|
-
border-block: 0;
|
|
710
|
+
border-block-width: 0;
|
|
710
711
|
}
|
|
711
712
|
.ui-card-bar[data-border="block-start"] {
|
|
712
|
-
border-block-end: 0;
|
|
713
|
+
border-block-end-width: 0;
|
|
713
714
|
border-block-start: var(--stroke-hairline) solid hsl(var(--border));
|
|
714
715
|
}
|
|
715
716
|
.ui-card-bar[data-border="block-end"] {
|
|
716
|
-
border-block-start: 0;
|
|
717
|
+
border-block-start-width: 0;
|
|
717
718
|
border-block-end: var(--stroke-hairline) solid hsl(var(--border));
|
|
718
719
|
}
|
|
719
720
|
.ui-card-bar[data-border="both"] {
|
package/dist/styles/control.css
CHANGED
|
@@ -215,7 +215,8 @@
|
|
|
215
215
|
height: auto;
|
|
216
216
|
min-height: 0;
|
|
217
217
|
padding: 0;
|
|
218
|
-
|
|
218
|
+
|
|
219
|
+
border-width: 0;
|
|
219
220
|
background: transparent;
|
|
220
221
|
color: inherit;
|
|
221
222
|
font: inherit;
|
|
@@ -897,7 +898,7 @@
|
|
|
897
898
|
width: 100%;
|
|
898
899
|
flex: 1;
|
|
899
900
|
height: var(--control-height);
|
|
900
|
-
border: 0;
|
|
901
|
+
border-width: 0;
|
|
901
902
|
background: transparent;
|
|
902
903
|
padding-inline: 0;
|
|
903
904
|
box-shadow: none;
|
|
@@ -87,8 +87,6 @@
|
|
|
87
87
|
gap: var(--space-stack-xs);
|
|
88
88
|
min-width: 0;
|
|
89
89
|
|
|
90
|
-
max-inline-size: var(--form-control-width, none);
|
|
91
|
-
|
|
92
90
|
min-height: var(--control-height);
|
|
93
91
|
align-content: center;
|
|
94
92
|
|
|
@@ -96,10 +94,32 @@
|
|
|
96
94
|
align-items: center;
|
|
97
95
|
}
|
|
98
96
|
|
|
97
|
+
.ui-form-field-control > :not(.ui-form-field-note, .ui-form-feedback) {
|
|
98
|
+
max-inline-size: var(--form-control-width, none);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.ui-form-field-input {
|
|
102
|
+
display: grid;
|
|
103
|
+
grid-template-columns: minmax(0, 1fr);
|
|
104
|
+
gap: inherit;
|
|
105
|
+
min-inline-size: 0;
|
|
106
|
+
margin: 0;
|
|
107
|
+
padding: 0;
|
|
108
|
+
border: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.ui-form-field-input > * {
|
|
112
|
+
min-width: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
99
115
|
.ui-form-field-control:has(> .ui-form-field-note:first-child) {
|
|
100
116
|
grid-template-rows: none;
|
|
101
117
|
}
|
|
102
118
|
|
|
119
|
+
.ui-form-field-control
|
|
120
|
+
> fieldset:has(> :is(.ui-choice-group[data-orientation="vertical"], .ui-choice-field)) {
|
|
121
|
+
align-self: start;
|
|
122
|
+
}
|
|
103
123
|
.ui-form-field-control
|
|
104
124
|
> fieldset
|
|
105
125
|
> :is(.ui-choice-group[data-orientation="vertical"], .ui-choice-field) {
|
package/dist/styles/layers.json
CHANGED
|
@@ -2283,7 +2283,7 @@
|
|
|
2283
2283
|
justify-content: center;
|
|
2284
2284
|
gap: var(--topbar-item-gap);
|
|
2285
2285
|
padding-inline: var(--topbar-item-padding-inline);
|
|
2286
|
-
border: 0;
|
|
2286
|
+
border-width: 0;
|
|
2287
2287
|
border-radius: var(--topbar-item-radius);
|
|
2288
2288
|
background: transparent;
|
|
2289
2289
|
color: hsl(var(--topbar-item-color, var(--muted-foreground)));
|