@godxjp/ui 21.0.0 → 22.0.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/app/app-provider.js +2 -1
- package/dist/components/data-display/badge.d.ts +28 -1
- package/dist/components/data-display/badge.js +2 -0
- package/dist/components/data-display/data-table.d.ts +18 -2
- package/dist/components/data-display/data-table.js +4 -0
- package/dist/components/data-display/index.d.ts +1 -1
- package/dist/components/data-display/popover.d.ts +19 -1
- package/dist/components/data-display/popover.js +9 -1
- package/dist/components/data-display/progress.d.ts +37 -0
- package/dist/components/data-display/progress.js +57 -5
- package/dist/components/data-entry/date-picker.d.ts +10 -3
- package/dist/components/data-entry/date-picker.js +503 -238
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +4 -1
- package/dist/components/data-entry/form.js +10 -1
- package/dist/components/data-entry/index.d.ts +0 -6
- package/dist/components/data-entry/index.js +0 -6
- package/dist/components/data-entry/label.d.ts +14 -1
- package/dist/components/data-entry/label.js +6 -1
- package/dist/components/data-entry/radio.d.ts +27 -6
- package/dist/components/data-entry/radio.js +88 -43
- package/dist/components/data-entry/search-select.js +8 -1
- package/dist/components/data-entry/select.js +2 -0
- package/dist/components/data-entry/switch.d.ts +6 -3
- package/dist/components/data-entry/switch.js +34 -11
- package/dist/components/layout/mobile-shell.d.ts +1 -1
- package/dist/components/layout/mobile-shell.js +2 -0
- package/dist/components/navigation/menubar.d.ts +10 -3
- package/dist/components/navigation/steps.d.ts +1 -1
- package/dist/components/navigation/steps.js +3 -1
- package/dist/components/navigation/tabs-scroll.d.ts +27 -0
- package/dist/components/navigation/tabs-scroll.js +52 -1
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +88 -23
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.js +0 -1
- package/dist/components/ui/segmented.d.ts +15 -1
- package/dist/components/ui/segmented.js +13 -6
- package/dist/i18n/messages/en.json +3 -2
- package/dist/i18n/messages/ja.json +3 -2
- package/dist/i18n/messages/vi.json +3 -2
- package/dist/lib/datetime/picker-format.d.ts +18 -0
- package/dist/lib/datetime/picker-format.js +35 -1
- package/dist/lib/field-a11y.d.ts +12 -0
- package/dist/lib/field-a11y.js +14 -1
- package/dist/props/components/data-display.prop.d.ts +7 -0
- package/dist/props/components/data-entry.prop.d.ts +180 -99
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +12 -1
- package/dist/props/components/navigation.prop.d.ts +76 -1
- package/dist/props/registry.d.ts +16 -16
- package/dist/props/registry.js +16 -15
- package/dist/props/vocabulary/data.prop.d.ts +12 -0
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/layout.prop.d.ts +11 -0
- package/dist/styles/badge-layout.css +4 -0
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/control.css +15 -6
- package/dist/styles/data-display-layout.css +76 -13
- package/dist/styles/focus-ring.css +4 -2
- package/dist/styles/navigation-layout.css +25 -0
- package/dist/styles/shell-layout.css +5 -0
- package/dist/styles/table-layout.css +34 -0
- package/dist/tokens/components/data-display.css +9 -0
- package/dist/tokens/components/navigation.css +6 -0
- package/dist/tokens/components/segmented.css +0 -2
- package/dist/tokens/components/shell.css +2 -0
- package/dist/tokens/components/table.css +3 -0
- package/dist/tokens/foundation.css +7 -0
- package/docs/COMPONENTS.md +14 -14
- package/docs/CONSUMER-RULES.md +24 -1
- package/docs/FRAME-COVERAGE-REPORT.md +3 -6
- package/docs/TOKENS.md +65 -0
- package/docs/data-display/badge.tsx +37 -0
- package/docs/data-display/data-table/index.tsx +18 -0
- package/docs/data-display/popover.tsx +41 -1
- package/docs/data-display/progress.tsx +28 -0
- package/docs/data-entry/calendar.tsx +2 -2
- package/docs/data-entry/date-picker.tsx +143 -3
- package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
- package/docs/data-entry/form.tsx +4 -4
- package/docs/data-entry/number-input.tsx +31 -0
- package/docs/data-entry/segmented.tsx +103 -1
- package/docs/layout/mobile-shell.tsx +5 -0
- package/docs/navigation/tabs.tsx +149 -0
- package/docs/roadmap/antd-parity.md +35 -34
- package/docs/roadmap/parity-audit-data-entry.md +178 -165
- package/docs/roadmap/parity-backlog.md +6 -7
- package/docs/showcase/acme-portal.tsx +7 -0
- package/package.json +11 -23
- package/scripts/ui-audit.mjs +17 -4
- package/dist/components/data-entry/date-range-picker.d.ts +0 -11
- package/dist/components/data-entry/date-range-picker.js +0 -349
- package/dist/components/data-entry/month-picker.d.ts +0 -10
- package/dist/components/data-entry/month-picker.js +0 -241
- package/dist/components/data-entry/month-range-picker.d.ts +0 -10
- package/dist/components/data-entry/month-range-picker.js +0 -301
- package/dist/components/ui/date-range-picker.d.ts +0 -1
- package/dist/components/ui/date-range-picker.js +0 -2
- package/docs/data-entry/date-range-picker.tsx +0 -175
- package/docs/data-entry/month-picker.tsx +0 -101
- package/docs/data-entry/month-range-picker.tsx +0 -115
package/docs/TOKENS.md
CHANGED
|
@@ -44,6 +44,71 @@ src/styles/
|
|
|
44
44
|
|
|
45
45
|
Default brand tokens use the GodX Agent Portal palette: navy primary, 朱 orange focus/accent, warm neutral surfaces. App or customer identity colors belong in the consuming app theme, not in package tokens.
|
|
46
46
|
|
|
47
|
+
#### The three tone tiers — FILL, TEXT, MARK
|
|
48
|
+
|
|
49
|
+
A status tone can be painted three ways, and each way is judged against a different thing. Reading
|
|
50
|
+
the wrong tier is this palette's most expensive recurring bug, because nothing about it looks wrong
|
|
51
|
+
in the source.
|
|
52
|
+
|
|
53
|
+
| Tier | Tokens | What it paints | Contrast bar |
|
|
54
|
+
| -------- | ------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------- |
|
|
55
|
+
| **FILL** | `--success`, `--warning`, `--info`, `--destructive` | A solid chip, band or bar with a label ON it | AA **4.5:1** against its own `*-foreground` label |
|
|
56
|
+
| **TEXT** | `--text-success`, `--text-warning`, `--text-info`, `--text-error` | Small coloured type — a StatCard delta, an outline badge label | AA **4.5:1** against the surface BEHIND it |
|
|
57
|
+
| **MARK** | `--mark-success`, `--mark-warning`, `--mark-info`, `--mark-destructive`, `--mark-primary`, `--mark-attention` | A thin shape carrying meaning with nothing written on it — a `Card accent` rail, a `DataTable rowTone` rail | SC 1.4.11 **3:1** against the surface it sits on |
|
|
58
|
+
|
|
59
|
+
The MARK tier exists because both rails were reading FILL, and two of them were effectively
|
|
60
|
+
invisible. Measured in Chromium on the light card: `Card accent="warning"` drew its 6px rail at
|
|
61
|
+
**1.74:1**, `accent="success"` at **2.18:1**. `--warning` is a bright 山吹 tuned to carry dark text
|
|
62
|
+
on top of it; on a near-white card it is barely a colour at all.
|
|
63
|
+
|
|
64
|
+
`--mark-*` resolve THROUGH the TEXT tier rather than restating its numbers, so a theme retunes one
|
|
65
|
+
place and both follow — and since 3:1 is looser than 4.5:1, anything legible as text is legible as
|
|
66
|
+
a mark by construction. `--mark-attention` is the exception: there is no `--text-attention`, and
|
|
67
|
+
the fill already clears the floor in both themes (3.32 / 6.75).
|
|
68
|
+
|
|
69
|
+
Two guards, because one was not enough:
|
|
70
|
+
`src/tokens/__tests__/tone-mark-contrast.test.ts` recomputes every tone × ground × theme off the
|
|
71
|
+
committed tokens and reads the alias out of the CSS (so repointing a mark back at FILL fails the
|
|
72
|
+
ratio, not just a name check); and `check:contrast` grew a **rail pass** — its graphic pass only
|
|
73
|
+
ever measured elements ≤24px on both axes, and a rail is 6px by the full height of a card, so no
|
|
74
|
+
gate had ever looked at one.
|
|
75
|
+
|
|
76
|
+
**Progress and Legend joined the tier, and the wa-iro question is settled.** `.ui-legend-swatch`,
|
|
77
|
+
`.ui-progress-bar` and `.ui-progress-segment` all read FILL as standalone graphics and all failed
|
|
78
|
+
the same floor. The floor applies: nothing is written on a progress fill, so *where the colour
|
|
79
|
+
stops* is the entire datum, and a slice of a partition carries its share of the whole with no words
|
|
80
|
+
on it — that is precisely a "graphical object required to understand the content". The hue loses.
|
|
81
|
+
The two moved together because a swatch is a SAMPLE of the bar beside it; a key that is not the
|
|
82
|
+
colour it is a key to is not a key.
|
|
83
|
+
|
|
84
|
+
| surface | tone | ground | FILL (before) | MARK (after) |
|
|
85
|
+
| --- | --- | --- | --- | --- |
|
|
86
|
+
| `.ui-legend-swatch` | warning | light card | **1.74** | **5.90** |
|
|
87
|
+
| `.ui-legend-swatch` | success | light card | **2.18** | **6.84** |
|
|
88
|
+
| `.ui-legend-swatch` | destructive | dark card | **2.95** | **5.52** |
|
|
89
|
+
| `.ui-progress-segment` | warning | light track | **1.60** | **5.41** |
|
|
90
|
+
| `.ui-progress-segment` | success | light track | **2.00** | **6.28** |
|
|
91
|
+
| `.ui-progress-segment` | destructive | dark track | **2.42** | **4.52** |
|
|
92
|
+
|
|
93
|
+
Worst case anywhere on the two routes after the move: **4.52:1**. `.ui-progress-bar` (meter and
|
|
94
|
+
over-capacity) reads the same tokens as the slice, so a `tone="warning"` meter and a `warning`
|
|
95
|
+
slice on one screen stay the same colour.
|
|
96
|
+
|
|
97
|
+
**A theme that repoints `--secondary` owes `--progress-track-background`.** The track defaults to
|
|
98
|
+
`hsl(var(--secondary))`, which is a pale neutral in the stock palette. `docs/showcase/acme-portal`
|
|
99
|
+
repurposes `--secondary` as a navy *button* colour, so its bars were drawn on a near-black track
|
|
100
|
+
and the mark fills measured 2.50 (success) / 2.90 (warning) on it. Naming the track explicitly is
|
|
101
|
+
the fix — 6.49 / 5.59 after — not dragging the tier back.
|
|
102
|
+
|
|
103
|
+
Three guards now, and each sees something the others cannot: the ratio tests in
|
|
104
|
+
`tone-mark-contrast.test.ts` (rails against card/background, progress marks against the track);
|
|
105
|
+
the *same file's* CSS-alias assertions, which fail if a rule is repointed at the fill tier and
|
|
106
|
+
which also pin the swatch and the slice to the **same** token per tone; and `check:contrast`'s
|
|
107
|
+
**thin fill** pass — added because adding `/isolate/data-display-progress` to that gate's route
|
|
108
|
+
list on its own changed nothing at all. The graphic pass wants ≤24px on both axes and the rail
|
|
109
|
+
pass wants a one-sided border; a progress fill is 8px (meter) or 22px (slice) tall by whatever
|
|
110
|
+
width its share makes it, so the sweep printed "AA clean" over a bar sitting at 1.60:1.
|
|
111
|
+
|
|
47
112
|
#### `--border` vs `--input` — decorative chrome vs control boundary (gh#315)
|
|
48
113
|
|
|
49
114
|
These two look like synonyms and are not. Keep them apart:
|
|
@@ -262,6 +262,43 @@ export default function Demo() {
|
|
|
262
262
|
</CardContent>
|
|
263
263
|
</Card>
|
|
264
264
|
|
|
265
|
+
<Card>
|
|
266
|
+
<CardHeader>
|
|
267
|
+
<CardTitle level={2}>tabular · 数字を桁で揃える</CardTitle>
|
|
268
|
+
<CardDescription>
|
|
269
|
+
件数のチップが縦に並ぶときは tabular を。プロポーショナル数字だと 1 が 0
|
|
270
|
+
より狭いので、列で見たときに桁がずれてチップ幅も揺れます。既定はオフ — 語を載せる
|
|
271
|
+
チップまで幅の広い数字を払う必要はありません。
|
|
272
|
+
</CardDescription>
|
|
273
|
+
</CardHeader>
|
|
274
|
+
<CardContent>
|
|
275
|
+
<Flex direction="row" gap="lg">
|
|
276
|
+
<Flex direction="col" align="start" gap="xs">
|
|
277
|
+
<Text size="xs" tone="muted">
|
|
278
|
+
既定(比例数字)
|
|
279
|
+
</Text>
|
|
280
|
+
<Badge tone="info">11</Badge>
|
|
281
|
+
<Badge tone="info">100</Badge>
|
|
282
|
+
<Badge tone="warning">1811</Badge>
|
|
283
|
+
</Flex>
|
|
284
|
+
<Flex direction="col" align="start" gap="xs">
|
|
285
|
+
<Text size="xs" tone="muted">
|
|
286
|
+
tabular
|
|
287
|
+
</Text>
|
|
288
|
+
<Badge tone="info" tabular>
|
|
289
|
+
11
|
|
290
|
+
</Badge>
|
|
291
|
+
<Badge tone="info" tabular>
|
|
292
|
+
100
|
|
293
|
+
</Badge>
|
|
294
|
+
<Badge tone="warning" tabular>
|
|
295
|
+
1811
|
|
296
|
+
</Badge>
|
|
297
|
+
</Flex>
|
|
298
|
+
</Flex>
|
|
299
|
+
</CardContent>
|
|
300
|
+
</Card>
|
|
301
|
+
|
|
265
302
|
<Card>
|
|
266
303
|
<CardHeader>
|
|
267
304
|
<CardTitle level={2}>Icon overrides</CardTitle>
|
|
@@ -140,6 +140,24 @@ export default function Demo() {
|
|
|
140
140
|
getRowId={(row) => row.id}
|
|
141
141
|
/>
|
|
142
142
|
</Flex>
|
|
143
|
+
{/* Row TONE — the leading-edge rail + wash for a row in a named state. The status Badge
|
|
144
|
+
stays in its own cell: the rail makes the row findable, it does not carry the meaning
|
|
145
|
+
(WCAG 1.4.1). */}
|
|
146
|
+
<Flex direction="col" gap="sm" id="row-tone">
|
|
147
|
+
<Text weight="medium">行のトーン(要対応の行)</Text>
|
|
148
|
+
<DataTable
|
|
149
|
+
data={invoices}
|
|
150
|
+
columns={columns}
|
|
151
|
+
getRowId={(row) => row.id}
|
|
152
|
+
rowTone={(row) =>
|
|
153
|
+
row.status === "failed"
|
|
154
|
+
? "destructive"
|
|
155
|
+
: row.status === "pending"
|
|
156
|
+
? "attention"
|
|
157
|
+
: undefined
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
</Flex>
|
|
143
161
|
{/* Primary: sorted (amount desc), one row preselected, clickable rows,
|
|
144
162
|
controlled density (comfortable), kebab row actions, pagination footer. */}
|
|
145
163
|
<DataTable
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
PopoverTrigger,
|
|
16
16
|
} from "@godxjp/ui/data-display";
|
|
17
17
|
import { Checkbox, FormField, Input, Label } from "@godxjp/ui/data-entry";
|
|
18
|
-
import { Button } from "@godxjp/ui/general";
|
|
18
|
+
import { Button, Text } from "@godxjp/ui/general";
|
|
19
19
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
20
20
|
import { Info, SlidersHorizontal } from "lucide-react";
|
|
21
21
|
|
|
@@ -209,6 +209,46 @@ export default function Demo() {
|
|
|
209
209
|
</CardContent>
|
|
210
210
|
</Card>
|
|
211
211
|
|
|
212
|
+
<Card>
|
|
213
|
+
<CardHeader>
|
|
214
|
+
<CardTitle level={2}>width · パネルの測り方</CardTitle>
|
|
215
|
+
<CardDescription>
|
|
216
|
+
既定の panel は --popover-width(18rem)。auto は中身に測らせます — 自前の幅を持つもの
|
|
217
|
+
(Calendar、チャート、固定幅のプレビュー)はこれ以外に正解がありません。trigger
|
|
218
|
+
はアンカーに合わせます。className に w-* を書くのは呼び出し側の定数で、どのテーマからも
|
|
219
|
+
retune できません。flush が padding 側で避けているのと同じ話です。
|
|
220
|
+
</CardDescription>
|
|
221
|
+
</CardHeader>
|
|
222
|
+
<CardContent>
|
|
223
|
+
<Flex direction="row" wrap gap="sm">
|
|
224
|
+
<Popover>
|
|
225
|
+
<PopoverTrigger asChild>
|
|
226
|
+
<Button variant="outline">panel(既定)</Button>
|
|
227
|
+
</PopoverTrigger>
|
|
228
|
+
<PopoverContent aria-label="既定の幅">
|
|
229
|
+
<Text size="sm">18rem のパネル幅。散文や、パネル自身が測るリスト向け。</Text>
|
|
230
|
+
</PopoverContent>
|
|
231
|
+
</Popover>
|
|
232
|
+
<Popover>
|
|
233
|
+
<PopoverTrigger asChild>
|
|
234
|
+
<Button variant="outline">auto</Button>
|
|
235
|
+
</PopoverTrigger>
|
|
236
|
+
<PopoverContent width="auto" aria-label="内容に合わせた幅">
|
|
237
|
+
<Text size="sm">中身が幅を決めます。</Text>
|
|
238
|
+
</PopoverContent>
|
|
239
|
+
</Popover>
|
|
240
|
+
<Popover>
|
|
241
|
+
<PopoverTrigger asChild>
|
|
242
|
+
<Button variant="outline">trigger に合わせる</Button>
|
|
243
|
+
</PopoverTrigger>
|
|
244
|
+
<PopoverContent width="trigger" aria-label="トリガーと同じ幅">
|
|
245
|
+
<Text size="sm">アンカーと同じ幅。</Text>
|
|
246
|
+
</PopoverContent>
|
|
247
|
+
</Popover>
|
|
248
|
+
</Flex>
|
|
249
|
+
</CardContent>
|
|
250
|
+
</Card>
|
|
251
|
+
|
|
212
252
|
<Card>
|
|
213
253
|
<CardHeader>
|
|
214
254
|
<CardTitle level={2}>Modal</CardTitle>
|
|
@@ -33,6 +33,34 @@ export default function Demo() {
|
|
|
33
33
|
</CardContent>
|
|
34
34
|
</Card>
|
|
35
35
|
|
|
36
|
+
<Card>
|
|
37
|
+
<CardHeader>
|
|
38
|
+
<CardTitle level={2}>shape="ring"</CardTitle>
|
|
39
|
+
<CardDescription>
|
|
40
|
+
同じメーターを弧で描きます。バーと同じ value / tone / size / ARIA
|
|
41
|
+
のまま、label がリングの内側に入ります。狭いアプリバーで「18 / 42
|
|
42
|
+
件」を見出しの横に出したいとき、バー+キャプションの 2
|
|
43
|
+
段ではなく 1 マスで済むのが理由です。数値が重要なときではなく、
|
|
44
|
+
場所が正方形のときに選びます。
|
|
45
|
+
</CardDescription>
|
|
46
|
+
</CardHeader>
|
|
47
|
+
<CardContent>
|
|
48
|
+
<Flex direction="row" wrap align="center" gap="lg">
|
|
49
|
+
{/* label はリング内の表示であり、同時に読み上げ名でもあります。「何の 18/42 か」を
|
|
50
|
+
名前にしたいときは、画面にすでにある見出しを aria-labelledby で指します
|
|
51
|
+
— bar と同じ規約です。 */}
|
|
52
|
+
<Progress shape="ring" value={43} label="18/42" />
|
|
53
|
+
<Progress shape="ring" value={43} size="sm" label="18/42" />
|
|
54
|
+
<Progress shape="ring" value={0} label="0/42" />
|
|
55
|
+
<Progress shape="ring" value={100} label="42/42" />
|
|
56
|
+
<Progress shape="ring" value={62} tone="warning" label="26/42" />
|
|
57
|
+
<Progress shape="ring" value={88} tone="destructive" label="37/42" />
|
|
58
|
+
{/* label なし=読み出しのないリング。名前は aria-label から。 */}
|
|
59
|
+
<Progress shape="ring" value={43} aria-label="実施済みの点検" />
|
|
60
|
+
</Flex>
|
|
61
|
+
</CardContent>
|
|
62
|
+
</Card>
|
|
63
|
+
|
|
36
64
|
<Card>
|
|
37
65
|
<CardHeader>
|
|
38
66
|
<CardTitle level={2}>Breakdown(segments)</CardTitle>
|
|
@@ -20,7 +20,7 @@ import type { DateRange } from "react-day-picker";
|
|
|
20
20
|
/**
|
|
21
21
|
* Calendar — styled react-day-picker grid for single, multiple, or range
|
|
22
22
|
* selection. Embed inside a Popover for picker UX; use DatePicker /
|
|
23
|
-
*
|
|
23
|
+
* `DatePicker range` for form-submittable inputs instead. Composed only from
|
|
24
24
|
* real @godxjp/ui components.
|
|
25
25
|
*/
|
|
26
26
|
export default function Demo() {
|
|
@@ -71,7 +71,7 @@ export default function Demo() {
|
|
|
71
71
|
<CardHeader>
|
|
72
72
|
<CardTitle level={2}>範囲選択 · Popover 内</CardTitle>
|
|
73
73
|
<CardDescription>
|
|
74
|
-
mode="range" でポップオーバー内に配置。
|
|
74
|
+
mode="range" でポップオーバー内に配置。DatePicker range
|
|
75
75
|
はこのパターンにフォーム送信を加えた上位コンポーネント。
|
|
76
76
|
</CardDescription>
|
|
77
77
|
</CardHeader>
|
|
@@ -4,11 +4,14 @@ import { ja } from "date-fns/locale";
|
|
|
4
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
5
5
|
import { DatePicker, FormField } from "@godxjp/ui/data-entry";
|
|
6
6
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
7
|
+
import type { DateRange } from "react-day-picker";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* DatePicker —
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* DatePicker — ONE date control. `picker` sets the granularity (day · week · month · quarter ·
|
|
11
|
+
* year), `range` turns it into a two-endpoint field, `multiple` into a set. Always give it a
|
|
12
|
+
* `name` for form submission; it emits ISO-8601 at the picker's own precision. Use
|
|
13
|
+
* `minDate`/`maxDate` to constrain what is selectable. Never hand-roll a date input + calendar,
|
|
14
|
+
* and never compose two DatePickers side-by-side to fake a range — that is `range`.
|
|
12
15
|
* Composed only from real @godxjp/ui components.
|
|
13
16
|
*/
|
|
14
17
|
export default function Demo() {
|
|
@@ -17,6 +20,11 @@ export default function Demo() {
|
|
|
17
20
|
const [closingDate, setClosingDate] = useState<Date | undefined>(undefined);
|
|
18
21
|
const [meetingDate, setMeetingDate] = useState<Date | undefined>(new Date(2026, 5, 25));
|
|
19
22
|
const [settlementDate, setSettlementDate] = useState<Date | undefined>(undefined);
|
|
23
|
+
const [period, setPeriod] = useState<DateRange | undefined>({
|
|
24
|
+
from: new Date(2026, 3, 1),
|
|
25
|
+
to: new Date(2027, 2, 31),
|
|
26
|
+
});
|
|
27
|
+
const [term, setTerm] = useState<DateRange | undefined>(undefined);
|
|
20
28
|
|
|
21
29
|
return (
|
|
22
30
|
<PageContainer
|
|
@@ -271,6 +279,138 @@ export default function Demo() {
|
|
|
271
279
|
</FormField>
|
|
272
280
|
</CardContent>
|
|
273
281
|
</Card>
|
|
282
|
+
<Card>
|
|
283
|
+
<CardHeader>
|
|
284
|
+
<CardTitle level={2}>期間 (range)</CardTitle>
|
|
285
|
+
<CardDescription>
|
|
286
|
+
range を付けると値は DateRange になり、開始/終了の 2 入力を 1
|
|
287
|
+
つのコントロールにまとめる。 name="period" は period_from / period_to として
|
|
288
|
+
ISO 送信される。 開始 > 終了で入力しても order (既定 true) が昇順に正規化する。
|
|
289
|
+
</CardDescription>
|
|
290
|
+
</CardHeader>
|
|
291
|
+
<CardContent data-axe-open>
|
|
292
|
+
<FormField id="period" label="会計期間" required>
|
|
293
|
+
<DatePicker
|
|
294
|
+
range
|
|
295
|
+
id="period"
|
|
296
|
+
name="period"
|
|
297
|
+
value={period}
|
|
298
|
+
onValueChange={setPeriod}
|
|
299
|
+
minDate={new Date(2020, 0, 1)}
|
|
300
|
+
maxDate={new Date(2030, 11, 31)}
|
|
301
|
+
/>
|
|
302
|
+
</FormField>
|
|
303
|
+
</CardContent>
|
|
304
|
+
</Card>
|
|
305
|
+
<Card>
|
|
306
|
+
<CardHeader>
|
|
307
|
+
<CardTitle level={2}>月次の期間 (range × picker)</CardTitle>
|
|
308
|
+
<CardDescription>
|
|
309
|
+
range と picker は直交する。picker="month" の期間は月グリッドで選び、
|
|
310
|
+
term_from / term_to を ISO yyyy-MM で送信する。
|
|
311
|
+
</CardDescription>
|
|
312
|
+
</CardHeader>
|
|
313
|
+
<CardContent>
|
|
314
|
+
<FormField id="term" label="対象期間">
|
|
315
|
+
<DatePicker
|
|
316
|
+
range
|
|
317
|
+
picker="month"
|
|
318
|
+
id="term"
|
|
319
|
+
name="term"
|
|
320
|
+
value={term}
|
|
321
|
+
onValueChange={setTerm}
|
|
322
|
+
minDate={new Date(2024, 0, 1)}
|
|
323
|
+
maxDate={new Date(2027, 11, 31)}
|
|
324
|
+
/>
|
|
325
|
+
</FormField>
|
|
326
|
+
</CardContent>
|
|
327
|
+
</Card>
|
|
328
|
+
<Card>
|
|
329
|
+
<CardHeader>
|
|
330
|
+
<CardTitle level={2}>期間プリセットと確定</CardTitle>
|
|
331
|
+
<CardDescription>
|
|
332
|
+
presets · needConfirm · allowEmpty は range でも同じ綴りで効く。
|
|
333
|
+
</CardDescription>
|
|
334
|
+
</CardHeader>
|
|
335
|
+
<CardContent>
|
|
336
|
+
<FormField id="preset-range" label="集計期間">
|
|
337
|
+
<DatePicker
|
|
338
|
+
range
|
|
339
|
+
id="preset-range"
|
|
340
|
+
name="report"
|
|
341
|
+
format="yyyy/MM/dd"
|
|
342
|
+
needConfirm
|
|
343
|
+
allowEmpty={[false, true]}
|
|
344
|
+
showWeek
|
|
345
|
+
minDate={new Date(2026, 0, 1)}
|
|
346
|
+
maxDate={new Date(2026, 11, 31)}
|
|
347
|
+
presets={[
|
|
348
|
+
{
|
|
349
|
+
label: "9月",
|
|
350
|
+
value: () => ({ from: new Date(2026, 8, 1), to: new Date(2026, 8, 30) }),
|
|
351
|
+
},
|
|
352
|
+
]}
|
|
353
|
+
/>
|
|
354
|
+
</FormField>
|
|
355
|
+
</CardContent>
|
|
356
|
+
</Card>
|
|
357
|
+
<Card>
|
|
358
|
+
<CardHeader>
|
|
359
|
+
<CardTitle level={2}>パネルの初期表示 (defaultPickerValue)</CardTitle>
|
|
360
|
+
<CardDescription>
|
|
361
|
+
値とは独立に「パネルがどの期間を開くか」を指定する。会計年度の開始月で開く、
|
|
362
|
+
編集中の行の月で開く、といった要求はこれでしか表現できない。antd と同じく
|
|
363
|
+
開くたびに再適用される。
|
|
364
|
+
</CardDescription>
|
|
365
|
+
</CardHeader>
|
|
366
|
+
<CardContent>
|
|
367
|
+
<FormField id="fiscal-open" label="会計年度開始月">
|
|
368
|
+
<DatePicker
|
|
369
|
+
id="fiscal-open"
|
|
370
|
+
name="fiscal_open"
|
|
371
|
+
picker="month"
|
|
372
|
+
defaultPickerValue={new Date(2027, 3, 1)}
|
|
373
|
+
/>
|
|
374
|
+
</FormField>
|
|
375
|
+
</CardContent>
|
|
376
|
+
</Card>
|
|
377
|
+
<Card>
|
|
378
|
+
<CardHeader>
|
|
379
|
+
<CardTitle level={2}>片側だけ固定した期間</CardTitle>
|
|
380
|
+
<CardDescription>
|
|
381
|
+
range のとき disabled は [from, to] のタプルを取り、片方だけロックできる。
|
|
382
|
+
「開始日は契約で確定、終了日だけ交渉中」がこれで表現できる。
|
|
383
|
+
</CardDescription>
|
|
384
|
+
</CardHeader>
|
|
385
|
+
<CardContent>
|
|
386
|
+
<FormField id="contract-period" label="契約期間">
|
|
387
|
+
<DatePicker
|
|
388
|
+
range
|
|
389
|
+
id="contract-period"
|
|
390
|
+
name="contract_period"
|
|
391
|
+
defaultValue={{ from: new Date(2026, 3, 1), to: new Date(2027, 2, 31) }}
|
|
392
|
+
disabled={[true, false]}
|
|
393
|
+
/>
|
|
394
|
+
</FormField>
|
|
395
|
+
</CardContent>
|
|
396
|
+
</Card>
|
|
397
|
+
<Card>
|
|
398
|
+
<CardHeader>
|
|
399
|
+
<CardTitle level={2}>disabled な期間</CardTitle>
|
|
400
|
+
<CardDescription>確定済み期間や読み取り専用フィールドに使用。</CardDescription>
|
|
401
|
+
</CardHeader>
|
|
402
|
+
<CardContent>
|
|
403
|
+
<FormField id="locked-period" label="確定期間">
|
|
404
|
+
<DatePicker
|
|
405
|
+
range
|
|
406
|
+
id="locked-period"
|
|
407
|
+
name="locked_period"
|
|
408
|
+
value={{ from: new Date(2025, 3, 1), to: new Date(2026, 2, 31) }}
|
|
409
|
+
disabled
|
|
410
|
+
/>
|
|
411
|
+
</FormField>
|
|
412
|
+
</CardContent>
|
|
413
|
+
</Card>
|
|
274
414
|
</Flex>
|
|
275
415
|
</PageContainer>
|
|
276
416
|
);
|
|
@@ -3,7 +3,6 @@ import { useState } from "react";
|
|
|
3
3
|
import {
|
|
4
4
|
Cascader,
|
|
5
5
|
DatePicker,
|
|
6
|
-
DateRangePicker,
|
|
7
6
|
Field,
|
|
8
7
|
Form,
|
|
9
8
|
FormField,
|
|
@@ -294,7 +293,8 @@ export default function Demo() {
|
|
|
294
293
|
/>
|
|
295
294
|
</FormField>
|
|
296
295
|
<FormField id="iv-period" label="対象期間">
|
|
297
|
-
<
|
|
296
|
+
<DatePicker
|
|
297
|
+
range
|
|
298
298
|
id="iv-period"
|
|
299
299
|
name="period"
|
|
300
300
|
value={v.period}
|
|
@@ -5,11 +5,9 @@ import {
|
|
|
5
5
|
CheckboxGroup,
|
|
6
6
|
ColorPicker,
|
|
7
7
|
DatePicker,
|
|
8
|
-
DateRangePicker,
|
|
9
8
|
Form,
|
|
10
9
|
FormField,
|
|
11
10
|
Input,
|
|
12
|
-
MonthPicker,
|
|
13
11
|
RadioGroup,
|
|
14
12
|
SearchInput,
|
|
15
13
|
Select,
|
|
@@ -129,14 +127,14 @@ export default function Demo() {
|
|
|
129
127
|
<FormField id="ac-date" label="取引日" helper="yyyy-MM-dd">
|
|
130
128
|
<DatePicker id="ac-date" name="date" />
|
|
131
129
|
</FormField>
|
|
132
|
-
<FormField id="ac-month" label="対象月" helper="yyyy
|
|
133
|
-
<
|
|
130
|
+
<FormField id="ac-month" label="対象月" helper="yyyy-MM">
|
|
131
|
+
<DatePicker picker="month" id="ac-month" name="month" />
|
|
134
132
|
</FormField>
|
|
135
133
|
<FormField id="ac-time" label="締め時刻" helper="24時間表記">
|
|
136
134
|
<TimePicker id="ac-time" name="time" />
|
|
137
135
|
</FormField>
|
|
138
136
|
<FormField id="ac-period" label="対象期間" helper="開始日と終了日">
|
|
139
|
-
<
|
|
137
|
+
<DatePicker range id="ac-period" name="period" />
|
|
140
138
|
</FormField>
|
|
141
139
|
<FormField id="ac-color" label="タグ色" helper="16進カラー">
|
|
142
140
|
<ColorPicker id="ac-color" onValueChange={() => {}} />
|
package/docs/data-entry/form.tsx
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
Checkbox,
|
|
6
6
|
CheckboxGroup,
|
|
7
7
|
DatePicker,
|
|
8
|
-
DateRangePicker,
|
|
9
8
|
Field,
|
|
10
9
|
Form,
|
|
11
10
|
FormErrors,
|
|
@@ -425,7 +424,7 @@ export default function Demo() {
|
|
|
425
424
|
|
|
426
425
|
<Card>
|
|
427
426
|
<CardHeader>
|
|
428
|
-
<CardTitle level={2}>日時系 · DatePicker /
|
|
427
|
+
<CardTitle level={2}>日時系 · DatePicker / DatePicker range / TimePicker</CardTitle>
|
|
429
428
|
<CardDescription>
|
|
430
429
|
単一日付・期間・時刻。ISO-8601 / IANA タイムゾーンに準拠し locale
|
|
431
430
|
連動でフォーマットする。
|
|
@@ -483,12 +482,13 @@ export default function Demo() {
|
|
|
483
482
|
/>
|
|
484
483
|
</FormField>
|
|
485
484
|
<FormField id="f-period" label="会計期間" colSpan={2}>
|
|
486
|
-
<
|
|
485
|
+
<DatePicker
|
|
486
|
+
range
|
|
487
487
|
id="f-period"
|
|
488
488
|
name="period"
|
|
489
489
|
value={period}
|
|
490
490
|
onValueChange={setPeriod}
|
|
491
|
-
// 土日は選べない。
|
|
491
|
+
// 土日は選べない。minDate/maxDate では表せない規則。
|
|
492
492
|
disabledDate={(date) => date.getDay() === 0 || date.getDay() === 6}
|
|
493
493
|
/>
|
|
494
494
|
</FormField>
|
|
@@ -223,6 +223,37 @@ export default function Demo() {
|
|
|
223
223
|
</CardContent>
|
|
224
224
|
</Card>
|
|
225
225
|
|
|
226
|
+
{/* prefix / suffix — all THREE combinations, because the two-affix case is the only one
|
|
227
|
+
that used to be exercised and the suffix-only one is where the bug lived. */}
|
|
228
|
+
<Card>
|
|
229
|
+
<CardHeader>
|
|
230
|
+
<CardTitle level={2}>prefix · suffix · 単位つきの数値</CardTitle>
|
|
231
|
+
<CardDescription>
|
|
232
|
+
通貨記号や単位はフィールドの内側に重ねる装飾。読み上げからは外れる (aria-hidden)
|
|
233
|
+
ので、意味は必ずラベル側に置く。
|
|
234
|
+
</CardDescription>
|
|
235
|
+
</CardHeader>
|
|
236
|
+
<CardContent>
|
|
237
|
+
<Flex direction="col" gap="md" id="number-input-affixes">
|
|
238
|
+
<FormField id="ni-prefix" label="金額(前置き)">
|
|
239
|
+
<NumberInput id="ni-prefix" prefix="¥" defaultValue={1980} aria-label="金額" />
|
|
240
|
+
</FormField>
|
|
241
|
+
<FormField id="ni-suffix" label="日数(後置き)">
|
|
242
|
+
<NumberInput id="ni-suffix" suffix="日" defaultValue={7} aria-label="日数" />
|
|
243
|
+
</FormField>
|
|
244
|
+
<FormField id="ni-both" label="率(前後)">
|
|
245
|
+
<NumberInput
|
|
246
|
+
id="ni-both"
|
|
247
|
+
prefix="+"
|
|
248
|
+
suffix="%"
|
|
249
|
+
defaultValue={12}
|
|
250
|
+
aria-label="率"
|
|
251
|
+
/>
|
|
252
|
+
</FormField>
|
|
253
|
+
</Flex>
|
|
254
|
+
</CardContent>
|
|
255
|
+
</Card>
|
|
256
|
+
|
|
226
257
|
<Card>
|
|
227
258
|
<CardHeader>
|
|
228
259
|
<Heading level={2}>キーボード操作</Heading>
|
|
@@ -2,7 +2,7 @@ import { useState } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
4
4
|
import { FormField, Segmented } from "@godxjp/ui/data-entry";
|
|
5
|
-
import { Text } from "@godxjp/ui/general";
|
|
5
|
+
import { Text, VisuallyHidden } from "@godxjp/ui/general";
|
|
6
6
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
7
7
|
import { CalendarDays, Columns3, LayoutList, Monitor, Moon, Sun } from "lucide-react";
|
|
8
8
|
|
|
@@ -84,6 +84,108 @@ export default function Demo() {
|
|
|
84
84
|
</CardContent>
|
|
85
85
|
</Card>
|
|
86
86
|
|
|
87
|
+
<Card>
|
|
88
|
+
<CardHeader>
|
|
89
|
+
<CardTitle level={2}>size · vertical · block</CardTitle>
|
|
90
|
+
<CardDescription>
|
|
91
|
+
size は 3 段(sm / md / lg)。トラックの高さは --control-height そのもので、
|
|
92
|
+
MobileShell のようにその変数をスコープする領域では自動的にタッチ段(44px)になります。
|
|
93
|
+
vertical では 1 行ぶんが 1 コントロールの高さになります — 横 1 行のときだけトラック
|
|
94
|
+
内側の余白を引くので、積んだときは引きません。block は幅いっぱいに広げます。
|
|
95
|
+
</CardDescription>
|
|
96
|
+
</CardHeader>
|
|
97
|
+
<CardContent>
|
|
98
|
+
<Flex direction="col" gap="md" align="start">
|
|
99
|
+
<Segmented
|
|
100
|
+
aria-label="サイズ sm"
|
|
101
|
+
size="sm"
|
|
102
|
+
defaultValue="a"
|
|
103
|
+
options={[
|
|
104
|
+
{ value: "a", label: "小" },
|
|
105
|
+
{ value: "b", label: "中" },
|
|
106
|
+
{ value: "c", label: "大" },
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
<Segmented
|
|
110
|
+
aria-label="サイズ md"
|
|
111
|
+
defaultValue="b"
|
|
112
|
+
options={[
|
|
113
|
+
{ value: "a", label: "小" },
|
|
114
|
+
{ value: "b", label: "中" },
|
|
115
|
+
{ value: "c", label: "大" },
|
|
116
|
+
]}
|
|
117
|
+
/>
|
|
118
|
+
<Segmented
|
|
119
|
+
aria-label="サイズ lg"
|
|
120
|
+
size="lg"
|
|
121
|
+
defaultValue="c"
|
|
122
|
+
options={[
|
|
123
|
+
{ value: "a", label: "小" },
|
|
124
|
+
{ value: "b", label: "中" },
|
|
125
|
+
{ value: "c", label: "大" },
|
|
126
|
+
]}
|
|
127
|
+
/>
|
|
128
|
+
<Segmented
|
|
129
|
+
aria-label="縦積み"
|
|
130
|
+
vertical
|
|
131
|
+
block
|
|
132
|
+
defaultValue="ok"
|
|
133
|
+
options={[
|
|
134
|
+
{ value: "ok", label: "実施" },
|
|
135
|
+
{ value: "warn", label: "要改善" },
|
|
136
|
+
{ value: "none", label: "未実施" },
|
|
137
|
+
]}
|
|
138
|
+
/>
|
|
139
|
+
</Flex>
|
|
140
|
+
</CardContent>
|
|
141
|
+
</Card>
|
|
142
|
+
|
|
143
|
+
<Card>
|
|
144
|
+
<CardHeader>
|
|
145
|
+
<CardTitle level={2}>短い記号を出して、長い名前を読ませる</CardTitle>
|
|
146
|
+
<CardDescription>
|
|
147
|
+
`label` は ReactNode なので、見える記号を aria-hidden にして VisuallyHidden に
|
|
148
|
+
読み上げ名を置けます。専用の prop は要りません。○ と読み上げられても意味が伝わらない
|
|
149
|
+
ため、色や記号だけに意味を持たせないという WCAG 1.4.1 の要求そのものです。
|
|
150
|
+
</CardDescription>
|
|
151
|
+
</CardHeader>
|
|
152
|
+
<CardContent>
|
|
153
|
+
<Segmented
|
|
154
|
+
aria-label="監査結果"
|
|
155
|
+
defaultValue="ok"
|
|
156
|
+
options={[
|
|
157
|
+
{
|
|
158
|
+
value: "ok",
|
|
159
|
+
label: (
|
|
160
|
+
<>
|
|
161
|
+
<span aria-hidden="true">○</span>
|
|
162
|
+
<VisuallyHidden>実施</VisuallyHidden>
|
|
163
|
+
</>
|
|
164
|
+
),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
value: "warn",
|
|
168
|
+
label: (
|
|
169
|
+
<>
|
|
170
|
+
<span aria-hidden="true">△</span>
|
|
171
|
+
<VisuallyHidden>要改善</VisuallyHidden>
|
|
172
|
+
</>
|
|
173
|
+
),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
value: "none",
|
|
177
|
+
label: (
|
|
178
|
+
<>
|
|
179
|
+
<span aria-hidden="true">×</span>
|
|
180
|
+
<VisuallyHidden>未実施</VisuallyHidden>
|
|
181
|
+
</>
|
|
182
|
+
),
|
|
183
|
+
},
|
|
184
|
+
]}
|
|
185
|
+
/>
|
|
186
|
+
</CardContent>
|
|
187
|
+
</Card>
|
|
188
|
+
|
|
87
189
|
<Card>
|
|
88
190
|
<CardHeader>
|
|
89
191
|
<CardTitle level={2}>フォームの中で</CardTitle>
|