@godxjp/ui 30.8.0 → 31.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/agent/START-HERE.md +1 -1
- package/agent/components/PrefetchLink.json +3 -3
- package/agent/components-index.json +1 -1
- package/agent/components.json +3 -3
- package/agent/index.json +2 -2
- package/agent/llms.txt +3 -3
- package/agent/patterns/settings-page-responsive.json +1 -1
- package/agent/patterns.json +1 -1
- package/dist/components/query/index.d.ts +0 -2
- package/dist/components/query/index.js +0 -2
- package/dist/components/react-router/index.d.ts +2 -0
- package/dist/components/react-router/index.js +4 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/styles/alert-layout.css +4 -2
- package/dist/styles/layers.json +1182 -0
- package/dist/styles/vendor-day-picker.css +2 -0
- package/dist/styles/vendor-sonner.css +2 -0
- package/docs/CONSUMER-RULES.md +1 -1
- package/docs/data-display/permission-matrix.tsx +30 -28
- package/docs/data-entry/cascader.tsx +22 -20
- package/docs/data-entry/date-picker.tsx +47 -43
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +80 -76
- package/docs/data-entry/form-field/examples/create-form.tsx +33 -31
- package/docs/data-entry/form-field/index.tsx +61 -57
- package/docs/data-entry/form.tsx +112 -106
- package/docs/data-entry/input.tsx +297 -270
- package/docs/data-entry/label.tsx +11 -9
- package/docs/data-entry/number-input.tsx +86 -78
- package/docs/data-entry/radio-group.tsx +33 -26
- package/docs/data-entry/rating.tsx +29 -25
- package/docs/data-entry/select-matrix.tsx +49 -47
- package/docs/data-entry/select.tsx +38 -35
- package/docs/data-entry/slider.tsx +45 -41
- package/docs/data-entry/textarea.tsx +44 -42
- package/docs/data-entry/time-picker.tsx +51 -47
- package/docs/feedback/sheet.tsx +84 -79
- package/docs/foundation/theme-editor.tsx +133 -124
- package/docs/general/button/examples/form-actions.tsx +17 -15
- package/docs/layout/auth-recovery/examples/password-recovery.tsx +31 -29
- package/docs/layout/auth-shell-registration.tsx +75 -72
- package/docs/layout/auth-shell-variants.tsx +55 -51
- package/docs/layout/space-compact.tsx +11 -8
- package/docs/{query → react-router}/prefetch-link.tsx +1 -1
- package/docs/showcase/caimono-price-comparison.tsx +59 -51
- package/docs/showcase/case3-approval-workflow.tsx +27 -25
- package/docs/showcase/case4-login.tsx +37 -34
- package/docs/showcase/permission-matrix.tsx +44 -42
- package/docs/showcase/theme-customization.tsx +108 -99
- package/package.json +10 -3
- package/scripts/cli.mjs +15 -0
- package/scripts/consumer-rule.md +14 -0
- package/scripts/prune-css.mjs +221 -0
- package/scripts/ui-audit.mjs +99 -2
- /package/dist/components/{query → react-router}/prefetch-link.d.ts +0 -0
- /package/dist/components/{query → react-router}/prefetch-link.js +0 -0
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type ColumnDef,
|
|
14
14
|
} from "@godxjp/ui/data-display";
|
|
15
15
|
import {
|
|
16
|
+
Form,
|
|
16
17
|
Checkbox,
|
|
17
18
|
ColorPicker,
|
|
18
19
|
Field,
|
|
@@ -172,26 +173,28 @@ function PreviewPane({
|
|
|
172
173
|
</AlertDescription>
|
|
173
174
|
</Alert>
|
|
174
175
|
|
|
175
|
-
<
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
176
|
+
<Form>
|
|
177
|
+
<ResponsiveGrid columns={{ base: 1, md: 2 }}>
|
|
178
|
+
<FormField
|
|
179
|
+
id={`${idPrefix}-org`}
|
|
180
|
+
label={t("themeEditor.preview.field.org")}
|
|
181
|
+
helper={t("themeEditor.preview.field.orgHelper")}
|
|
182
|
+
>
|
|
183
|
+
<Input id={`${idPrefix}-org`} defaultValue="Acme 株式会社" />
|
|
184
|
+
</FormField>
|
|
185
|
+
<FormField id={`${idPrefix}-plan`} label={t("themeEditor.preview.field.plan")}>
|
|
186
|
+
<Select
|
|
187
|
+
id={`${idPrefix}-plan`}
|
|
188
|
+
defaultValue="pro"
|
|
189
|
+
options={[
|
|
190
|
+
{ value: "starter", label: t("themeEditor.preview.plan.starter") },
|
|
191
|
+
{ value: "pro", label: t("themeEditor.preview.plan.pro") },
|
|
192
|
+
{ value: "scale", label: t("themeEditor.preview.plan.scale") },
|
|
193
|
+
]}
|
|
194
|
+
/>
|
|
195
|
+
</FormField>
|
|
196
|
+
</ResponsiveGrid>
|
|
197
|
+
</Form>
|
|
195
198
|
|
|
196
199
|
<Flex gap="lg" wrap>
|
|
197
200
|
<Field id={`${idPrefix}-tax`} label={t("themeEditor.preview.field.tax")}>
|
|
@@ -417,53 +420,55 @@ export default function Demo() {
|
|
|
417
420
|
<CardDescription>{t("themeEditor.seed.description")}</CardDescription>
|
|
418
421
|
</CardHeader>
|
|
419
422
|
<CardContent>
|
|
420
|
-
<
|
|
421
|
-
<
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
<Form>
|
|
424
|
+
<Flex direction="col" gap="md">
|
|
425
|
+
<FormField
|
|
426
|
+
id="theme-editor-seed"
|
|
427
|
+
label={t("themeEditor.seed.label")}
|
|
428
|
+
helper={t("themeEditor.seed.helper")}
|
|
429
|
+
>
|
|
430
|
+
<ColorPicker id="theme-editor-seed" value={seed} onValueChange={setSeed} />
|
|
431
|
+
</FormField>
|
|
428
432
|
|
|
429
|
-
|
|
430
|
-
id="theme-editor-preset"
|
|
431
|
-
label={t("themeEditor.seed.presetLabel")}
|
|
432
|
-
helper={t("themeEditor.seed.presetHelper")}
|
|
433
|
-
>
|
|
434
|
-
<Select
|
|
433
|
+
<FormField
|
|
435
434
|
id="theme-editor-preset"
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
435
|
+
label={t("themeEditor.seed.presetLabel")}
|
|
436
|
+
helper={t("themeEditor.seed.presetHelper")}
|
|
437
|
+
>
|
|
438
|
+
<Select
|
|
439
|
+
id="theme-editor-preset"
|
|
440
|
+
value={SEED_PRESETS.find((p) => p.hex === seed)?.id ?? ""}
|
|
441
|
+
placeholder={t("themeEditor.seed.presetPlaceholder")}
|
|
442
|
+
onValueChange={(next: string) => {
|
|
443
|
+
const preset = SEED_PRESETS.find((p) => p.id === next);
|
|
444
|
+
if (!preset) return;
|
|
445
|
+
setSeed(preset.hex);
|
|
446
|
+
setDarkL(null);
|
|
447
|
+
}}
|
|
448
|
+
options={SEED_PRESETS.map((preset) => ({
|
|
449
|
+
value: preset.id,
|
|
450
|
+
label: t(`themeEditor.seed.preset.${preset.id}`),
|
|
451
|
+
sublabel: preset.hex,
|
|
452
|
+
}))}
|
|
453
|
+
/>
|
|
454
|
+
</FormField>
|
|
455
|
+
|
|
456
|
+
{/* The live region. It is VISIBLE as well as announced: the same sentence a screen
|
|
453
457
|
reader hears is the one the summary line needs anyway, so there is no hidden
|
|
454
458
|
copy to drift out of sync with what is on screen. */}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
459
|
+
<Text as="div" size="xs" tone="muted" role="status" aria-live="polite">
|
|
460
|
+
{brand && brand.dark
|
|
461
|
+
? t("themeEditor.seed.announce", {
|
|
462
|
+
hex: toHex(brand.light.seed),
|
|
463
|
+
light: toHex(brand.light.seed),
|
|
464
|
+
dark: toHex(brand.dark.seed),
|
|
465
|
+
pass: passing,
|
|
466
|
+
count: report.length,
|
|
467
|
+
})
|
|
468
|
+
: t("themeEditor.seed.announceNoDark", { hex: seed })}
|
|
469
|
+
</Text>
|
|
470
|
+
</Flex>
|
|
471
|
+
</Form>
|
|
467
472
|
</CardContent>
|
|
468
473
|
</Card>
|
|
469
474
|
|
|
@@ -503,72 +508,76 @@ export default function Demo() {
|
|
|
503
508
|
<CardDescription>{t("themeEditor.overrides.description")}</CardDescription>
|
|
504
509
|
</CardHeader>
|
|
505
510
|
<CardContent>
|
|
506
|
-
<
|
|
507
|
-
<
|
|
508
|
-
<
|
|
509
|
-
|
|
510
|
-
|
|
511
|
+
<Form>
|
|
512
|
+
<Flex direction="col" gap="md">
|
|
513
|
+
<Alert tone="warning">
|
|
514
|
+
<AlertTitle>{t("themeEditor.overrides.costTitle")}</AlertTitle>
|
|
515
|
+
<AlertDescription>
|
|
516
|
+
{t("themeEditor.overrides.costDescription")}
|
|
517
|
+
</AlertDescription>
|
|
518
|
+
</Alert>
|
|
511
519
|
|
|
512
|
-
|
|
513
|
-
id="theme-editor-label"
|
|
514
|
-
label={t("themeEditor.overrides.labelTitle")}
|
|
515
|
-
helper={t("themeEditor.overrides.labelHelper")}
|
|
516
|
-
>
|
|
517
|
-
<Segmented
|
|
520
|
+
<FormField
|
|
518
521
|
id="theme-editor-label"
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
{
|
|
525
|
-
{
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
: t("themeEditor.overrides.darkHelper", {
|
|
537
|
-
parity: percentFormat.format(searchedL),
|
|
538
|
-
})
|
|
539
|
-
}
|
|
540
|
-
>
|
|
541
|
-
<Slider
|
|
522
|
+
label={t("themeEditor.overrides.labelTitle")}
|
|
523
|
+
helper={t("themeEditor.overrides.labelHelper")}
|
|
524
|
+
>
|
|
525
|
+
<Segmented
|
|
526
|
+
id="theme-editor-label"
|
|
527
|
+
aria-label={t("themeEditor.overrides.labelTitle")}
|
|
528
|
+
value={labelMode}
|
|
529
|
+
onValueChange={(next) => setLabelMode(next as LabelMode)}
|
|
530
|
+
options={[
|
|
531
|
+
{ value: "auto", label: t("themeEditor.overrides.labelAuto") },
|
|
532
|
+
{ value: "light", label: t("themeEditor.overrides.labelLight") },
|
|
533
|
+
{ value: "dark", label: t("themeEditor.overrides.labelDark") },
|
|
534
|
+
]}
|
|
535
|
+
/>
|
|
536
|
+
</FormField>
|
|
537
|
+
|
|
538
|
+
<FormField
|
|
542
539
|
id="theme-editor-dark-l"
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
/>
|
|
552
|
-
</FormField>
|
|
553
|
-
|
|
554
|
-
<Flex gap="sm" wrap align="center">
|
|
555
|
-
<Button
|
|
556
|
-
variant="outline"
|
|
557
|
-
size="sm"
|
|
558
|
-
disabled={!darkOverridden}
|
|
559
|
-
onClick={() => setDarkL(null)}
|
|
540
|
+
label={t("themeEditor.overrides.darkTitle")}
|
|
541
|
+
helper={
|
|
542
|
+
searchedL === null
|
|
543
|
+
? t("themeEditor.overrides.darkNoParity")
|
|
544
|
+
: t("themeEditor.overrides.darkHelper", {
|
|
545
|
+
parity: percentFormat.format(searchedL),
|
|
546
|
+
})
|
|
547
|
+
}
|
|
560
548
|
>
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
549
|
+
<Slider
|
|
550
|
+
id="theme-editor-dark-l"
|
|
551
|
+
aria-label={t("themeEditor.overrides.darkTitle")}
|
|
552
|
+
min={0}
|
|
553
|
+
max={100}
|
|
554
|
+
step={0.1}
|
|
555
|
+
value={darkL ?? searchedL ?? 0}
|
|
556
|
+
onChange={(next: number) => setDarkL(next)}
|
|
557
|
+
tooltip={{ formatter: (value) => percentFormat.format(value) }}
|
|
558
|
+
marks={searchedL === null ? undefined : { [searchedL]: "" }}
|
|
559
|
+
/>
|
|
560
|
+
</FormField>
|
|
561
|
+
|
|
562
|
+
<Flex gap="sm" wrap align="center">
|
|
563
|
+
<Button
|
|
564
|
+
variant="outline"
|
|
565
|
+
size="sm"
|
|
566
|
+
disabled={!darkOverridden}
|
|
567
|
+
onClick={() => setDarkL(null)}
|
|
568
|
+
>
|
|
569
|
+
{t("themeEditor.overrides.darkReset")}
|
|
570
|
+
</Button>
|
|
571
|
+
<Badge tone={darkOverridden ? "warning" : "neutral"} variant="outline">
|
|
572
|
+
{t(
|
|
573
|
+
darkOverridden
|
|
574
|
+
? "themeEditor.overrides.darkAuthored"
|
|
575
|
+
: "themeEditor.overrides.darkParity",
|
|
576
|
+
)}
|
|
577
|
+
</Badge>
|
|
578
|
+
</Flex>
|
|
570
579
|
</Flex>
|
|
571
|
-
</
|
|
580
|
+
</Form>
|
|
572
581
|
</CardContent>
|
|
573
582
|
</Card>
|
|
574
583
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FormField, Input } from "@godxjp/ui/data-entry";
|
|
1
|
+
import { Form, FormField, Input } from "@godxjp/ui/data-entry";
|
|
2
2
|
import { Button } from "@godxjp/ui/general";
|
|
3
3
|
import { Avatar, AvatarFallback } from "@godxjp/ui/data-display";
|
|
4
4
|
import {
|
|
@@ -65,20 +65,22 @@ export default function Demo() {
|
|
|
65
65
|
</Flex>
|
|
66
66
|
}
|
|
67
67
|
>
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
<Form>
|
|
69
|
+
<Flex direction="col" gap="md">
|
|
70
|
+
<FormField id="name" label="取引先名">
|
|
71
|
+
<Input id="name" defaultValue="株式会社ベトヤ" />
|
|
72
|
+
</FormField>
|
|
73
|
+
<FormField id="code" label="取引先コード">
|
|
74
|
+
<Input id="code" defaultValue="BTY-0012" />
|
|
75
|
+
</FormField>
|
|
76
|
+
<FormField id="contact" label="担当者">
|
|
77
|
+
<Input id="contact" defaultValue="グエン・ヴァン・A" />
|
|
78
|
+
</FormField>
|
|
79
|
+
<FormField id="email" label="メール">
|
|
80
|
+
<Input id="email" type="email" defaultValue="ke-toan@betoya.test" />
|
|
81
|
+
</FormField>
|
|
82
|
+
</Flex>
|
|
83
|
+
</Form>
|
|
82
84
|
</PageContainer>
|
|
83
85
|
</AppShell>
|
|
84
86
|
);
|
|
@@ -2,7 +2,7 @@ import { useState } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { Alert, AlertDescription, AlertTitle } from "@godxjp/ui/feedback";
|
|
4
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
5
|
-
import { FormField, Input, PasswordInput, PasswordStrength } from "@godxjp/ui/data-entry";
|
|
5
|
+
import { Form, FormField, Input, PasswordInput, PasswordStrength } from "@godxjp/ui/data-entry";
|
|
6
6
|
import { Button, Logo, Text } from "@godxjp/ui/general";
|
|
7
7
|
import { AuthFooter, AuthShell, AuthStack, Flex } from "@godxjp/ui/layout";
|
|
8
8
|
import { AppSettingPicker } from "@godxjp/ui/navigation";
|
|
@@ -128,34 +128,36 @@ export default function Demo() {
|
|
|
128
128
|
</CardDescription>
|
|
129
129
|
</CardHeader>
|
|
130
130
|
<CardContent>
|
|
131
|
-
<
|
|
132
|
-
<
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
131
|
+
<Form>
|
|
132
|
+
<AuthStack>
|
|
133
|
+
<FormField id="recovery-new-password" label="新しいパスワード" required>
|
|
134
|
+
<PasswordInput
|
|
135
|
+
autoComplete="new-password"
|
|
136
|
+
value={password}
|
|
137
|
+
onChange={(event) => setPassword(event.target.value)}
|
|
138
|
+
/>
|
|
139
|
+
</FormField>
|
|
140
|
+
<PasswordStrength value={password} />
|
|
141
|
+
<FormField
|
|
142
|
+
id="recovery-confirm-password"
|
|
143
|
+
label="新しいパスワード(確認)"
|
|
144
|
+
error="パスワードが一致しません。"
|
|
145
|
+
required
|
|
146
|
+
>
|
|
147
|
+
<PasswordInput
|
|
148
|
+
autoComplete="new-password"
|
|
149
|
+
value={confirmation}
|
|
150
|
+
onChange={(event) => setConfirmation(event.target.value)}
|
|
151
|
+
/>
|
|
152
|
+
</FormField>
|
|
153
|
+
<Button fullWidth>パスワードを更新</Button>
|
|
154
|
+
<Flex justify="between" gap="sm" wrap>
|
|
155
|
+
<Button variant="ghost" size="sm">
|
|
156
|
+
サインインに戻る
|
|
157
|
+
</Button>
|
|
158
|
+
</Flex>
|
|
159
|
+
</AuthStack>
|
|
160
|
+
</Form>
|
|
159
161
|
</CardContent>
|
|
160
162
|
</Card>
|
|
161
163
|
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
ListRow,
|
|
15
15
|
} from "@godxjp/ui/data-display";
|
|
16
16
|
import {
|
|
17
|
+
Form,
|
|
17
18
|
Checkbox,
|
|
18
19
|
Field,
|
|
19
20
|
FormField,
|
|
@@ -137,91 +138,93 @@ export default function Demo() {
|
|
|
137
138
|
</CardDescription>
|
|
138
139
|
</CardHeader>
|
|
139
140
|
<CardContent>
|
|
140
|
-
<
|
|
141
|
-
<
|
|
142
|
-
<
|
|
143
|
-
|
|
141
|
+
<Form>
|
|
142
|
+
<AuthStack>
|
|
143
|
+
<FormField id="reg-name" label="お名前" required>
|
|
144
|
+
<Input id="reg-name" autoComplete="name" placeholder="山田 太郎" />
|
|
145
|
+
</FormField>
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
id="reg-email"
|
|
147
|
-
label="メールアドレス"
|
|
148
|
-
required
|
|
149
|
-
helper="確認リンクの送信先になります。"
|
|
150
|
-
>
|
|
151
|
-
<Input
|
|
147
|
+
<FormField
|
|
152
148
|
id="reg-email"
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
label="メールアドレス"
|
|
150
|
+
required
|
|
151
|
+
helper="確認リンクの送信先になります。"
|
|
152
|
+
>
|
|
153
|
+
<Input
|
|
154
|
+
id="reg-email"
|
|
155
|
+
type="email"
|
|
156
|
+
autoComplete="email"
|
|
157
|
+
placeholder="you@example.com"
|
|
158
|
+
/>
|
|
159
|
+
</FormField>
|
|
158
160
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
<FormField id="reg-password" label="パスワード" required>
|
|
162
|
+
<PasswordInput
|
|
163
|
+
id="reg-password"
|
|
164
|
+
autoComplete="new-password"
|
|
165
|
+
value={password}
|
|
166
|
+
onChange={(event) => setPassword(event.target.value)}
|
|
167
|
+
/>
|
|
168
|
+
</FormField>
|
|
167
169
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
{/* The strength meter is a real primitive — never a hand-rolled coloured bar. */}
|
|
171
|
+
<PasswordStrength value={password} />
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
id="reg-confirm"
|
|
173
|
-
label="パスワード(確認)"
|
|
174
|
-
required
|
|
175
|
-
// The error is announced through the field's own aria-errormessage wiring, so
|
|
176
|
-
// the mismatch is not communicated by colour alone.
|
|
177
|
-
error={mismatch ? "パスワードが一致しません。" : undefined}
|
|
178
|
-
>
|
|
179
|
-
<PasswordInput
|
|
173
|
+
<FormField
|
|
180
174
|
id="reg-confirm"
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
175
|
+
label="パスワード(確認)"
|
|
176
|
+
required
|
|
177
|
+
// The error is announced through the field's own aria-errormessage wiring, so
|
|
178
|
+
// the mismatch is not communicated by colour alone.
|
|
179
|
+
error={mismatch ? "パスワードが一致しません。" : undefined}
|
|
180
|
+
>
|
|
181
|
+
<PasswordInput
|
|
182
|
+
id="reg-confirm"
|
|
183
|
+
autoComplete="new-password"
|
|
184
|
+
value={confirm}
|
|
185
|
+
onChange={(event) => setConfirm(event.target.value)}
|
|
186
|
+
/>
|
|
187
|
+
</FormField>
|
|
186
188
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
<Field id="reg-consent" label="利用規約とプライバシーポリシーに同意します">
|
|
190
|
+
<Checkbox
|
|
191
|
+
id="reg-consent"
|
|
192
|
+
checked={consent}
|
|
193
|
+
onCheckedChange={(next) => setConsent(next === true)}
|
|
194
|
+
/>
|
|
195
|
+
</Field>
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
197
|
+
<Button
|
|
198
|
+
loading={submitting}
|
|
199
|
+
disabled={!consent || mismatch}
|
|
200
|
+
onClick={() => {
|
|
201
|
+
setSubmitting(true);
|
|
202
|
+
setSubmitting(false);
|
|
203
|
+
setStage("pending-email");
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
アカウントを作成
|
|
207
|
+
</Button>
|
|
206
208
|
|
|
207
|
-
|
|
209
|
+
{/* ── SOCIAL / PROVIDER ACTIONS — the documented "SocialLinks" composition.
|
|
208
210
|
A divider + real outline Buttons. No new component: the package must not
|
|
209
211
|
invent which providers a product offers, in what order, or what consent
|
|
210
212
|
they imply. `disabled` / `loading` are the Button's own props. ── */}
|
|
211
|
-
|
|
213
|
+
<AuthDivider label="または" />
|
|
212
214
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
<Flex direction="col" gap="sm">
|
|
216
|
+
{PROVIDERS.map((provider) => {
|
|
217
|
+
const Icon = provider.icon;
|
|
218
|
+
return (
|
|
219
|
+
<Button key={provider.id} variant="outline" disabled={submitting}>
|
|
220
|
+
<Icon aria-hidden="true" />
|
|
221
|
+
{provider.label}
|
|
222
|
+
</Button>
|
|
223
|
+
);
|
|
224
|
+
})}
|
|
225
|
+
</Flex>
|
|
226
|
+
</AuthStack>
|
|
227
|
+
</Form>
|
|
225
228
|
</CardContent>
|
|
226
229
|
</Card>
|
|
227
230
|
) : (
|