@godxjp/ui-mcp 0.9.0 → 0.11.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/dist/index.js +492 -241
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -191,6 +191,75 @@ import { Button } from "@godxjp/ui/general";
|
|
|
191
191
|
storyPath: "layout/Inline.stories.tsx",
|
|
192
192
|
rules: [2]
|
|
193
193
|
},
|
|
194
|
+
{
|
|
195
|
+
name: "Flex",
|
|
196
|
+
group: "layout",
|
|
197
|
+
tagline: "Token-spaced flex primitive with explicit direction, alignment, justification, and wrapping controls.",
|
|
198
|
+
props: [
|
|
199
|
+
{
|
|
200
|
+
name: "direction",
|
|
201
|
+
type: '"row" | "col"',
|
|
202
|
+
defaultValue: '"col"',
|
|
203
|
+
description: "Main axis direction. Use row for horizontal runs, col for vertical stacks."
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: "gap",
|
|
207
|
+
type: '"xs" | "sm" | "md" | "lg" | "xl"',
|
|
208
|
+
defaultValue: '"md"',
|
|
209
|
+
description: "Token gap between children, shared with Stack and Inline."
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "align",
|
|
213
|
+
type: '"start" | "center" | "end" | "stretch" | "baseline"',
|
|
214
|
+
description: "Cross-axis alignment, emitted as a data attribute for the layout CSS."
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: "justify",
|
|
218
|
+
type: '"start" | "center" | "end" | "between" | "around" | "evenly"',
|
|
219
|
+
description: "Main-axis distribution, emitted as a data attribute for the layout CSS."
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "wrap",
|
|
223
|
+
type: "boolean",
|
|
224
|
+
defaultValue: "false",
|
|
225
|
+
description: "Allows children to wrap onto additional flex lines."
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
usage: [
|
|
229
|
+
'DO import from `@godxjp/ui/layout` and reach for Flex when the axis, alignment, justification, or wrap behavior is part of the component contract: `import { Flex } from "@godxjp/ui/layout"`.',
|
|
230
|
+
"DO keep spacing on the `gap` prop instead of raw `gap-*`, `space-*`, or padding utilities. Flex uses the same token scale as Stack and Inline, so spacing remains tied to the design system.",
|
|
231
|
+
'DO use `direction="row"` with `wrap` for responsive control rows, chip clusters, and action groups that need more control than Inline exposes.',
|
|
232
|
+
'DO use `direction="col"` for vertical groupings that need explicit `align` or `justify` behavior. If all you need is a vertical gap, Stack is the thinner alias.',
|
|
233
|
+
"DON'T override the axis with `className` after choosing a direction prop. Keep the layout intent in props so catalog guidance and data attributes stay accurate.",
|
|
234
|
+
"Flex is a plain div with React.HTMLAttributes<HTMLDivElement>; pass `id`, `role`, `aria-*`, `data-*`, and structural className values as needed, but do not use it as a semantic form or button wrapper."
|
|
235
|
+
],
|
|
236
|
+
useCases: [
|
|
237
|
+
"Toolbar internals where controls should sit in a row, wrap on narrow widths, and stay vertically centered.",
|
|
238
|
+
"Card headers that need title content on the left and actions on the right via `justify='between'` without hand-rolling flex utility classes.",
|
|
239
|
+
"Empty-state or loading blocks that center content on both axes using `align='center'` and `justify='center'`.",
|
|
240
|
+
"Form sub-sections where a vertical group needs stretched children or centered helper content beyond what Stack exposes.",
|
|
241
|
+
"Badge, chip, or tag clusters where wrapping is required but the caller also needs a larger token gap than Inline's default.",
|
|
242
|
+
"Low-level layout composition inside custom components where Stack and Inline are too opinionated but raw flex classes would duplicate the primitive."
|
|
243
|
+
],
|
|
244
|
+
related: [
|
|
245
|
+
"Stack \u2014 thin `direction='col'` alias of Flex. Use Stack for ordinary vertical block spacing; use Flex when you need align, justify, or wrap control.",
|
|
246
|
+
"Inline \u2014 thin `direction='row'` alias of Flex with wrapping and centered alignment. Use Inline for simple horizontal groups; use Flex for explicit axis/distribution control.",
|
|
247
|
+
"ResponsiveGrid \u2014 use for equal-width, multi-column tile layouts. Flex arranges children on one flex axis and does not provide column-count behavior.",
|
|
248
|
+
"PageContainer \u2014 page scaffold and padding context. Flex is an inner layout primitive used inside page sections, cards, dialogs, and toolbars."
|
|
249
|
+
],
|
|
250
|
+
example: `import { Flex } from "@godxjp/ui/layout";
|
|
251
|
+
import { Button } from "@godxjp/ui/general";
|
|
252
|
+
|
|
253
|
+
<Flex direction="row" gap="sm" align="center" justify="between" wrap>
|
|
254
|
+
<SearchSummary />
|
|
255
|
+
<Flex direction="row" gap="xs" align="center" wrap>
|
|
256
|
+
<Button variant="outline">\u30EA\u30BB\u30C3\u30C8</Button>
|
|
257
|
+
<Button>\u9069\u7528</Button>
|
|
258
|
+
</Flex>
|
|
259
|
+
</Flex>`,
|
|
260
|
+
storyPath: "layout/Flex.stories.tsx",
|
|
261
|
+
rules: [2, 40]
|
|
262
|
+
},
|
|
194
263
|
{
|
|
195
264
|
name: "ResponsiveGrid",
|
|
196
265
|
group: "layout",
|
|
@@ -811,7 +880,7 @@ function MyShell({ children }: { content: React.ReactNode }) {
|
|
|
811
880
|
],
|
|
812
881
|
useCases: [
|
|
813
882
|
'Primary form submission in a Dialog or Sheet (e.g. `<Button type="submit" disabled={form.processing}>\u4FDD\u5B58</Button>`) \u2014 the `disabled` prop greys it out and blocks pointer events, preventing double-submit during async operations.',
|
|
814
|
-
'Destructive confirmation inside a Dialog \u2014 pair `
|
|
883
|
+
'Destructive confirmation inside a Dialog \u2014 pair `tone="destructive"` Button as the confirm action and `variant="outline"` as Cancel; never use `variant="default"` for a delete action.',
|
|
815
884
|
'Icon-only toolbar actions in a DataTable column (edit, delete, copy) using `size="icon-sm"` + `variant="ghost"` + a Lucide icon child \u2014 gives equal-width square targets that don\'t distort the row.',
|
|
816
885
|
"Navigation links styled as buttons (e.g. 'New Invoice', 'Back to list') using `asChild` + Inertia `<Link>` \u2014 preserves SPA navigation while using the button's visual treatment.",
|
|
817
886
|
"Async mutation trigger in an accounting workflow (e.g. 'Sync from MF', 'Export CSV') \u2014 disable on processing state; pair with `MutationFeedback` for error/retry UI rather than inline `try/catch` alerts.",
|
|
@@ -1212,7 +1281,11 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
|
1212
1281
|
type: "React.ComponentType<{ className?: string }> | null",
|
|
1213
1282
|
description: "Leading icon override. Pass null to suppress the auto status icon."
|
|
1214
1283
|
},
|
|
1215
|
-
{
|
|
1284
|
+
{
|
|
1285
|
+
name: "children",
|
|
1286
|
+
type: "ReactNode",
|
|
1287
|
+
description: "Badge label. When omitted with status, Badge renders the translated lifecycle label or raw status."
|
|
1288
|
+
}
|
|
1216
1289
|
],
|
|
1217
1290
|
usage: [
|
|
1218
1291
|
"DO pick the correct variant semantically: `success` (approved/paid), `warning` (pending/overdue), `destructive` (rejected/error), `secondary` (neutral category), `outline` (subtle label), `default` (primary accent). Never force a colour just for aesthetics \u2014 agents and screen readers read the variant as intent.",
|
|
@@ -1224,7 +1297,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
|
1224
1297
|
],
|
|
1225
1298
|
useCases: [
|
|
1226
1299
|
'Category or tier labels on table rows \u2014 e.g. plan tier (`<Badge variant="secondary">Pro</Badge>`), document type (`<Badge variant="outline">Invoice</Badge>`), or locale tag (`<Badge variant="secondary">EN</Badge>`).',
|
|
1227
|
-
'Approval or review state in an accounting list where the value is not a lifecycle key in Badge\'s STATUS_MAP \u2014 e.g. a custom approval tier like `<Badge
|
|
1300
|
+
'Approval or review state in an accounting list where the value is not a lifecycle key in Badge\'s STATUS_MAP \u2014 e.g. a custom approval tier like `<Badge tone="success">\u627F\u8A8D\u6E08</Badge>` or `<Badge tone="warning">\u8981\u78BA\u8A8D</Badge>`.',
|
|
1228
1301
|
"Inline count or highlight next to a heading or nav item \u2014 e.g. `<Badge variant=\"destructive\">3</Badge>` beside 'Overdue invoices' to draw attention to a non-zero count.",
|
|
1229
1302
|
'Feature flags or experiment variant labels on admin records \u2014 e.g. `<Badge variant="outline">A/B</Badge>` alongside a campaign row to indicate it is in a split test.',
|
|
1230
1303
|
"Read-only metadata chips inside a Descriptions.Item or Card header where a lifecycle icon would be visually heavy \u2014 e.g. currency code, payment method, or region tag."
|
|
@@ -1236,7 +1309,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
|
1236
1309
|
|
|
1237
1310
|
<Badge variant="secondary">A/B</Badge>
|
|
1238
1311
|
<Badge status="active">\u516C\u958B\u4E2D</Badge>
|
|
1239
|
-
<Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0"
|
|
1312
|
+
<Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" tone="success" icon={null}>\u30D7\u30EC\u30DF\u30A2\u30E0</Badge>`,
|
|
1240
1313
|
storyPath: "data-display/Badge.stories.tsx",
|
|
1241
1314
|
rules: [35]
|
|
1242
1315
|
},
|
|
@@ -1356,7 +1429,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
|
1356
1429
|
"DON'T use Progress for editable numeric input or range selection \u2014 it has no callbacks, no interactivity, and no form `name` prop. Use Slider (bounded range input) or Input (free-form number) for data-entry scenarios."
|
|
1357
1430
|
],
|
|
1358
1431
|
useCases: [
|
|
1359
|
-
'Budget utilisation in an accounting dashboard \u2014 show how much of a monthly budget has been consumed, switching to `
|
|
1432
|
+
'Budget utilisation in an accounting dashboard \u2014 show how much of a monthly budget has been consumed, switching to `tone="warning"` when the figure crosses 80%.',
|
|
1360
1433
|
'Invoice payment progress \u2014 display the proportion of an invoice total that has been settled (e.g. partial payments), with a label like `"\xA545,000 / \xA560,000 \u652F\u6255\u6E08"` computed before passing `value`.',
|
|
1361
1434
|
"Storage or quota indicator in an admin panel \u2014 visualise disk usage, API quota, or seat licence consumption against a fixed limit.",
|
|
1362
1435
|
"Sync / import job completion feedback \u2014 surface the completion percentage of a long-running background job (polling the server) without giving the user an interactive control.",
|
|
@@ -1675,7 +1748,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
|
1675
1748
|
example: `import { FormField, Input } from "@godxjp/ui/data-entry";
|
|
1676
1749
|
|
|
1677
1750
|
<FormField id="coupon-name" label="\u30AF\u30FC\u30DD\u30F3\u540D" required error={errors.name} helper="\u6700\u592750\u6587\u5B57">
|
|
1678
|
-
<Input id="coupon-name" placeholder="\u6625\u306E\u82B1\u7C89\u75C7\u5BFE\u7B5615%OFF" value={name}
|
|
1751
|
+
<Input id="coupon-name" placeholder="\u6625\u306E\u82B1\u7C89\u75C7\u5BFE\u7B5615%OFF" value={name} onValueChange={(e) => setName(e.target.value)} />
|
|
1679
1752
|
</FormField>`,
|
|
1680
1753
|
storyPath: "data-entry/FormField.stories.tsx",
|
|
1681
1754
|
rules: [23]
|
|
@@ -1718,7 +1791,7 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
|
1718
1791
|
],
|
|
1719
1792
|
example: `import { Input } from "@godxjp/ui/data-entry";
|
|
1720
1793
|
|
|
1721
|
-
<Input id="qty" type="number" placeholder="\u4F8B: 500" value={value}
|
|
1794
|
+
<Input id="qty" type="number" placeholder="\u4F8B: 500" value={value} onValueChange={(e) => setValue(e.target.value)} />`,
|
|
1722
1795
|
storyPath: "data-entry/Input.stories.tsx",
|
|
1723
1796
|
rules: []
|
|
1724
1797
|
},
|
|
@@ -1916,7 +1989,7 @@ export function StatusSelect({ value, onChange }) {
|
|
|
1916
1989
|
return (
|
|
1917
1990
|
<Select
|
|
1918
1991
|
value={value}
|
|
1919
|
-
|
|
1992
|
+
onValueChange={onChange}
|
|
1920
1993
|
options={[
|
|
1921
1994
|
{ value: "draft", label: "Draft" },
|
|
1922
1995
|
{ value: "sent", label: "Sent" },
|
|
@@ -1935,7 +2008,7 @@ export function CurrencySelect({ value, onChange }) {
|
|
|
1935
2008
|
return (
|
|
1936
2009
|
<Select
|
|
1937
2010
|
value={value}
|
|
1938
|
-
|
|
2011
|
+
onValueChange={onChange}
|
|
1939
2012
|
showSearch
|
|
1940
2013
|
options={[
|
|
1941
2014
|
{ value: "JPY", label: "Japanese Yen", group: "Asia" },
|
|
@@ -1961,7 +2034,7 @@ export function AccountSelect({ value, onChange, selectedLabel }) {
|
|
|
1961
2034
|
return (
|
|
1962
2035
|
<Select
|
|
1963
2036
|
value={value}
|
|
1964
|
-
|
|
2037
|
+
onValueChange={onChange}
|
|
1965
2038
|
loadOptions={loadOptions}
|
|
1966
2039
|
selectedLabel={selectedLabel}
|
|
1967
2040
|
placeholder="Search accounts\u2026"
|
|
@@ -2084,7 +2157,7 @@ export function PrioritySelect({ value, onValueChange }) {
|
|
|
2084
2157
|
],
|
|
2085
2158
|
example: `import { Textarea } from "@godxjp/ui/data-entry";
|
|
2086
2159
|
|
|
2087
|
-
<Textarea id="notes" rows={4} placeholder="\u81EA\u7531\u8A18\u8FF0" value={notes}
|
|
2160
|
+
<Textarea id="notes" rows={4} placeholder="\u81EA\u7531\u8A18\u8FF0" value={notes} onValueChange={(e) => setNotes(e.target.value)} />`,
|
|
2088
2161
|
storyPath: "data-entry/Textarea.stories.tsx",
|
|
2089
2162
|
rules: []
|
|
2090
2163
|
},
|
|
@@ -2320,7 +2393,7 @@ export function InvoiceDueDateField() {
|
|
|
2320
2393
|
id="due-date"
|
|
2321
2394
|
name="due_date"
|
|
2322
2395
|
value={dueDate}
|
|
2323
|
-
|
|
2396
|
+
onValueChange={setDueDate}
|
|
2324
2397
|
fromDate={new Date()}
|
|
2325
2398
|
placeholder="yyyy-mm-dd"
|
|
2326
2399
|
/>
|
|
@@ -2465,7 +2538,7 @@ import { Button } from "@godxjp/ui/general";
|
|
|
2465
2538
|
}
|
|
2466
2539
|
],
|
|
2467
2540
|
usage: [
|
|
2468
|
-
'DO compose with sub-parts in order: wrap text content in `<Alert.Content>` (or bare `<AlertContent>`), then `<Alert.Title>` + `<Alert.Description>` inside it, then `<Alert.Actions>` for any retry/CTA buttons. Example: `<Alert
|
|
2541
|
+
'DO compose with sub-parts in order: wrap text content in `<Alert.Content>` (or bare `<AlertContent>`), then `<Alert.Title>` + `<Alert.Description>` inside it, then `<Alert.Actions>` for any retry/CTA buttons. Example: `<Alert tone="destructive"><Alert.Content><Alert.Title>Error</Alert.Title><Alert.Description>{msg}</Alert.Description></Alert.Content><Alert.Actions><Button \u2026/></Alert.Actions></Alert>`.',
|
|
2469
2542
|
"DO use `Alert.QueryError` (alias `AlertQueryError`) for TanStack Query / API failure surfaces \u2014 it already renders humanError(error), an i18n title, and an optional Retry button. Never hand-roll that pattern.",
|
|
2470
2543
|
'DON\'T pass raw action elements directly as top-level children of `<Alert>` without wrapping them in `<Alert.Actions>` \u2014 the layout slot only activates correctly via the `data-slot="alert-actions"` wrapper.',
|
|
2471
2544
|
'DON\'T hand-roll a dismiss \u2715 button \u2014 pass `onDismiss` to `<Alert>` and the component renders its own accessible dismiss button with `aria-label="Dismiss"`. The `onDismiss` handler may return a Promise.',
|
|
@@ -2473,12 +2546,12 @@ import { Button } from "@godxjp/ui/general";
|
|
|
2473
2546
|
"DO NOT use `Alert` for transient ephemeral feedback (e.g. 'saved successfully'). Use `toast()` from sonner + `<Toaster>` for that. `Alert` is for persistent, page-scoped banners that stay visible until the user acts or dismisses."
|
|
2474
2547
|
],
|
|
2475
2548
|
useCases: [
|
|
2476
|
-
'Page-level error banner after a form submission fails server-side validation \u2014 `
|
|
2549
|
+
'Page-level error banner after a form submission fails server-side validation \u2014 `tone="destructive"` with `Alert.Title` summarising the error and `Alert.Description` listing field issues, paired with `onDismiss` so the user can clear it.',
|
|
2477
2550
|
"Inline warning at the top of an accounting invoice list when the OAuth token for the MF sync is about to expire \u2014 `variant=\"warning\"` with an `Alert.Actions` containing a 'Reconnect' Button.",
|
|
2478
|
-
'Success confirmation banner rendered after a bulk-import job completes and the user returns to the list page \u2014 `
|
|
2551
|
+
'Success confirmation banner rendered after a bulk-import job completes and the user returns to the list page \u2014 `tone="success"` with `Alert.Description` showing the record count imported.',
|
|
2479
2552
|
"TanStack Query data-fetch failure inside a Card body \u2014 use `<Alert.QueryError error={error} onRetry={refetch} />` instead of writing a custom error state.",
|
|
2480
2553
|
"Informational notice at the top of a settings page when a feature is in beta or requires a plan upgrade \u2014 `variant=\"default\"` (Info icon) with a short description and an `Alert.Actions` 'Learn more' link.",
|
|
2481
|
-
'Dismissible billing-overdue notice at the top of the dashboard \u2014 `
|
|
2554
|
+
'Dismissible billing-overdue notice at the top of the dashboard \u2014 `tone="destructive"` with `onDismiss` that sets a session flag so it does not reappear until the next login.'
|
|
2482
2555
|
],
|
|
2483
2556
|
related: [
|
|
2484
2557
|
"Toaster \u2014 use for transient, auto-dismissing feedback ('Record saved', 'Deleted'). Alert is for persistent page-scoped banners; Toaster is for fire-and-forget notifications triggered by toast() from sonner.",
|
|
@@ -2488,7 +2561,7 @@ import { Button } from "@godxjp/ui/general";
|
|
|
2488
2561
|
],
|
|
2489
2562
|
example: `import { Alert, AlertTitle, AlertDescription } from "@godxjp/ui/feedback";
|
|
2490
2563
|
|
|
2491
|
-
<Alert
|
|
2564
|
+
<Alert tone="warning">
|
|
2492
2565
|
<AlertTitle>3 \u4EF6\u306E\u6253\u523B\u6F0F\u308C\u304C\u3042\u308A\u307E\u3059</AlertTitle>
|
|
2493
2566
|
<AlertDescription>\u672C\u65E5\u4E2D\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002</AlertDescription>
|
|
2494
2567
|
</Alert>`,
|
|
@@ -2809,7 +2882,7 @@ import { SearchInput, Select, SelectTrigger, SelectValue, SelectContent, SelectI
|
|
|
2809
2882
|
],
|
|
2810
2883
|
example: `import { Pagination } from "@godxjp/ui/navigation";
|
|
2811
2884
|
|
|
2812
|
-
<Pagination current={page} total={filtered.length} pageSize={10} showTotal
|
|
2885
|
+
<Pagination current={page} total={filtered.length} pageSize={10} showTotal onValueChange={(p) => setPage(p)} />`,
|
|
2813
2886
|
storyPath: "navigation/Pagination.stories.tsx",
|
|
2814
2887
|
rules: [40]
|
|
2815
2888
|
},
|
|
@@ -2855,7 +2928,7 @@ import { Button } from "@godxjp/ui/general";
|
|
|
2855
2928
|
<DropdownMenuContent>
|
|
2856
2929
|
<DropdownMenuItem>\u7DE8\u96C6</DropdownMenuItem>
|
|
2857
2930
|
<DropdownMenuSeparator />
|
|
2858
|
-
<DropdownMenuItem
|
|
2931
|
+
<DropdownMenuItem tone="destructive">\u524A\u9664</DropdownMenuItem>
|
|
2859
2932
|
</DropdownMenuContent>
|
|
2860
2933
|
</DropdownMenu>`,
|
|
2861
2934
|
storyPath: "navigation/DropdownMenu.stories.tsx",
|
|
@@ -3110,7 +3183,7 @@ export function ShiftStartField() {
|
|
|
3110
3183
|
id="shift-start"
|
|
3111
3184
|
name="shift_start"
|
|
3112
3185
|
value={startTime}
|
|
3113
|
-
|
|
3186
|
+
onValueChange={setStartTime}
|
|
3114
3187
|
minuteStep={15}
|
|
3115
3188
|
className="w-36"
|
|
3116
3189
|
/>
|
|
@@ -3230,7 +3303,7 @@ export function InvoicePeriodFilter() {
|
|
|
3230
3303
|
id="invoice-period"
|
|
3231
3304
|
name="period"
|
|
3232
3305
|
value={range}
|
|
3233
|
-
|
|
3306
|
+
onValueChange={setRange}
|
|
3234
3307
|
fromDate={new Date(2020, 0, 1)}
|
|
3235
3308
|
toDate={new Date(2030, 11, 31)}
|
|
3236
3309
|
/>
|
|
@@ -3386,7 +3459,7 @@ function RegionPicker() {
|
|
|
3386
3459
|
<Cascader
|
|
3387
3460
|
options={REGIONS}
|
|
3388
3461
|
value={path}
|
|
3389
|
-
|
|
3462
|
+
onValueChange={(v) => setPath(v as string[])}
|
|
3390
3463
|
showSearch
|
|
3391
3464
|
placeholder="Select region\u2026"
|
|
3392
3465
|
/>
|
|
@@ -3402,7 +3475,7 @@ function MultiRegionPicker() {
|
|
|
3402
3475
|
options={REGIONS}
|
|
3403
3476
|
multiple
|
|
3404
3477
|
value={paths}
|
|
3405
|
-
|
|
3478
|
+
onValueChange={(v) => setPaths(v as string[][])}
|
|
3406
3479
|
showSearch
|
|
3407
3480
|
/>
|
|
3408
3481
|
);
|
|
@@ -3419,7 +3492,7 @@ function MultiRegionPicker() {
|
|
|
3419
3492
|
<Cascader
|
|
3420
3493
|
options={REGIONS}
|
|
3421
3494
|
changeOnSelect
|
|
3422
|
-
|
|
3495
|
+
onValueChange={(v) => console.log("path", v)}
|
|
3423
3496
|
/>
|
|
3424
3497
|
\`}`,
|
|
3425
3498
|
storyPath: "data-entry/Cascader.stories.tsx",
|
|
@@ -3582,7 +3655,7 @@ export function AccountPicker() {
|
|
|
3582
3655
|
id="account-picker"
|
|
3583
3656
|
treeData={accountTree}
|
|
3584
3657
|
value={account}
|
|
3585
|
-
|
|
3658
|
+
onValueChange={(v) => setAccount(v as string | undefined)}
|
|
3586
3659
|
showSearch
|
|
3587
3660
|
treeDefaultExpandAll
|
|
3588
3661
|
placeholder="Select account\u2026"
|
|
@@ -3600,7 +3673,7 @@ export function DepartmentFilter() {
|
|
|
3600
3673
|
id="dept-filter"
|
|
3601
3674
|
treeData={accountTree}
|
|
3602
3675
|
value={selected}
|
|
3603
|
-
|
|
3676
|
+
onValueChange={(v) => setSelected(v as string[])}
|
|
3604
3677
|
treeCheckable
|
|
3605
3678
|
showCheckedStrategy={TreeSelect.SHOW_PARENT}
|
|
3606
3679
|
showSearch
|
|
@@ -3673,7 +3746,7 @@ export function DepartmentFilter() {
|
|
|
3673
3746
|
}
|
|
3674
3747
|
],
|
|
3675
3748
|
usage: [
|
|
3676
|
-
"DO own `targetKeys` in state and update it inside `onChange`: `const [targetKeys, setTargetKeys] = useState<string[]>([]);
|
|
3749
|
+
"DO own `targetKeys` in state and update it inside `onChange`: `const [targetKeys, setTargetKeys] = useState<string[]>([]); onValueChange={(next) => setTargetKeys(next)}`.",
|
|
3677
3750
|
"DO NOT hand-roll a two-panel checkbox picker \u2014 Transfer ships the full shuttle UX (select-all header, indeterminate state, search, move buttons, empty state) out of the box.",
|
|
3678
3751
|
"DO enable `showSearch` for lists longer than ~10 items; the built-in SearchInput filters by both `title` and `description` text content, including ReactNode content via `reactNodeText`.",
|
|
3679
3752
|
"DO use `oneWay={true}` for append-only flows (e.g. adding permissions to a role) where items must never be moved back.",
|
|
@@ -3712,7 +3785,7 @@ export function AccountMapping() {
|
|
|
3712
3785
|
<Transfer
|
|
3713
3786
|
dataSource={ALL_ACCOUNTS}
|
|
3714
3787
|
targetKeys={targetKeys}
|
|
3715
|
-
|
|
3788
|
+
onValueChange={(nextKeys) => setTargetKeys(nextKeys)}
|
|
3716
3789
|
titles={["Available Accounts", "Mapped Accounts"]}
|
|
3717
3790
|
showSearch
|
|
3718
3791
|
/>
|
|
@@ -3842,7 +3915,7 @@ export function AvatarUploadForm() {
|
|
|
3842
3915
|
<Upload
|
|
3843
3916
|
variant="avatar-crop"
|
|
3844
3917
|
value={items}
|
|
3845
|
-
|
|
3918
|
+
onValueChange={setItems}
|
|
3846
3919
|
onUpload={handleUpload}
|
|
3847
3920
|
maxSizeBytes={5 * 1024 * 1024}
|
|
3848
3921
|
/>
|
|
@@ -3859,7 +3932,7 @@ export function DocumentUploadDropzone() {
|
|
|
3859
3932
|
<Upload
|
|
3860
3933
|
variant="dropzone"
|
|
3861
3934
|
value={items}
|
|
3862
|
-
|
|
3935
|
+
onValueChange={setItems}
|
|
3863
3936
|
accept=".pdf,.xlsx"
|
|
3864
3937
|
maxCount={10}
|
|
3865
3938
|
maxSizeBytes={20 * 1024 * 1024}
|
|
@@ -3948,7 +4021,7 @@ export function AvatarField() {
|
|
|
3948
4021
|
|
|
3949
4022
|
return (
|
|
3950
4023
|
<>
|
|
3951
|
-
<input type="file" accept="image/*"
|
|
4024
|
+
<input type="file" accept="image/*" onValueChange={handleFileChange} />
|
|
3952
4025
|
<UploadCropDialog
|
|
3953
4026
|
open={cropFile !== null}
|
|
3954
4027
|
onOpenChange={(open) => { if (!open) setCropFile(null); }}
|
|
@@ -4004,7 +4077,7 @@ export function AvatarField() {
|
|
|
4004
4077
|
}
|
|
4005
4078
|
],
|
|
4006
4079
|
usage: [
|
|
4007
|
-
"DO wrap in FormField when a label or validation message is needed \u2014 pass the same id to both FormField and ColorPicker so htmlFor wires up correctly: `<FormField id='brand' label='Brand color'><ColorPicker id='brand' value={v}
|
|
4080
|
+
"DO wrap in FormField when a label or validation message is needed \u2014 pass the same id to both FormField and ColorPicker so htmlFor wires up correctly: `<FormField id='brand' label='Brand color'><ColorPicker id='brand' value={v} onValueChange={setV} /></FormField>`.",
|
|
4008
4081
|
"DO use controlled mode (value + onChange) \u2014 there is no defaultValue/uncontrolled path; always supply value.",
|
|
4009
4082
|
"DON'T pass an invalid or empty string to value \u2014 the component will flash the invalid color on the preview swatch. Always initialize state to a valid 3- or 6-digit hex (e.g. '#2563eb').",
|
|
4010
4083
|
"The hex Input is a live draft field \u2014 onChange is NOT called until the user presses Enter or blurs; only then is the value validated and the parent notified. Do not rely on onChange firing on every keystroke.",
|
|
@@ -4034,7 +4107,7 @@ export function BrandColorField() {
|
|
|
4034
4107
|
<ColorPicker
|
|
4035
4108
|
id="brand-color"
|
|
4036
4109
|
value={color}
|
|
4037
|
-
|
|
4110
|
+
onValueChange={setColor}
|
|
4038
4111
|
/>
|
|
4039
4112
|
</FormField>
|
|
4040
4113
|
);
|
|
@@ -4043,7 +4116,7 @@ export function BrandColorField() {
|
|
|
4043
4116
|
// Compact swatch-only variant (no hex input)
|
|
4044
4117
|
export function SwatchOnly() {
|
|
4045
4118
|
const [color, setColor] = useState("#16a34a");
|
|
4046
|
-
return <ColorPicker value={color}
|
|
4119
|
+
return <ColorPicker value={color} onValueChange={setColor} showHexInput={false} />;
|
|
4047
4120
|
}
|
|
4048
4121
|
|
|
4049
4122
|
// Disabled state
|
|
@@ -4976,7 +5049,7 @@ export function ControlledExample() {
|
|
|
4976
5049
|
name="permissions"
|
|
4977
5050
|
options={PERMISSIONS}
|
|
4978
5051
|
value={selected}
|
|
4979
|
-
|
|
5052
|
+
onValueChange={setSelected}
|
|
4980
5053
|
/>
|
|
4981
5054
|
);
|
|
4982
5055
|
}`,
|
|
@@ -5213,7 +5286,7 @@ function LegacyAccountPicker({ value, onChange }) {
|
|
|
5213
5286
|
return (
|
|
5214
5287
|
<SearchSelect
|
|
5215
5288
|
value={value}
|
|
5216
|
-
|
|
5289
|
+
onValueChange={onChange}
|
|
5217
5290
|
options={[
|
|
5218
5291
|
{ value: "acc-001", label: "Cash", sublabel: "Current assets", group: "Assets" },
|
|
5219
5292
|
{ value: "acc-002", label: "Accounts Receivable", group: "Assets" },
|
|
@@ -5237,7 +5310,7 @@ function LegacyVendorPicker({ value, currentVendorName, onChange }) {
|
|
|
5237
5310
|
return (
|
|
5238
5311
|
<SearchSelect
|
|
5239
5312
|
value={value}
|
|
5240
|
-
|
|
5313
|
+
onValueChange={onChange}
|
|
5241
5314
|
loadOptions={fetchVendors}
|
|
5242
5315
|
selectedLabel={currentVendorName}
|
|
5243
5316
|
placeholder="Select vendor"
|
|
@@ -5326,7 +5399,7 @@ import { Autocomplete } from "@godxjp/ui/data-entry";
|
|
|
5326
5399
|
|
|
5327
5400
|
// \u2705 Replace with:
|
|
5328
5401
|
// import { Select } from "@godxjp/ui/data-entry";
|
|
5329
|
-
// <Select options={options} showSearch placeholder="Search\u2026"
|
|
5402
|
+
// <Select options={options} showSearch placeholder="Search\u2026" onValueChange={setValue} value={value} />
|
|
5330
5403
|
|
|
5331
5404
|
// Legacy usage (backward compat only):
|
|
5332
5405
|
import { Autocomplete } from "@godxjp/ui/data-entry";
|
|
@@ -5840,10 +5913,10 @@ export function LegacyInvoiceHeader() {
|
|
|
5840
5913
|
],
|
|
5841
5914
|
useCases: [
|
|
5842
5915
|
"App shell / top-nav language switcher that persists the user's locale preference via AppProvider and localStorage without any extra state.",
|
|
5843
|
-
"Settings page 'Language' field where locale is part of a form submitted to the backend \u2014 use controlled mode: value={form.locale}
|
|
5916
|
+
"Settings page 'Language' field where locale is part of a form submitted to the backend \u2014 use controlled mode: value={form.locale} onValueChange={(v) => form.setLocale(v)}.",
|
|
5844
5917
|
"Onboarding wizard step that lets the user pick their language before the rest of the app is configured \u2014 mount with AppProvider persist={false} and a controlled value to keep state local to the wizard.",
|
|
5845
5918
|
"Admin user-profile form where locale is one of several preferences (alongside timezone and date/time format) \u2014 pair with TimezonePicker, DateFormatPicker, TimeFormatPicker under the same AppProvider.",
|
|
5846
|
-
"Storybook / test harness where AppProvider is not present \u2014 render in fully controlled mode: <LocalePicker value='en'
|
|
5919
|
+
"Storybook / test harness where AppProvider is not present \u2014 render in fully controlled mode: <LocalePicker value='en' onValueChange={fn} />.",
|
|
5847
5920
|
"Localization QA tool that cycles through locales programmatically \u2014 drive via controlled value to switch the UI language without user interaction."
|
|
5848
5921
|
],
|
|
5849
5922
|
related: [
|
|
@@ -5875,7 +5948,7 @@ export function LocaleField() {
|
|
|
5875
5948
|
return (
|
|
5876
5949
|
<div className="flex flex-col gap-1.5">
|
|
5877
5950
|
<label htmlFor="locale-picker">Language</label>
|
|
5878
|
-
<LocalePicker id="locale-picker" value={locale}
|
|
5951
|
+
<LocalePicker id="locale-picker" value={locale} onValueChange={setLocale} />
|
|
5879
5952
|
</div>
|
|
5880
5953
|
);
|
|
5881
5954
|
}\`}`,
|
|
@@ -5916,7 +5989,7 @@ export function LocaleField() {
|
|
|
5916
5989
|
],
|
|
5917
5990
|
usage: [
|
|
5918
5991
|
"DO: Wrap with <AppProvider> and omit value/onChange \u2014 the picker reads and writes context automatically. This is the canonical zero-prop usage: <TimezonePicker />.",
|
|
5919
|
-
"DO: Pass value + onChange for fully controlled standalone usage (e.g. a form field that posts the timezone string): <TimezonePicker value={tz}
|
|
5992
|
+
"DO: Pass value + onChange for fully controlled standalone usage (e.g. a form field that posts the timezone string): <TimezonePicker value={tz} onValueChange={setTz} />. AppProvider is not required in this mode.",
|
|
5920
5993
|
"DON'T: Omit BOTH AppProvider context AND controlled props \u2014 the component throws at runtime: 'TimezonePicker requires <AppProvider> or controlled value + onChange'.",
|
|
5921
5994
|
"DO: Pass options={['Asia/Tokyo', 'UTC']} to restrict the list. The current value is automatically prepended if it is missing from the list, so the picker never shows an empty/invalid selection.",
|
|
5922
5995
|
"DON'T: Hand-roll a timezone <select> or a custom combobox \u2014 TimezonePicker already handles locale-aware labels (translated city + GMT offset), the full IANA list, and ARIA semantics.",
|
|
@@ -5945,7 +6018,7 @@ export function TimezoneField() {
|
|
|
5945
6018
|
return (
|
|
5946
6019
|
<TimezonePicker
|
|
5947
6020
|
value={tz}
|
|
5948
|
-
|
|
6021
|
+
onValueChange={setTz}
|
|
5949
6022
|
options={["Asia/Tokyo", "Asia/Ho_Chi_Minh", "UTC"]}
|
|
5950
6023
|
/>
|
|
5951
6024
|
);
|
|
@@ -6053,7 +6126,7 @@ export function ExportDialog() {
|
|
|
6053
6126
|
return (
|
|
6054
6127
|
<div className="flex items-center gap-2">
|
|
6055
6128
|
<label htmlFor="export-fmt">Export date format</label>
|
|
6056
|
-
<DateFormatPicker id="export-fmt" value={fmt}
|
|
6129
|
+
<DateFormatPicker id="export-fmt" value={fmt} onValueChange={setFmt} />
|
|
6057
6130
|
</div>
|
|
6058
6131
|
);
|
|
6059
6132
|
}\`}`,
|
|
@@ -6093,7 +6166,7 @@ export function ExportDialog() {
|
|
|
6093
6166
|
],
|
|
6094
6167
|
usage: [
|
|
6095
6168
|
"DO use inside <AppProvider> with no extra props to let it read/write the global time-format automatically: <AppProvider defaultTimeFormat='24h'><TimeFormatPicker /></AppProvider>",
|
|
6096
|
-
"DO switch to fully controlled mode when you need to manage the value yourself \u2014 supply BOTH value and onChange, or the component will throw: <TimeFormatPicker value={fmt}
|
|
6169
|
+
"DO switch to fully controlled mode when you need to manage the value yourself \u2014 supply BOTH value and onChange, or the component will throw: <TimeFormatPicker value={fmt} onValueChange={setFmt} />",
|
|
6097
6170
|
"DON'T omit both AppProvider and controlled props \u2014 the component throws an Error at render time: 'TimeFormatPicker requires <AppProvider> or controlled value + onChange'. There is no silent fallback.",
|
|
6098
6171
|
"DON'T hand-roll a time-format <select> \u2014 the locale-aware labels (e.g. '24 gi\u1EDD' for vi, '24-hour' for en) are generated internally from the i18n layer; reinventing this loses those translations.",
|
|
6099
6172
|
"DO wire a <label htmlFor={id}> when using the id prop for accessibility; the SelectTrigger already sets aria-label from i18n but a visible label improves discoverability.",
|
|
@@ -6136,7 +6209,7 @@ export function SettingsForm() {
|
|
|
6136
6209
|
return (
|
|
6137
6210
|
<div>
|
|
6138
6211
|
<label htmlFor="time-fmt">Time format</label>
|
|
6139
|
-
<TimeFormatPicker id="time-fmt" value={fmt}
|
|
6212
|
+
<TimeFormatPicker id="time-fmt" value={fmt} onValueChange={setFmt} />
|
|
6140
6213
|
</div>
|
|
6141
6214
|
);
|
|
6142
6215
|
}\`}
|
|
@@ -6449,7 +6522,11 @@ export function InvoiceListHeader() {
|
|
|
6449
6522
|
group: "data-display",
|
|
6450
6523
|
tagline: "Radix Avatar wrapper with image and fallback slots for users, teams, and entities.",
|
|
6451
6524
|
props: [
|
|
6452
|
-
{
|
|
6525
|
+
{
|
|
6526
|
+
name: "children",
|
|
6527
|
+
type: "ReactNode",
|
|
6528
|
+
description: "Compose AvatarImage and AvatarFallback."
|
|
6529
|
+
},
|
|
6453
6530
|
{ name: "className", type: "string", description: "Extra classes on the avatar root." }
|
|
6454
6531
|
],
|
|
6455
6532
|
usage: [
|
|
@@ -6472,11 +6549,28 @@ export function InvoiceListHeader() {
|
|
|
6472
6549
|
group: "layout",
|
|
6473
6550
|
tagline: "Radix Separator wrapper for tokenized horizontal or vertical dividers.",
|
|
6474
6551
|
props: [
|
|
6475
|
-
{
|
|
6476
|
-
|
|
6552
|
+
{
|
|
6553
|
+
name: "orientation",
|
|
6554
|
+
type: '"horizontal" | "vertical"',
|
|
6555
|
+
defaultValue: '"horizontal"',
|
|
6556
|
+
description: "Divider direction."
|
|
6557
|
+
},
|
|
6558
|
+
{
|
|
6559
|
+
name: "decorative",
|
|
6560
|
+
type: "boolean",
|
|
6561
|
+
defaultValue: "true",
|
|
6562
|
+
description: "Whether the separator is decorative for assistive tech."
|
|
6563
|
+
}
|
|
6564
|
+
],
|
|
6565
|
+
usage: [
|
|
6566
|
+
"DO use Separator for section dividers instead of raw border divs.",
|
|
6567
|
+
"DO set orientation='vertical' only when the parent gives it a stable height."
|
|
6568
|
+
],
|
|
6569
|
+
useCases: [
|
|
6570
|
+
"Separating toolbar groups",
|
|
6571
|
+
"Dividing stacked page sections",
|
|
6572
|
+
"Vertical split between metadata groups"
|
|
6477
6573
|
],
|
|
6478
|
-
usage: ["DO use Separator for section dividers instead of raw border divs.", "DO set orientation='vertical' only when the parent gives it a stable height."],
|
|
6479
|
-
useCases: ["Separating toolbar groups", "Dividing stacked page sections", "Vertical split between metadata groups"],
|
|
6480
6574
|
related: ["Stack \u2014 use for vertical spacing without a visible rule."],
|
|
6481
6575
|
example: `import { Separator } from "@godxjp/ui/layout";
|
|
6482
6576
|
|
|
@@ -6491,8 +6585,15 @@ export function InvoiceListHeader() {
|
|
|
6491
6585
|
props: [
|
|
6492
6586
|
{ name: "className", type: "string", description: "Size and layout classes for the block." }
|
|
6493
6587
|
],
|
|
6494
|
-
usage: [
|
|
6495
|
-
|
|
6588
|
+
usage: [
|
|
6589
|
+
"DO use Skeleton for a custom block when SkeletonRows/Table/Card do not match the final layout.",
|
|
6590
|
+
"DON'T use a spinner overlay for skeletonable page content."
|
|
6591
|
+
],
|
|
6592
|
+
useCases: [
|
|
6593
|
+
"Single loading line",
|
|
6594
|
+
"Custom card media placeholder",
|
|
6595
|
+
"Inline metadata placeholder"
|
|
6596
|
+
],
|
|
6496
6597
|
related: ["SkeletonRows", "SkeletonTable", "SkeletonCard"],
|
|
6497
6598
|
example: `import { Skeleton } from "@godxjp/ui/feedback";
|
|
6498
6599
|
|
|
@@ -6506,11 +6607,28 @@ export function InvoiceListHeader() {
|
|
|
6506
6607
|
tagline: "Radix Toggle wrapper with default/outline variants and tokenized sizes.",
|
|
6507
6608
|
props: [
|
|
6508
6609
|
{ name: "pressed", type: "boolean", description: "Controlled pressed state." },
|
|
6509
|
-
{
|
|
6510
|
-
|
|
6511
|
-
|
|
6610
|
+
{
|
|
6611
|
+
name: "onPressedChange",
|
|
6612
|
+
type: "(pressed: boolean) => void",
|
|
6613
|
+
description: "Pressed-state callback."
|
|
6614
|
+
},
|
|
6615
|
+
{
|
|
6616
|
+
name: "variant",
|
|
6617
|
+
type: '"default" | "outline"',
|
|
6618
|
+
defaultValue: '"default"',
|
|
6619
|
+
description: "Visual style."
|
|
6620
|
+
},
|
|
6621
|
+
{
|
|
6622
|
+
name: "size",
|
|
6623
|
+
type: '"sm" | "default" | "lg"',
|
|
6624
|
+
defaultValue: '"default"',
|
|
6625
|
+
description: "Control size."
|
|
6626
|
+
}
|
|
6627
|
+
],
|
|
6628
|
+
usage: [
|
|
6629
|
+
"DO provide an accessible label when the toggle only contains an icon.",
|
|
6630
|
+
"DON'T use Toggle for multi-option selection; use ToggleGroup."
|
|
6512
6631
|
],
|
|
6513
|
-
usage: ["DO provide an accessible label when the toggle only contains an icon.", "DON'T use Toggle for multi-option selection; use ToggleGroup."],
|
|
6514
6632
|
useCases: ["Bold/italic toolbar buttons", "Pinned filter toggles", "Compact view mode buttons"],
|
|
6515
6633
|
related: ["ToggleGroup", "Button"],
|
|
6516
6634
|
example: `import { Toggle } from "@godxjp/ui/data-entry";
|
|
@@ -6524,11 +6642,23 @@ export function InvoiceListHeader() {
|
|
|
6524
6642
|
group: "data-entry",
|
|
6525
6643
|
tagline: "Radix ToggleGroup wrapper for single or multiple toggle selection.",
|
|
6526
6644
|
props: [
|
|
6527
|
-
{
|
|
6645
|
+
{
|
|
6646
|
+
name: "type",
|
|
6647
|
+
type: '"single" | "multiple"',
|
|
6648
|
+
required: true,
|
|
6649
|
+
description: "Selection mode."
|
|
6650
|
+
},
|
|
6528
6651
|
{ name: "value", type: "string | string[]", description: "Controlled selected value(s)." },
|
|
6529
|
-
{
|
|
6652
|
+
{
|
|
6653
|
+
name: "onValueChange",
|
|
6654
|
+
type: "(value: string | string[]) => void",
|
|
6655
|
+
description: "Selection callback."
|
|
6656
|
+
}
|
|
6657
|
+
],
|
|
6658
|
+
usage: [
|
|
6659
|
+
"DO choose type='single' for mutually exclusive toolbar modes.",
|
|
6660
|
+
"DO choose type='multiple' for independent formatting toggles."
|
|
6530
6661
|
],
|
|
6531
|
-
usage: ["DO choose type='single' for mutually exclusive toolbar modes.", "DO choose type='multiple' for independent formatting toggles."],
|
|
6532
6662
|
useCases: ["Text alignment selector", "Formatting toolbar", "View density switcher"],
|
|
6533
6663
|
related: ["Toggle", "RadioGroup"],
|
|
6534
6664
|
example: `import { ToggleGroup, ToggleGroupItem } from "@godxjp/ui/data-entry";
|
|
@@ -6544,10 +6674,18 @@ export function InvoiceListHeader() {
|
|
|
6544
6674
|
group: "layout",
|
|
6545
6675
|
tagline: "Radix AspectRatio wrapper for stable media and preview frames.",
|
|
6546
6676
|
props: [
|
|
6547
|
-
{
|
|
6677
|
+
{
|
|
6678
|
+
name: "ratio",
|
|
6679
|
+
type: "number",
|
|
6680
|
+
defaultValue: "16 / 9",
|
|
6681
|
+
description: "Width divided by height."
|
|
6682
|
+
},
|
|
6548
6683
|
{ name: "children", type: "ReactNode", description: "Content constrained to the ratio." }
|
|
6549
6684
|
],
|
|
6550
|
-
usage: [
|
|
6685
|
+
usage: [
|
|
6686
|
+
"DO use AspectRatio for media, maps, charts, or previews that must not jump during load.",
|
|
6687
|
+
"DON'T use it for unconstrained text content."
|
|
6688
|
+
],
|
|
6551
6689
|
useCases: ["Video embed frame", "Image preview slot", "Dashboard chart placeholder"],
|
|
6552
6690
|
related: ["CardCover", "Skeleton"],
|
|
6553
6691
|
example: `import { AspectRatio } from "@godxjp/ui/layout";
|
|
@@ -6555,6 +6693,229 @@ export function InvoiceListHeader() {
|
|
|
6555
6693
|
<AspectRatio ratio={16 / 9}>...</AspectRatio>`,
|
|
6556
6694
|
storyPath: "layout/AspectRatio.stories.tsx",
|
|
6557
6695
|
rules: [2, 3]
|
|
6696
|
+
},
|
|
6697
|
+
{
|
|
6698
|
+
name: "Accordion",
|
|
6699
|
+
group: "data-display",
|
|
6700
|
+
tagline: "Radix accordion \u2014 vertically stacked, collapsible sections. Compose Accordion > AccordionItem > AccordionTrigger + AccordionContent.",
|
|
6701
|
+
props: [
|
|
6702
|
+
{
|
|
6703
|
+
name: "type",
|
|
6704
|
+
type: '"single" | "multiple"',
|
|
6705
|
+
required: true,
|
|
6706
|
+
description: "single = one open at a time; multiple = independent."
|
|
6707
|
+
},
|
|
6708
|
+
{
|
|
6709
|
+
name: "collapsible",
|
|
6710
|
+
type: "boolean",
|
|
6711
|
+
description: "When type=single, allow closing the open item."
|
|
6712
|
+
},
|
|
6713
|
+
{ name: "value", type: "string | string[]", description: "Controlled open item(s)." },
|
|
6714
|
+
{
|
|
6715
|
+
name: "defaultValue",
|
|
6716
|
+
type: "string | string[]",
|
|
6717
|
+
description: "Uncontrolled initial open item(s)."
|
|
6718
|
+
},
|
|
6719
|
+
{
|
|
6720
|
+
name: "onValueChange",
|
|
6721
|
+
type: "(value: string | string[]) => void",
|
|
6722
|
+
description: "Open-state callback."
|
|
6723
|
+
}
|
|
6724
|
+
],
|
|
6725
|
+
usage: [
|
|
6726
|
+
'DO compose the full set: <Accordion type="single" collapsible><AccordionItem value="a"><AccordionTrigger/><AccordionContent/></AccordionItem></Accordion>.',
|
|
6727
|
+
"DO give each AccordionItem a unique `value`.",
|
|
6728
|
+
"DON'T use it for primary navigation \u2014 that's Sidebar/Tabs. Accordion is for collapsible content/FAQ."
|
|
6729
|
+
],
|
|
6730
|
+
useCases: [
|
|
6731
|
+
"FAQ lists",
|
|
6732
|
+
"Grouped settings sections",
|
|
6733
|
+
"Collapsible detail panels on a record page",
|
|
6734
|
+
"Filter facet groups in a sidebar"
|
|
6735
|
+
],
|
|
6736
|
+
related: [
|
|
6737
|
+
"Collapsible (single open/close region, no item set)",
|
|
6738
|
+
"Tabs (mutually-exclusive views, always one visible)"
|
|
6739
|
+
],
|
|
6740
|
+
example: `import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@godxjp/ui/data-display";
|
|
6741
|
+
|
|
6742
|
+
<Accordion type="single" collapsible>
|
|
6743
|
+
<AccordionItem value="ship">
|
|
6744
|
+
<AccordionTrigger>\u914D\u9001\u306B\u3064\u3044\u3066</AccordionTrigger>
|
|
6745
|
+
<AccordionContent>3\u301C5\u55B6\u696D\u65E5\u3067\u304A\u5C4A\u3051\u3057\u307E\u3059\u3002</AccordionContent>
|
|
6746
|
+
</AccordionItem>
|
|
6747
|
+
</Accordion>`,
|
|
6748
|
+
storyPath: "data-display/Accordion.stories.tsx",
|
|
6749
|
+
rules: [3, 6]
|
|
6750
|
+
},
|
|
6751
|
+
{
|
|
6752
|
+
name: "HoverCard",
|
|
6753
|
+
group: "data-display",
|
|
6754
|
+
tagline: "Radix hover card \u2014 a rich popover shown on hover/focus of a trigger (for sighted-pointer affordances; not a replacement for Tooltip's short text).",
|
|
6755
|
+
props: [
|
|
6756
|
+
{
|
|
6757
|
+
name: "openDelay",
|
|
6758
|
+
type: "number",
|
|
6759
|
+
defaultValue: "700",
|
|
6760
|
+
description: "ms before opening on hover."
|
|
6761
|
+
},
|
|
6762
|
+
{
|
|
6763
|
+
name: "closeDelay",
|
|
6764
|
+
type: "number",
|
|
6765
|
+
defaultValue: "300",
|
|
6766
|
+
description: "ms before closing."
|
|
6767
|
+
},
|
|
6768
|
+
{ name: "open", type: "boolean", description: "Controlled open state." },
|
|
6769
|
+
{
|
|
6770
|
+
name: "onOpenChange",
|
|
6771
|
+
type: "(open: boolean) => void",
|
|
6772
|
+
description: "Open-state callback."
|
|
6773
|
+
}
|
|
6774
|
+
],
|
|
6775
|
+
usage: [
|
|
6776
|
+
"DO compose HoverCard > HoverCardTrigger > HoverCardContent.",
|
|
6777
|
+
"DO use for RICH preview content (a card, avatar + bio); for short plain-text hints use Tooltip.",
|
|
6778
|
+
"DON'T rely on it for essential info \u2014 hover isn't available on touch; provide the same content on click/tap elsewhere."
|
|
6779
|
+
],
|
|
6780
|
+
useCases: [
|
|
6781
|
+
"User/profile preview on @mention hover",
|
|
6782
|
+
"Entity preview (customer/account) on a table cell",
|
|
6783
|
+
"Glossary term definitions",
|
|
6784
|
+
"Commit/PR preview links"
|
|
6785
|
+
],
|
|
6786
|
+
related: [
|
|
6787
|
+
"Tooltip (short text label, not rich content)",
|
|
6788
|
+
"Popover (click-triggered, interactive content)"
|
|
6789
|
+
],
|
|
6790
|
+
example: `import { HoverCard, HoverCardTrigger, HoverCardContent } from "@godxjp/ui/data-display";
|
|
6791
|
+
|
|
6792
|
+
<HoverCard>
|
|
6793
|
+
<HoverCardTrigger>@yamada</HoverCardTrigger>
|
|
6794
|
+
<HoverCardContent>\u5C71\u7530\u592A\u90CE \u2014 \u7D4C\u7406\u90E8</HoverCardContent>
|
|
6795
|
+
</HoverCard>`,
|
|
6796
|
+
storyPath: "data-display/HoverCard.stories.tsx",
|
|
6797
|
+
rules: [3, 6]
|
|
6798
|
+
},
|
|
6799
|
+
{
|
|
6800
|
+
name: "PasswordInput",
|
|
6801
|
+
group: "data-entry",
|
|
6802
|
+
tagline: "Input for passwords with a built-in show/hide eye toggle. Accepts all Input props except `type`.",
|
|
6803
|
+
props: [
|
|
6804
|
+
{
|
|
6805
|
+
name: "value",
|
|
6806
|
+
type: "string",
|
|
6807
|
+
description: "Controlled value (or use defaultValue/uncontrolled)."
|
|
6808
|
+
},
|
|
6809
|
+
{ name: "name", type: "string", description: "Form field name for native submission." },
|
|
6810
|
+
{ name: "placeholder", type: "string", description: "Placeholder text." },
|
|
6811
|
+
{ name: "disabled", type: "boolean", description: "Disables the field + toggle." }
|
|
6812
|
+
],
|
|
6813
|
+
usage: [
|
|
6814
|
+
'DO use for any password / secret field instead of `<Input type="password">` so users get the show/hide affordance.',
|
|
6815
|
+
'DO pass `name` + `autoComplete="current-password"|"new-password"` for correct form/password-manager behavior.',
|
|
6816
|
+
"DON'T add your own eye button \u2014 it's built in (and excluded from the tab order)."
|
|
6817
|
+
],
|
|
6818
|
+
useCases: [
|
|
6819
|
+
"Login password field",
|
|
6820
|
+
"Sign-up / change-password forms (new-password)",
|
|
6821
|
+
"API key / secret entry in settings"
|
|
6822
|
+
],
|
|
6823
|
+
related: [
|
|
6824
|
+
"Input (the base text field this wraps)",
|
|
6825
|
+
"FormField (label + error wrapper around it)"
|
|
6826
|
+
],
|
|
6827
|
+
example: `import { PasswordInput } from "@godxjp/ui/data-entry";
|
|
6828
|
+
|
|
6829
|
+
<PasswordInput name="password" autoComplete="current-password" placeholder="\u30D1\u30B9\u30EF\u30FC\u30C9" />`,
|
|
6830
|
+
storyPath: "data-entry/PasswordInput.stories.tsx",
|
|
6831
|
+
rules: [3, 6]
|
|
6832
|
+
},
|
|
6833
|
+
{
|
|
6834
|
+
name: "Drawer",
|
|
6835
|
+
group: "feedback",
|
|
6836
|
+
tagline: "Bottom-sheet (vaul) \u2014 a draggable sheet that slides up from the screen edge. DISTINCT from Sheet (the side panel); use Drawer for mobile/touch bottom sheets.",
|
|
6837
|
+
props: [
|
|
6838
|
+
{ name: "open", type: "boolean", description: "Controlled open state." },
|
|
6839
|
+
{
|
|
6840
|
+
name: "onOpenChange",
|
|
6841
|
+
type: "(open: boolean) => void",
|
|
6842
|
+
description: "Open-state callback."
|
|
6843
|
+
},
|
|
6844
|
+
{
|
|
6845
|
+
name: "shouldScaleBackground",
|
|
6846
|
+
type: "boolean",
|
|
6847
|
+
defaultValue: "true",
|
|
6848
|
+
description: "Scale the page behind the sheet (iOS-style)."
|
|
6849
|
+
}
|
|
6850
|
+
],
|
|
6851
|
+
usage: [
|
|
6852
|
+
"DO compose Drawer > DrawerTrigger > DrawerContent (> DrawerHeader/DrawerTitle + body + DrawerFooter).",
|
|
6853
|
+
"DO use Drawer for mobile/touch bottom sheets; use Sheet for a desktop side panel and Dialog for a centered modal.",
|
|
6854
|
+
"DON'T confuse with Sheet \u2014 Sheet slides from a side edge, Drawer is the draggable bottom sheet."
|
|
6855
|
+
],
|
|
6856
|
+
useCases: [
|
|
6857
|
+
"Mobile action sheet / menu",
|
|
6858
|
+
"Filter panel on small screens",
|
|
6859
|
+
"Quick-create form on touch",
|
|
6860
|
+
"Detail peek that drags up"
|
|
6861
|
+
],
|
|
6862
|
+
related: [
|
|
6863
|
+
"Sheet (side panel, same Radix Dialog base, different placement)",
|
|
6864
|
+
"Dialog (centered modal)"
|
|
6865
|
+
],
|
|
6866
|
+
example: `import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle } from "@godxjp/ui/feedback";
|
|
6867
|
+
|
|
6868
|
+
<Drawer>
|
|
6869
|
+
<DrawerTrigger>\u7D5E\u308A\u8FBC\u307F</DrawerTrigger>
|
|
6870
|
+
<DrawerContent>
|
|
6871
|
+
<DrawerHeader><DrawerTitle>\u7D5E\u308A\u8FBC\u307F</DrawerTitle></DrawerHeader>
|
|
6872
|
+
{/* filters */}
|
|
6873
|
+
</DrawerContent>
|
|
6874
|
+
</Drawer>`,
|
|
6875
|
+
storyPath: "feedback/Drawer.stories.tsx",
|
|
6876
|
+
rules: [3, 6, 24]
|
|
6877
|
+
},
|
|
6878
|
+
{
|
|
6879
|
+
name: "InputOTP",
|
|
6880
|
+
group: "data-entry",
|
|
6881
|
+
tagline: "One-time-code / 2FA input (input-otp) \u2014 N single-character slots that behave as one field. Compose InputOTP > InputOTPGroup > InputOTPSlot.",
|
|
6882
|
+
props: [
|
|
6883
|
+
{
|
|
6884
|
+
name: "maxLength",
|
|
6885
|
+
type: "number",
|
|
6886
|
+
required: true,
|
|
6887
|
+
description: "Number of slots (e.g. 6)."
|
|
6888
|
+
},
|
|
6889
|
+
{ name: "value", type: "string", description: "Controlled value." },
|
|
6890
|
+
{
|
|
6891
|
+
name: "onChange",
|
|
6892
|
+
type: "(value: string) => void",
|
|
6893
|
+
description: "Value callback (this is a true text input \u2014 onChange is the DOM-style value handler here)."
|
|
6894
|
+
},
|
|
6895
|
+
{ name: "pattern", type: "string", description: "Allowed-char regex (e.g. digits only)." }
|
|
6896
|
+
],
|
|
6897
|
+
usage: [
|
|
6898
|
+
"DO set `maxLength` to the code length and render that many InputOTPSlot with sequential `index`.",
|
|
6899
|
+
"DO wrap slots in InputOTPGroup; use InputOTPSeparator between groups (e.g. 3 + 3).",
|
|
6900
|
+
"DON'T build N separate Inputs \u2014 this is ONE field with paste, arrow-key, and caret handling built in."
|
|
6901
|
+
],
|
|
6902
|
+
useCases: [
|
|
6903
|
+
"2FA / OTP verification code",
|
|
6904
|
+
"Email / SMS confirmation code",
|
|
6905
|
+
"PIN entry",
|
|
6906
|
+
"Invite / redemption code"
|
|
6907
|
+
],
|
|
6908
|
+
related: ["Input (a normal single text field)", "PasswordInput (masked secret field)"],
|
|
6909
|
+
example: `import { InputOTP, InputOTPGroup, InputOTPSlot } from "@godxjp/ui/data-entry";
|
|
6910
|
+
|
|
6911
|
+
<InputOTP maxLength={6}>
|
|
6912
|
+
<InputOTPGroup>
|
|
6913
|
+
<InputOTPSlot index={0} /><InputOTPSlot index={1} /><InputOTPSlot index={2} />
|
|
6914
|
+
<InputOTPSlot index={3} /><InputOTPSlot index={4} /><InputOTPSlot index={5} />
|
|
6915
|
+
</InputOTPGroup>
|
|
6916
|
+
</InputOTP>`,
|
|
6917
|
+
storyPath: "data-entry/InputOTP.stories.tsx",
|
|
6918
|
+
rules: [3, 6]
|
|
6558
6919
|
}
|
|
6559
6920
|
];
|
|
6560
6921
|
function findComponent(name) {
|
|
@@ -6568,194 +6929,85 @@ function componentsByGroup(group) {
|
|
|
6568
6929
|
// src/data/prop-vocabulary.ts
|
|
6569
6930
|
var PROP_VOCABULARY = [
|
|
6570
6931
|
{
|
|
6571
|
-
name: "
|
|
6572
|
-
concept: "
|
|
6573
|
-
values: ["
|
|
6574
|
-
usedBy: [
|
|
6575
|
-
"InputSize",
|
|
6576
|
-
"CheckboxGroupSize",
|
|
6577
|
-
"ColorPickerSize",
|
|
6578
|
-
"MediaUploadSize",
|
|
6579
|
-
"ProgressSize",
|
|
6580
|
-
"RadioGroupSize",
|
|
6581
|
-
"RateSize",
|
|
6582
|
-
"TransferSize",
|
|
6583
|
-
"SegmentedControlSize (subset)",
|
|
6584
|
-
"SpaceSize (+ number)",
|
|
6585
|
-
"FlexGap (+ number)",
|
|
6586
|
-
"GridGap (+ number)",
|
|
6587
|
-
"MasonryGap (+ number)"
|
|
6588
|
-
]
|
|
6589
|
-
},
|
|
6590
|
-
{
|
|
6591
|
-
name: "SizeWithXSProp",
|
|
6592
|
-
concept: 'Extension of `SizeProp` with `"x-small"` for compact icon-bar / table-row contexts.',
|
|
6593
|
-
values: ["x-small", "small", "default", "large"],
|
|
6594
|
-
usedBy: ["ButtonSize"]
|
|
6932
|
+
name: "ValueProp<T = string>",
|
|
6933
|
+
concept: "Abstract controlled value.",
|
|
6934
|
+
values: ["generic"],
|
|
6935
|
+
usedBy: ["CheckboxGroup", "Upload", "Cascader", "TreeSelect", "Tabs", "SearchSelect"]
|
|
6595
6936
|
},
|
|
6596
6937
|
{
|
|
6597
|
-
name: "
|
|
6598
|
-
concept: "
|
|
6599
|
-
values: ["
|
|
6600
|
-
usedBy: ["
|
|
6938
|
+
name: "DefaultValueProp<T = string>",
|
|
6939
|
+
concept: "Abstract uncontrolled initial value.",
|
|
6940
|
+
values: ["generic"],
|
|
6941
|
+
usedBy: ["CheckboxGroup", "Upload", "Cascader", "TreeSelect", "Tabs"]
|
|
6601
6942
|
},
|
|
6602
6943
|
{
|
|
6603
|
-
name: "
|
|
6604
|
-
concept: "
|
|
6605
|
-
values: ["
|
|
6606
|
-
usedBy: ["
|
|
6607
|
-
notes: 'Form errors use `"error"` (not `"destructive"`) \u2014 different concern from destructive actions.'
|
|
6944
|
+
name: "OnValueChangeProp<T = string>",
|
|
6945
|
+
concept: "Callback for abstract value changes. DOM events continue to use onChange.",
|
|
6946
|
+
values: ["(value: T) => void"],
|
|
6947
|
+
usedBy: ["CheckboxGroup", "Upload", "Cascader", "TreeSelect", "Transfer", "settings pickers"]
|
|
6608
6948
|
},
|
|
6609
6949
|
{
|
|
6610
|
-
name: "
|
|
6611
|
-
concept: "
|
|
6612
|
-
values: ["
|
|
6613
|
-
usedBy: ["
|
|
6614
|
-
},
|
|
6615
|
-
{
|
|
6616
|
-
name: "HelpToneProp",
|
|
6617
|
-
concept: "Help-line / Alert colour ladder. Adds `info` + `warn` to StatusProp.",
|
|
6618
|
-
values: ["default", "info", "warn", "error", "success"],
|
|
6619
|
-
usedBy: ["FieldHelpTone"]
|
|
6620
|
-
},
|
|
6621
|
-
{
|
|
6622
|
-
name: "OrientationProp",
|
|
6623
|
-
concept: "Layout axis.",
|
|
6624
|
-
values: ["horizontal", "vertical"],
|
|
6625
|
-
usedBy: [
|
|
6626
|
-
"AnchorOrientation",
|
|
6627
|
-
"MenuOrientation",
|
|
6628
|
-
"RadioGroupOrientation",
|
|
6629
|
-
"CheckboxGroupOrientation",
|
|
6630
|
-
"SegmentedControlOrientation",
|
|
6631
|
-
"StepsOrientation",
|
|
6632
|
-
"TabsOrientation"
|
|
6633
|
-
]
|
|
6950
|
+
name: "OpenProp / DefaultOpenProp / OnOpenChangeProp",
|
|
6951
|
+
concept: "Disclosure state.",
|
|
6952
|
+
values: ["boolean", "(open: boolean) => void"],
|
|
6953
|
+
usedBy: ["Dialog", "Sheet", "Popover"]
|
|
6634
6954
|
},
|
|
6635
6955
|
{
|
|
6636
|
-
name: "
|
|
6637
|
-
concept: "
|
|
6638
|
-
values: ["
|
|
6639
|
-
usedBy: ["
|
|
6640
|
-
|
|
6641
|
-
{
|
|
6642
|
-
name: "SideProp",
|
|
6643
|
-
concept: "Edge a floating panel docks against.",
|
|
6644
|
-
values: ["top", "right", "bottom", "left"],
|
|
6645
|
-
usedBy: ["SheetSide", "TabsPlacement", "TableStickySide (subset)"]
|
|
6646
|
-
},
|
|
6647
|
-
{
|
|
6648
|
-
name: "PlacementProp",
|
|
6649
|
-
concept: "Extension of SideProp with a centred anchor (Tabs placement, Tour spotlight).",
|
|
6650
|
-
values: ["top", "right", "bottom", "left", "center"],
|
|
6651
|
-
usedBy: ["TourPlacement"]
|
|
6652
|
-
},
|
|
6653
|
-
{
|
|
6654
|
-
name: "PaddingProp",
|
|
6655
|
-
concept: "Outer gutter scale for surface containers.",
|
|
6656
|
-
values: ["tight", "default", "cozy", "none"],
|
|
6657
|
-
usedBy: ["CardPadding", "PageHeaderPadding", "PageContentPadding"]
|
|
6956
|
+
name: "SizeProp",
|
|
6957
|
+
concept: "Shared public size names.",
|
|
6958
|
+
values: ["xs", "sm", "md", "lg"],
|
|
6959
|
+
usedBy: ["Button", "Steps", "Switch"],
|
|
6960
|
+
notes: "Component-specific subsets must be documented. Old alias small is sm."
|
|
6658
6961
|
},
|
|
6659
6962
|
{
|
|
6660
|
-
name: "
|
|
6661
|
-
concept: "
|
|
6662
|
-
values: ["
|
|
6663
|
-
usedBy: ["
|
|
6963
|
+
name: "ToneProp",
|
|
6964
|
+
concept: "Semantic status/color intent.",
|
|
6965
|
+
values: ["default", "success", "warning", "destructive", "info", "muted", "neutral"],
|
|
6966
|
+
usedBy: ["Badge", "Alert"],
|
|
6967
|
+
notes: "Status values belong in tone, not variant."
|
|
6664
6968
|
},
|
|
6665
6969
|
{
|
|
6666
|
-
name: "
|
|
6667
|
-
concept: "
|
|
6668
|
-
values: ["
|
|
6669
|
-
usedBy: ["
|
|
6670
|
-
notes: "
|
|
6970
|
+
name: "GapProp",
|
|
6971
|
+
concept: "Shared layout gap scale.",
|
|
6972
|
+
values: ["xs", "sm", "md", "lg", "xl"],
|
|
6973
|
+
usedBy: ["Stack", "Inline"],
|
|
6974
|
+
notes: "Inline uses an Exclude<GapProp, 'xl'> subset."
|
|
6671
6975
|
},
|
|
6672
6976
|
{
|
|
6673
|
-
name: "
|
|
6674
|
-
concept: "
|
|
6675
|
-
values: ["
|
|
6676
|
-
usedBy: ["
|
|
6977
|
+
name: "TitleProp",
|
|
6978
|
+
concept: "Primary heading text.",
|
|
6979
|
+
values: ["React.ReactNode"],
|
|
6980
|
+
usedBy: ["PageContainer", "PageHeader", "EmptyState", "Dialog"]
|
|
6677
6981
|
},
|
|
6678
6982
|
{
|
|
6679
|
-
name: "
|
|
6680
|
-
concept: "
|
|
6681
|
-
values: ["
|
|
6682
|
-
usedBy: ["
|
|
6683
|
-
notes: 'Cascade: `<Form loading>` sets a default for every nested `<FormField>`. Per-field `loading` overrides Form\'s. `true` \u2192 spinner (default). `{ kind: "skeleton" }` \u2192 use for INITIAL fetch state. UX nuance: skeleton on init, spinner on save.'
|
|
6983
|
+
name: "DensityProp",
|
|
6984
|
+
concept: "Page/subtree density.",
|
|
6985
|
+
values: ["compact", "default", "comfortable"],
|
|
6986
|
+
usedBy: ["PageContainer"]
|
|
6684
6987
|
}
|
|
6685
6988
|
];
|
|
6686
6989
|
function findVocab(name) {
|
|
6687
|
-
const normalized = name.trim().toLowerCase().replace(/prop
|
|
6688
|
-
return PROP_VOCABULARY.find(
|
|
6689
|
-
(v) => v.name.toLowerCase().replace(/prop$/i, "") === normalized
|
|
6690
|
-
);
|
|
6990
|
+
const normalized = name.trim().toLowerCase().replace(/prop(?:<.*>)?$/i, "");
|
|
6991
|
+
return PROP_VOCABULARY.find((v) => v.name.toLowerCase().replace(/prop(?:<.*>)?$/i, "") === normalized);
|
|
6691
6992
|
}
|
|
6692
6993
|
|
|
6693
6994
|
// src/data/tokens.ts
|
|
6694
6995
|
var TOKENS = [
|
|
6695
|
-
|
|
6696
|
-
{ name: "--
|
|
6697
|
-
{ name: "--
|
|
6698
|
-
{ name: "--
|
|
6699
|
-
{ name: "--
|
|
6700
|
-
{ name: "--
|
|
6701
|
-
{ name: "--
|
|
6702
|
-
{ name: "--
|
|
6703
|
-
{ name: "--
|
|
6704
|
-
{ name: "--
|
|
6705
|
-
{ name: "--
|
|
6706
|
-
{ name: "--
|
|
6707
|
-
{ name: "--
|
|
6708
|
-
{ name: "--
|
|
6709
|
-
{ name: "--
|
|
6710
|
-
{ name: "--success", category: "color", role: "Success semantic slot" },
|
|
6711
|
-
{ name: "--warning", category: "color", role: "Warning semantic slot" },
|
|
6712
|
-
{ name: "--destructive", category: "color", role: "Danger / destructive action slot" },
|
|
6713
|
-
{ name: "--info", category: "color", role: "Info / neutral notice slot" },
|
|
6714
|
-
{ name: "--attention", category: "color", role: "Attention / non-destructive alert slot" },
|
|
6715
|
-
// Spacing — fixed scale
|
|
6716
|
-
{ name: "--spacing-1", category: "spacing", role: "4px", value: "0.25rem" },
|
|
6717
|
-
{ name: "--spacing-2", category: "spacing", role: "8px", value: "0.5rem" },
|
|
6718
|
-
{ name: "--spacing-3", category: "spacing", role: "12px", value: "0.75rem" },
|
|
6719
|
-
{ name: "--spacing-4", category: "spacing", role: "16px", value: "1rem" },
|
|
6720
|
-
{ name: "--spacing-5", category: "spacing", role: "20px", value: "1.25rem" },
|
|
6721
|
-
{ name: "--spacing-6", category: "spacing", role: "24px", value: "1.5rem" },
|
|
6722
|
-
{ name: "--spacing-8", category: "spacing", role: "32px", value: "2rem" },
|
|
6723
|
-
// Typography — fixed scale
|
|
6724
|
-
{ name: "--text-2xs", category: "typography", role: "10px", value: "0.625rem" },
|
|
6725
|
-
{ name: "--text-xs", category: "typography", role: "12px", value: "0.75rem" },
|
|
6726
|
-
{ name: "--text-sm", category: "typography", role: "14px", value: "0.875rem" },
|
|
6727
|
-
{ name: "--text-base", category: "typography", role: "16px", value: "1rem" },
|
|
6728
|
-
{ name: "--text-lg", category: "typography", role: "18px", value: "1.125rem" },
|
|
6729
|
-
{ name: "--text-xl", category: "typography", role: "20px", value: "1.25rem" },
|
|
6730
|
-
{ name: "--text-2xl", category: "typography", role: "24px", value: "1.5rem" },
|
|
6731
|
-
{ name: "--font-mono", category: "typography", role: "Monospace stack" },
|
|
6732
|
-
// Radius — fixed scale
|
|
6733
|
-
{ name: "--radius-sm", category: "radius", role: "Small (chips, inputs)", value: "0.25rem" },
|
|
6734
|
-
{ name: "--radius-md", category: "radius", role: "Medium (cards)", value: "0.5rem" },
|
|
6735
|
-
{ name: "--radius-lg", category: "radius", role: "Large (dialogs)", value: "0.75rem" },
|
|
6736
|
-
{ name: "--radius-full", category: "radius", role: "Pill / circle", value: "9999px" },
|
|
6737
|
-
// Breakpoints — mobile-first min-widths
|
|
6738
|
-
{ name: "--breakpoint-xs", category: "breakpoint", role: "Mobile-first base (\u22650px)", value: "0" },
|
|
6739
|
-
{ name: "--breakpoint-sm", category: "breakpoint", role: "Phone landscape / tablet portrait", value: "640px" },
|
|
6740
|
-
{ name: "--breakpoint-md", category: "breakpoint", role: "Tablet landscape", value: "768px" },
|
|
6741
|
-
{ name: "--breakpoint-lg", category: "breakpoint", role: "Laptop", value: "1024px" },
|
|
6742
|
-
{ name: "--breakpoint-xl", category: "breakpoint", role: "Desktop", value: "1280px" },
|
|
6743
|
-
{ name: "--breakpoint-xxl", category: "breakpoint", role: "Wide desktop", value: "1536px" },
|
|
6744
|
-
// Density — rebound by `data-density`
|
|
6745
|
-
{ name: "--density-element", category: "density", role: "Element height (Input/Button)", axis: "data-density" },
|
|
6746
|
-
{ name: "--density-element-sm", category: "density", role: "Small element", axis: "data-density" },
|
|
6747
|
-
{ name: "--density-element-lg", category: "density", role: "Large element", axis: "data-density" },
|
|
6748
|
-
{ name: "--density-card", category: "density", role: "Card padding", axis: "data-density" },
|
|
6749
|
-
{ name: "--density-page", category: "density", role: "Page (PageContent) padding", axis: "data-density" },
|
|
6750
|
-
{ name: "--density-section", category: "density", role: "Section padding (cozy variant)", axis: "data-density" },
|
|
6751
|
-
{ name: "--header-height", category: "density", role: "Topbar height", axis: "data-density" },
|
|
6752
|
-
{ name: "--sidebar-width", category: "density", role: "Sidebar width (expanded)", axis: "data-density" },
|
|
6753
|
-
{ name: "--sidebar-width-collapsed", category: "density", role: "Sidebar icon-only width", axis: "data-density" },
|
|
6754
|
-
{ name: "--touch-target-min", category: "density", role: "Mobile touch target (does NOT scale)", value: "44px" },
|
|
6755
|
-
// Motion — fixed timings
|
|
6756
|
-
{ name: "--transition-base", category: "motion", role: "Standard transition duration", value: "200ms" },
|
|
6757
|
-
{ name: "--ease-out", category: "motion", role: "Out easing curve", value: "cubic-bezier(0, 0, 0.2, 1)" },
|
|
6758
|
-
{ name: "--ease-in-out", category: "motion", role: "In-out easing", value: "cubic-bezier(0.4, 0, 0.2, 1)" }
|
|
6996
|
+
{ name: "--wa-*", category: "primitive", tier: "primitive", role: "Neutral decorative Japanese accent primitives for charts/tags/decoration only." },
|
|
6997
|
+
{ name: "--chart-1..6", category: "primitive", tier: "primitive", role: "Neutral decorative chart primitives; @theme chart colors reference these tokens." },
|
|
6998
|
+
{ name: "--space-0..12", category: "primitive", tier: "primitive", role: "Raw spacing scale." },
|
|
6999
|
+
{ name: "--font-size-*", category: "primitive", tier: "primitive", role: "Raw typography scale." },
|
|
7000
|
+
{ name: "--primary", category: "semantic", tier: "semantic", role: "Brand/action color role." },
|
|
7001
|
+
{ name: "--success", category: "semantic", tier: "semantic", role: "Success status role." },
|
|
7002
|
+
{ name: "--warning", category: "semantic", tier: "semantic", role: "Warning status role." },
|
|
7003
|
+
{ name: "--destructive", category: "semantic", tier: "semantic", role: "Destructive/error status role." },
|
|
7004
|
+
{ name: "--info", category: "semantic", tier: "semantic", role: "Information status role." },
|
|
7005
|
+
{ name: "--attention", category: "semantic", tier: "semantic", role: "Attention status role." },
|
|
7006
|
+
{ name: "--badge-space-*", category: "component", tier: "component", role: "Badge spacing." },
|
|
7007
|
+
{ name: "--card-*", category: "component", tier: "component", role: "Card surface, border, spacing, and typography." },
|
|
7008
|
+
{ name: "--control-*", category: "component", tier: "component", role: "Shared form control heights, padding, icons, and focus chrome." },
|
|
7009
|
+
{ name: "--table-*", category: "component", tier: "component", role: "Table row/cell sizing." },
|
|
7010
|
+
{ name: "--dialog-* / --alert-* / --skeleton-*", category: "component", tier: "component", role: "Feedback component sizing and spacing." }
|
|
6759
7011
|
];
|
|
6760
7012
|
function tokensByCategory(category) {
|
|
6761
7013
|
return TOKENS.filter((t) => t.category === category);
|
|
@@ -6839,7 +7091,7 @@ var PATTERNS = [
|
|
|
6839
7091
|
// 2) Badge renders grey with a \u25CB (no colour) for localized/tier labels
|
|
6840
7092
|
// Cause: it auto-maps only English lifecycle keys. (@godxjp/ui >= 6.1)
|
|
6841
7093
|
// \u274C <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" />
|
|
6842
|
-
// \u2705 <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0"
|
|
7094
|
+
// \u2705 <Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" tone="success" icon={null} /> // tier \u2192 pill, no icon
|
|
6843
7095
|
// \u2705 <Badge status="active">\u516C\u958B\u4E2D</Badge> // lifecycle \u2192 keep icon
|
|
6844
7096
|
|
|
6845
7097
|
// 3) Table text collapses to one char per line, or a chip wraps
|
|
@@ -6990,11 +7242,11 @@ export function DeleteProjectDialog({ open, onOpenChange, slug }: { open: boolea
|
|
|
6990
7242
|
<DialogDescription>\u3053\u306E\u64CD\u4F5C\u306F\u53D6\u308A\u6D88\u305B\u307E\u305B\u3093\u3002\u78BA\u8A8D\u306E\u305F\u3081\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D "{slug}" \u3068\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002</DialogDescription>
|
|
6991
7243
|
</DialogHeader>
|
|
6992
7244
|
<Stack gap="md">
|
|
6993
|
-
<Input value={confirm}
|
|
7245
|
+
<Input value={confirm} onValueChange={(e) => setConfirm(e.target.value)} placeholder={slug} />
|
|
6994
7246
|
</Stack>
|
|
6995
7247
|
<DialogFooter>
|
|
6996
7248
|
<Button variant="outline" onClick={() => onOpenChange(false)}>\u30AD\u30E3\u30F3\u30BB\u30EB</Button>
|
|
6997
|
-
<Button
|
|
7249
|
+
<Button tone="destructive" disabled={confirm !== slug} onClick={() => { toast.success("\u524A\u9664\u3057\u307E\u3057\u305F"); onOpenChange(false); }}>\u5B8C\u5168\u306B\u524A\u9664</Button>
|
|
6998
7250
|
</DialogFooter>
|
|
6999
7251
|
</DialogContent>
|
|
7000
7252
|
</Dialog>
|
|
@@ -7060,7 +7312,7 @@ function Coupons({ coupons }: { coupons: Coupon[] }) {
|
|
|
7060
7312
|
// ColumnDef = { key, header, render?, align?: "left"|"center"|"right", sortable?, width? }
|
|
7061
7313
|
const columns: ColumnDef<Coupon>[] = [
|
|
7062
7314
|
{ key: "name", header: "\u30AF\u30FC\u30DD\u30F3\u540D", render: (c) => <span className="font-medium">{c.name}</span> },
|
|
7063
|
-
{ key: "scope", header: "\u30B9\u30B3\u30FC\u30D7", render: (c) => <Badge status={c.scope}
|
|
7315
|
+
{ key: "scope", header: "\u30B9\u30B3\u30FC\u30D7", render: (c) => <Badge status={c.scope} tone="info" icon={null} /> },
|
|
7064
7316
|
{ key: "status", header: "\u30B9\u30C6\u30FC\u30BF\u30B9", render: (c) => <Badge status={c.status} /> },
|
|
7065
7317
|
{ key: "valid", header: "\u6709\u52B9\u671F\u9593", render: (c) => \`\${formatDate(c.validFrom)} \u301C \${formatDate(c.validTo)}\` },
|
|
7066
7318
|
{ key: "usage", header: "\u5229\u7528\u6570", align: "right", render: (c) => c.usage.toLocaleString() },
|
|
@@ -7102,7 +7354,7 @@ function Coupons({ coupons }: { coupons: Coupon[] }) {
|
|
|
7102
7354
|
</Card>
|
|
7103
7355
|
|
|
7104
7356
|
{filtered.length > PAGE_SIZE && (
|
|
7105
|
-
<Pagination current={page} total={filtered.length} pageSize={PAGE_SIZE} showTotal
|
|
7357
|
+
<Pagination current={page} total={filtered.length} pageSize={PAGE_SIZE} showTotal onValueChange={(p) => setPage(p)} />
|
|
7106
7358
|
)}
|
|
7107
7359
|
</Stack>
|
|
7108
7360
|
</PageContainer>
|
|
@@ -7158,7 +7410,7 @@ function MemberShow({ id }: { id: string }) {
|
|
|
7158
7410
|
{/* Descriptions is COMPOUND \u2014 value goes in children, not a prop */}
|
|
7159
7411
|
<Descriptions columns={2}>
|
|
7160
7412
|
<Descriptions.Item label="\u6C0F\u540D">{member.name}</Descriptions.Item>
|
|
7161
|
-
<Descriptions.Item label="\u30E9\u30F3\u30AF"><Badge status={member.rank}
|
|
7413
|
+
<Descriptions.Item label="\u30E9\u30F3\u30AF"><Badge status={member.rank} tone="info" icon={null} /></Descriptions.Item>
|
|
7162
7414
|
<Descriptions.Item label="\u30B9\u30C6\u30FC\u30BF\u30B9"><Badge status={member.status} /></Descriptions.Item>
|
|
7163
7415
|
<Descriptions.Item label="\u767B\u9332\u65E5">{formatDate(member.registeredAt)}</Descriptions.Item>
|
|
7164
7416
|
</Descriptions>
|
|
@@ -7221,12 +7473,12 @@ const seeded = (n: number) => { const x = Math.sin((n + 1) * 99.71) * 1e4; retur
|
|
|
7221
7473
|
<Badge status="active">\u516C\u958B\u4E2D</Badge> // green \u2713 \u516C\u958B\u4E2D
|
|
7222
7474
|
|
|
7223
7475
|
// 2) Unknown label \u2014 set tone explicitly (no icon, since the key is unknown):
|
|
7224
|
-
<Badge status="\u516C\u958B\u4E2D"
|
|
7476
|
+
<Badge status="\u516C\u958B\u4E2D" tone="success" />
|
|
7225
7477
|
|
|
7226
7478
|
// 3) Tier / category \u2014 coloured pill, drop the misleading glyph with icon={null}:
|
|
7227
|
-
<Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0"
|
|
7228
|
-
<Badge status="\u30B4\u30FC\u30EB\u30C9"
|
|
7229
|
-
<Badge status="\u6CD5\u4EBA\u5171\u901A"
|
|
7479
|
+
<Badge status="\u30D7\u30EC\u30DF\u30A2\u30E0" tone="success" icon={null} />
|
|
7480
|
+
<Badge status="\u30B4\u30FC\u30EB\u30C9" tone="warning" icon={null} />
|
|
7481
|
+
<Badge status="\u6CD5\u4EBA\u5171\u901A" tone="info" icon={null} />
|
|
7230
7482
|
|
|
7231
7483
|
// tone: "success" | "warning" | "destructive" | "info" | "neutral" (import type BadgeTone)
|
|
7232
7484
|
// RULE: a chip never wraps \u2014 it is pinned white-space: nowrap, so it stays one line in
|
|
@@ -9075,11 +9327,10 @@ function getTokens(cat) {
|
|
|
9075
9327
|
for (const [c, items] of Object.entries(grouped)) {
|
|
9076
9328
|
out += `## ${c}
|
|
9077
9329
|
|
|
9078
|
-
| Name | Role |
|
|
9079
|
-
|
|
9330
|
+
| Name | Role | Tier |
|
|
9331
|
+
|---|---|---|
|
|
9080
9332
|
`;
|
|
9081
|
-
for (const t of items)
|
|
9082
|
-
out += `| \`${t.name}\` | ${t.role} | ${t.value ?? "\u2014"} | ${t.axis ?? "\u2014"} |
|
|
9333
|
+
for (const t of items) out += `| \`${t.name}\` | ${t.role} | ${t.tier} |
|
|
9083
9334
|
`;
|
|
9084
9335
|
out += "\n";
|
|
9085
9336
|
}
|