@godxjp/ui 20.2.1 → 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/chat-bubble.d.ts +30 -0
- package/dist/components/data-display/chat-bubble.js +229 -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/descriptions.js +4 -1
- package/dist/components/data-display/index.d.ts +5 -3
- package/dist/components/data-display/index.js +5 -2
- package/dist/components/data-display/popover.d.ts +19 -1
- package/dist/components/data-display/popover.js +12 -3
- package/dist/components/data-display/progress.d.ts +37 -0
- package/dist/components/data-display/progress.js +57 -5
- package/dist/components/data-display/scroll-area.js +26 -1
- package/dist/components/data-display/tree.d.ts +8 -0
- package/dist/components/data-display/tree.js +426 -0
- package/dist/components/data-entry/chat-composer.d.ts +50 -0
- package/dist/components/data-entry/chat-composer.js +163 -0
- package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
- package/dist/components/data-entry/chat-suggestion.js +285 -0
- 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 +4 -6
- package/dist/components/data-entry/index.js +4 -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/data-entry/textarea.js +3 -1
- package/dist/components/data-entry/tree-utils.d.ts +10 -48
- package/dist/components/data-entry/tree-utils.js +1 -154
- 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 +48 -2
- package/dist/i18n/messages/ja.json +46 -2
- package/dist/i18n/messages/vi.json +47 -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/lib/tree.d.ts +53 -0
- package/dist/lib/tree.js +155 -0
- package/dist/props/components/data-display.prop.d.ts +194 -1
- package/dist/props/components/data-entry.prop.d.ts +307 -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 +156 -19
- package/dist/props/registry.js +218 -18
- 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 +22 -6
- package/dist/styles/data-display-layout.css +385 -68
- package/dist/styles/data-entry-layout.css +64 -0
- package/dist/styles/focus-ring.css +4 -2
- package/dist/styles/navigation-layout.css +25 -0
- package/dist/styles/shell-layout.css +7 -1
- package/dist/styles/table-layout.css +34 -0
- package/dist/tokens/base.css +3 -0
- package/dist/tokens/components/chat-bubble.css +36 -0
- package/dist/tokens/components/chat-composer.css +19 -0
- package/dist/tokens/components/data-display.css +9 -6
- package/dist/tokens/components/descriptions.css +4 -0
- package/dist/tokens/components/navigation.css +6 -0
- package/dist/tokens/components/segmented.css +0 -2
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/components/table.css +3 -0
- package/dist/tokens/components/tree.css +27 -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-LEDGER.md +1 -1
- package/docs/FRAME-COVERAGE-REPORT.md +9 -7
- package/docs/TOKENS.md +65 -0
- package/docs/data-display/badge.tsx +37 -0
- package/docs/data-display/chat-bubble.tsx +397 -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-display/timeline.tsx +46 -0
- package/docs/data-display/tree.tsx +394 -0
- package/docs/data-entry/calendar.tsx +2 -2
- package/docs/data-entry/chat-composer.tsx +464 -0
- package/docs/data-entry/chat-suggestion.tsx +301 -0
- 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/ai-chat-components.md +207 -0
- package/docs/roadmap/antd-parity.md +155 -0
- package/docs/roadmap/badge-tag-chip-count.md +172 -0
- package/docs/roadmap/list-masonry.md +159 -0
- package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
- package/docs/roadmap/parity-audit-data-entry.md +357 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
- package/docs/roadmap/parity-backlog.md +78 -0
- package/docs/roadmap/tree-components.md +151 -0
- package/docs/showcase/acme-portal.tsx +7 -0
- package/docs/showcase/table-tree-rows.tsx +4 -4
- package/package.json +13 -23
- package/scripts/ui-audit.mjs +17 -4
- package/dist/components/data-display/tree-list.d.ts +0 -13
- package/dist/components/data-display/tree-list.js +0 -26
- 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-display/tree-list.tsx +0 -107
- 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
|
@@ -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>
|
|
@@ -32,6 +32,11 @@ export default function Demo() {
|
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
34
|
<MobileShell
|
|
35
|
+
// `width="phone"` because this frame is READ ON A DESKTOP. The shell's own docstring already
|
|
36
|
+
// named this case on the block axis (`height="fill"` for "a phone view embedded in a wider
|
|
37
|
+
// page"); the inline axis had no answer until now, so this page drew a 1232px-wide handheld
|
|
38
|
+
// app with its four tab-bar destinations spread across the screen.
|
|
39
|
+
width="phone"
|
|
35
40
|
statusBar={
|
|
36
41
|
<>
|
|
37
42
|
<Text size="sm" weight="medium" tabular>
|
package/docs/navigation/tabs.tsx
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { Trash2 } from "lucide-react";
|
|
4
|
+
|
|
3
5
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
4
6
|
import { Button, Text } from "@godxjp/ui/general";
|
|
5
7
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
@@ -44,8 +46,45 @@ const journalItems = [
|
|
|
44
46
|
},
|
|
45
47
|
];
|
|
46
48
|
|
|
49
|
+
/**
|
|
50
|
+
* 20 saved views with real Japanese labels — a strip that is wider than 1920px, so it overflows at
|
|
51
|
+
* EVERY width the browser gates sweep. `scripts/check-tabs-overflow-menu.mjs` measures this frame;
|
|
52
|
+
* it fails if the strip ever stops overflowing, because a gate over a bar that fits proves nothing.
|
|
53
|
+
*/
|
|
54
|
+
const savedViewItems = [
|
|
55
|
+
{ value: "all", label: "すべての仕訳", content: <Text as="p">全 1,284 件</Text> },
|
|
56
|
+
{ value: "pending", label: "未承認の仕訳", content: <Text as="p">未承認 2 件</Text> },
|
|
57
|
+
{ value: "posted", label: "承認済の仕訳", content: <Text as="p">承認済 48 件</Text> },
|
|
58
|
+
{ value: "voided", label: "取消済の仕訳", content: <Text as="p">取消済 3 件</Text> },
|
|
59
|
+
{ value: "returned", label: "差戻しの仕訳", content: <Text as="p">差戻し 5 件</Text> },
|
|
60
|
+
{ value: "draft", label: "下書きの仕訳", content: <Text as="p">下書き 12 件</Text> },
|
|
61
|
+
{ value: "recurring", label: "定期仕訳のひな形", content: <Text as="p">ひな形 7 件</Text> },
|
|
62
|
+
{ value: "accrual", label: "未払費用の計上", content: <Text as="p">未払費用 9 件</Text> },
|
|
63
|
+
{ value: "prepaid", label: "前払費用の振替", content: <Text as="p">前払費用 4 件</Text> },
|
|
64
|
+
{ value: "payroll", label: "給与支払の仕訳", content: <Text as="p">給与 3 件</Text> },
|
|
65
|
+
{ value: "tax", label: "消費税の集計", content: <Text as="p">消費税 6 件</Text> },
|
|
66
|
+
{ value: "closing", label: "決算整理の仕訳", content: <Text as="p">決算整理 11 件</Text> },
|
|
67
|
+
{ value: "audit", label: "監査対象の仕訳", content: <Text as="p">監査対象 8 件</Text> },
|
|
68
|
+
{
|
|
69
|
+
value: "archived",
|
|
70
|
+
label: "アーカイブ済の仕訳",
|
|
71
|
+
content: <Text as="p">アーカイブ 214 件</Text>,
|
|
72
|
+
},
|
|
73
|
+
{ value: "fx", label: "外貨建の換算差額", content: <Text as="p">換算差額 2 件</Text> },
|
|
74
|
+
{ value: "intercompany", label: "関係会社間の取引", content: <Text as="p">関係会社 15 件</Text> },
|
|
75
|
+
{
|
|
76
|
+
value: "fixed-asset",
|
|
77
|
+
label: "固定資産の減価償却",
|
|
78
|
+
content: <Text as="p">減価償却 22 件</Text>,
|
|
79
|
+
},
|
|
80
|
+
{ value: "inventory", label: "棚卸資産の評価替", content: <Text as="p">評価替 6 件</Text> },
|
|
81
|
+
{ value: "bank", label: "銀行勘定の照合", content: <Text as="p">未照合 4 件</Text> },
|
|
82
|
+
{ value: "reversal", label: "翌期首の振戻し", content: <Text as="p">振戻し 9 件</Text> },
|
|
83
|
+
];
|
|
84
|
+
|
|
47
85
|
export default function Demo() {
|
|
48
86
|
const [activeTab, setActiveTab] = useState("pending");
|
|
87
|
+
const [reopened, setReopened] = useState(0);
|
|
49
88
|
const [editableTabs, setEditableTabs] = useState([
|
|
50
89
|
{ value: "je-0042", label: "JE-0042", content: <Text as="p">売上計上 ¥480,000</Text> },
|
|
51
90
|
{ value: "je-0043", label: "JE-0043", content: <Text as="p">仕入計上 ¥120,000</Text> },
|
|
@@ -367,6 +406,116 @@ export default function Demo() {
|
|
|
367
406
|
</CardContent>
|
|
368
407
|
</Card>
|
|
369
408
|
|
|
409
|
+
{/* antd `more` — overflow="menu" beside the default overflow="scroll" */}
|
|
410
|
+
<Card>
|
|
411
|
+
<CardHeader>
|
|
412
|
+
<CardTitle level={2}>overflow · scroll(既定) / menu</CardTitle>
|
|
413
|
+
<CardDescription>
|
|
414
|
+
antd の more をこのライブラリの overflow 語彙に写したもの。既定の scroll
|
|
415
|
+
は今までどおり、帯が自分の水平オーバーフローをスクロールする。menu
|
|
416
|
+
はそれに加えて、帯の外に「他のタブ」ボタンを出し、いま見えていないタブだけを並べる ·
|
|
417
|
+
antd と違いタブは帯から取り除かれない(tablist はタブ以外を持てず、display:none
|
|
418
|
+
のタブはロービングフォーカスを受け取れないため · WAI-ARIA APG)。
|
|
419
|
+
</CardDescription>
|
|
420
|
+
</CardHeader>
|
|
421
|
+
<CardContent>
|
|
422
|
+
<Flex direction="col" gap="lg">
|
|
423
|
+
<Flex direction="col" gap="sm">
|
|
424
|
+
<Text as="p" size="sm" tone="muted">
|
|
425
|
+
overflow="scroll"(既定)
|
|
426
|
+
</Text>
|
|
427
|
+
<Tabs
|
|
428
|
+
id="antd-overflow-scroll"
|
|
429
|
+
defaultValue="all"
|
|
430
|
+
variant="line"
|
|
431
|
+
items={savedViewItems}
|
|
432
|
+
/>
|
|
433
|
+
</Flex>
|
|
434
|
+
<Flex direction="col" gap="sm">
|
|
435
|
+
<Text as="p" size="sm" tone="muted">
|
|
436
|
+
overflow="menu"
|
|
437
|
+
</Text>
|
|
438
|
+
<Tabs
|
|
439
|
+
id="antd-overflow-menu"
|
|
440
|
+
defaultValue="all"
|
|
441
|
+
variant="line"
|
|
442
|
+
overflow="menu"
|
|
443
|
+
items={savedViewItems}
|
|
444
|
+
/>
|
|
445
|
+
</Flex>
|
|
446
|
+
</Flex>
|
|
447
|
+
</CardContent>
|
|
448
|
+
</Card>
|
|
449
|
+
|
|
450
|
+
{/* antd onTabClick / removeIcon / Tab.forceRender */}
|
|
451
|
+
<Card>
|
|
452
|
+
<CardHeader>
|
|
453
|
+
<CardTitle level={2}>onTabClick · closeIcon · forceRender</CardTitle>
|
|
454
|
+
<CardDescription>
|
|
455
|
+
onTabClick は antd の onTabClick で、ポインタで押されたときだけ発火し DOM の
|
|
456
|
+
MouseEvent を渡す(キーボードは activationMode="manual"
|
|
457
|
+
でフォーカス移動と選択が分かれるため発火しない · 選択は onValueChange
|
|
458
|
+
が担当)。closeIcon は antd の removeIcon で、タブ全体の既定グリフを差し替える(item
|
|
459
|
+
側の closeIcon が優先)。item の forceRender は antd の Tab.forceRender
|
|
460
|
+
で、そのパネルだけを先にマウントし、他のタブに 切り替えても保持する。
|
|
461
|
+
</CardDescription>
|
|
462
|
+
</CardHeader>
|
|
463
|
+
<CardContent>
|
|
464
|
+
<Flex direction="col" gap="md">
|
|
465
|
+
<Text as="p" size="sm" tone="muted">
|
|
466
|
+
同じタブを押し直した回数: {reopened}
|
|
467
|
+
</Text>
|
|
468
|
+
<Tabs
|
|
469
|
+
id="antd-tab-click"
|
|
470
|
+
defaultValue="pending"
|
|
471
|
+
variant="line"
|
|
472
|
+
onTabClick={(value) => {
|
|
473
|
+
if (value === activeTab) setReopened((count) => count + 1);
|
|
474
|
+
setActiveTab(value);
|
|
475
|
+
}}
|
|
476
|
+
items={[
|
|
477
|
+
{
|
|
478
|
+
value: "pending",
|
|
479
|
+
label: "未承認",
|
|
480
|
+
content: <Text as="p">未承認の仕訳が 2 件あります。</Text>,
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
value: "chart",
|
|
484
|
+
label: "推移グラフ",
|
|
485
|
+
// The one panel that must survive a tab switch — antd Tab.forceRender.
|
|
486
|
+
forceRender: true,
|
|
487
|
+
content: <Text as="p">月次推移: 4 月 ¥3,120,000 · 5 月 ¥4,820,000</Text>,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
value: "posted",
|
|
491
|
+
label: "承認済",
|
|
492
|
+
content: <Text as="p">当期承認済: 48 件</Text>,
|
|
493
|
+
},
|
|
494
|
+
]}
|
|
495
|
+
/>
|
|
496
|
+
<Tabs
|
|
497
|
+
id="antd-remove-icon"
|
|
498
|
+
variant="editable-card"
|
|
499
|
+
defaultValue="je-0042"
|
|
500
|
+
closeIcon={<Trash2 aria-hidden="true" />}
|
|
501
|
+
onEdit={() => undefined}
|
|
502
|
+
items={[
|
|
503
|
+
{
|
|
504
|
+
value: "je-0042",
|
|
505
|
+
label: "JE-0042",
|
|
506
|
+
content: <Text as="p">売上計上 ¥480,000</Text>,
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
value: "je-0043",
|
|
510
|
+
label: "JE-0043",
|
|
511
|
+
content: <Text as="p">仕入計上 ¥120,000</Text>,
|
|
512
|
+
},
|
|
513
|
+
]}
|
|
514
|
+
/>
|
|
515
|
+
</Flex>
|
|
516
|
+
</CardContent>
|
|
517
|
+
</Card>
|
|
518
|
+
|
|
370
519
|
<Card>
|
|
371
520
|
<CardHeader>
|
|
372
521
|
<CardTitle level={2}>tabPlacement · size · centered</CardTitle>
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# AI / chatbot components — normalized spec
|
|
2
|
+
|
|
3
|
+
> **Reference:** Ant Design X (<https://x.ant.design/components/overview>) for *capabilities*.
|
|
4
|
+
> **Naming:** when a component's identity is unclear, look it up at
|
|
5
|
+
> <https://namethatui.com/?platform=web> before inventing a name.
|
|
6
|
+
> **Contract:** `.claude/skills/godxjp-ui-component/SKILL.md` is the hard gate — MCP-first, real
|
|
7
|
+
> primitives only, `t()` + `Intl`, WAI-ARIA APG + WCAG 2.2 AA + vitest-axe, logical CSS, controlled
|
|
8
|
+
> vocabulary, semantic tokens, MCP catalog entry, real-screen docs page.
|
|
9
|
+
|
|
10
|
+
The library has **zero** chat/AI components today (verified against `mcp/src/data/components.ts` —
|
|
11
|
+
127 entries, none of them conversational). This document decides which of Ant Design X's surface
|
|
12
|
+
becomes a framework component and which is a composition, so no agent has to re-litigate it.
|
|
13
|
+
|
|
14
|
+
## 1. Scope decision — GATE 0 applied to every Ant Design X component
|
|
15
|
+
|
|
16
|
+
`docs/COMPOSITION-VS-COMPONENT.md` C1–C7. **Any FAIL ⇒ composition, never `src/components/`.**
|
|
17
|
+
|
|
18
|
+
| Ant Design X | godx name | Group | C1 | C2 | C3 | C4 | C5 | C6 | C7 | Verdict |
|
|
19
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
20
|
+
| `Bubble` | **`ChatBubble`** | `data-display` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** |
|
|
21
|
+
| `Bubble.List` | **`ChatBubbleList`** | `data-display` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** |
|
|
22
|
+
| `Sender` | **`ChatComposer`** | `data-entry` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** |
|
|
23
|
+
| `Suggestion` | **`ChatSuggestion`** | `data-entry` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Framework component** |
|
|
24
|
+
| `Conversations` | **`ConversationList`** | `navigation` | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | ✅ | **Verify C3 first** — see §6 |
|
|
25
|
+
| `ThoughtChain` / `Think` | **`ThoughtChain`** | `data-display` | ✅ | ⚠️ | ⚠️ | ✅ | ✅ | ✅ | ⚠️ | **Verify C2/C3 first** — see §6 |
|
|
26
|
+
| `Prompts` | — | — | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | **Composition** — `ResponsiveGrid` + `Card` + `Button` |
|
|
27
|
+
| `Welcome` | — | — | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | **Composition** — static block, `EmptyState`-shaped |
|
|
28
|
+
| `Actions` | — | — | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | **Composition** — `Button` + `DropdownMenu` |
|
|
29
|
+
| `Attachments` / `FileCard` | — | — | ✅ | ✅ | ❌ | — | — | — | — | **Extend `Upload`** — duplicating it is an instant reject |
|
|
30
|
+
| `CodeHighlighter` | — | — | — | — | ❌ | — | — | — | — | **Use `CodeBlock`** |
|
|
31
|
+
| `Sources` | — | — | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | **Composition** — `ListRow` + `Badge` |
|
|
32
|
+
| `XProvider` | — | — | — | — | ❌ | — | — | — | — | **Use `AppProvider`** |
|
|
33
|
+
| `Mermaid` | — | — | ❌ | — | — | — | — | — | ❌ | **Out of scope** |
|
|
34
|
+
| `Folder/File Tree` | — | — | — | — | ❌ | — | — | — | — | **Covered by `Tree`** — see `docs/roadmap/tree-components.md` |
|
|
35
|
+
|
|
36
|
+
**v1 build set: `ChatBubble`, `ChatBubbleList`, `ChatComposer`, `ChatSuggestion`** (4 components,
|
|
37
|
+
all 7/7 PASS). `ConversationList` and `ThoughtChain` are conditional — §6.
|
|
38
|
+
Everything else ships as a `docs/` showcase composition, not library code.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 2. `ChatBubble` — one message in a conversation
|
|
43
|
+
|
|
44
|
+
**Group:** `data-display` · **File:** `src/components/data-display/chat-bubble.tsx`
|
|
45
|
+
|
|
46
|
+
| Ant Design X | **godx `ChatBubble`** |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `content` | `children` (a `ReactNode`; do not take an HTML string) |
|
|
49
|
+
| `placement: 'start' \| 'end'` | `placement: 'start' \| 'end'` (logical — flips under RTL) |
|
|
50
|
+
| `variant: 'filled' \| 'borderless' \| 'outlined' \| 'shadow'` | `variant: 'filled' \| 'borderless' \| 'outlined'` (drop `shadow`; the design system is 1px-border, no drop shadows) |
|
|
51
|
+
| `avatar` | `avatar` (a real `Avatar` node, never a styled div) |
|
|
52
|
+
| `header` / `footer` | `header` / `footer` |
|
|
53
|
+
| `loading` | `loading` (renders `Skeleton`, `aria-busy="true"`) |
|
|
54
|
+
| `typing` | `typing: boolean \| { step?: number; interval?: number }` |
|
|
55
|
+
| `messageRender` | *dropped* — the caller passes rendered `children` |
|
|
56
|
+
| `shape` | *dropped* — corner radius is a token |
|
|
57
|
+
| — | `size: 'xs' \| 'sm' \| 'lg'` (`md` default) |
|
|
58
|
+
| — | `tone` for status-tinted bubbles (error/warning), never colour-only |
|
|
59
|
+
|
|
60
|
+
**Semantics.** The bubble is an `<article>` inside the list's feed. Give it an accessible name from
|
|
61
|
+
its `header` (the author). The typing animation MUST respect `prefers-reduced-motion: reduce` —
|
|
62
|
+
render the full text immediately, no animation. Streaming text goes in an `aria-live="polite"`
|
|
63
|
+
region owned by `ChatBubbleList`, **not** per-bubble (a live region per bubble floods a screen
|
|
64
|
+
reader). Decorative avatars `aria-hidden`.
|
|
65
|
+
|
|
66
|
+
**Tokens** (`src/tokens/components/chat-bubble.css`): `--chat-bubble-background`,
|
|
67
|
+
`--chat-bubble-foreground`, `--chat-bubble-border-color`, `--chat-bubble-radius`,
|
|
68
|
+
`--chat-bubble-gap`, `--chat-bubble-max-inline-size`, `--chat-bubble-avatar-size`.
|
|
69
|
+
Role-mirror knobs default to `initial` (`docs/TOKENS.md`).
|
|
70
|
+
|
|
71
|
+
**i18n:** `chat.bubble.typing`, `chat.bubble.loading`, `chat.bubble.you`, `chat.bubble.assistant`.
|
|
72
|
+
Timestamps via `Intl.DateTimeFormat`; "2 minutes ago" via `Intl.RelativeTimeFormat`; counted nouns
|
|
73
|
+
via `Intl.PluralRules` — never a template string.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 3. `ChatBubbleList` — the message feed
|
|
78
|
+
|
|
79
|
+
**Group:** `data-display` · **File:** same module as `ChatBubble`
|
|
80
|
+
|
|
81
|
+
| Ant Design X `Bubble.List` | **godx `ChatBubbleList`** |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `items` | `items: ChatMessageProp[]` |
|
|
84
|
+
| `roles` | `roles: Record<string, Partial<ChatBubbleProp>>` — per-role defaults (`user`, `assistant`, `system`) |
|
|
85
|
+
| `autoScroll` | `autoScroll` (default `true`) |
|
|
86
|
+
|
|
87
|
+
**The behavior that earns C2/C3** — none of it is composable from `ScrollArea` + `map`:
|
|
88
|
+
|
|
89
|
+
1. **Stick-to-bottom.** Auto-scroll while the user is at the bottom; the moment they scroll up,
|
|
90
|
+
**stop** and surface a "jump to latest" affordance. Silently yanking a reader back to the bottom
|
|
91
|
+
mid-read is the classic chat-UI defect.
|
|
92
|
+
2. **Streaming without layout thrash** — appended text must not re-scroll the whole feed.
|
|
93
|
+
3. **One `aria-live="polite"` region** for the whole feed, announcing only the newest message.
|
|
94
|
+
4. `role="log"` on the scroll container, `aria-label` via `t()`.
|
|
95
|
+
5. Keyboard: the feed is focusable and scrollable with `PageUp`/`PageDown`/`Home`/`End`; bubbles
|
|
96
|
+
are not a roving-tabindex widget (interactive controls inside them keep normal tab order).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 4. `ChatComposer` — the message input (Ant's `Sender`)
|
|
101
|
+
|
|
102
|
+
**Group:** `data-entry` · **File:** `src/components/data-entry/chat-composer.tsx`
|
|
103
|
+
|
|
104
|
+
Named for what the industry calls it (Slack/Discord "composer"); `Sender` describes the person,
|
|
105
|
+
not the control.
|
|
106
|
+
|
|
107
|
+
| Ant Design X `Sender` | **godx `ChatComposer`** |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `value` / `onChange` | `value` / `defaultValue` / `onValueChange` (**controlled triad — mandatory**) |
|
|
110
|
+
| `onSubmit` | `onSubmit(value: string)` |
|
|
111
|
+
| `loading` + `onCancel` | `loading` + `onCancel` |
|
|
112
|
+
| `submitType: 'enter' \| 'shiftEnter'` | `submitType: 'enter' \| 'shiftEnter'` |
|
|
113
|
+
| `placeholder` | `placeholder` (through `t()` at the call site) |
|
|
114
|
+
| `disabled` / `readOnly` | `disabled` / `readOnly` |
|
|
115
|
+
| `header` (`Sender.Header`) | `header` slot |
|
|
116
|
+
| `prefix` / `footer` / `actions` | `prefix` / `footer` / `actions` slots |
|
|
117
|
+
| `allowSpeech` | **out of scope v1** — Web Speech API is not cross-browser; file a follow-up |
|
|
118
|
+
| `autoSize` | always auto-sizing; not a prop |
|
|
119
|
+
| — | `size: 'xs' \| 'sm' \| 'lg'` · `maxLength` · `status` |
|
|
120
|
+
|
|
121
|
+
**Rules.**
|
|
122
|
+
- Built on the real `Textarea` primitive and real `Button`s — **no raw `<textarea>`/`<button>`**.
|
|
123
|
+
- Auto-grow between a min and max height expressed in `--control-height` tiers, then scroll
|
|
124
|
+
inside. **No literal `height` and no `calc(var(--control-height) ± …)`** (`check:control-sizing`).
|
|
125
|
+
- `submitType="enter"`: `Enter` submits, `Shift+Enter` newline. `submitType="shiftEnter"`: the
|
|
126
|
+
inverse. IME composition must never submit — guard on `compositionstart`/`compositionend`
|
|
127
|
+
(this is the bug that breaks Japanese and Vietnamese input; it is not optional).
|
|
128
|
+
- Empty or whitespace-only input does not submit; the submit button is `disabled` with an
|
|
129
|
+
accessible name, not merely greyed out.
|
|
130
|
+
- While `loading`, the submit button becomes a cancel button — **one trailing action at a time**,
|
|
131
|
+
the same discipline as the picker trailing-action rule.
|
|
132
|
+
- The textarea is the semantic focus target; forward the `FormField` label/helper/error contract
|
|
133
|
+
onto it via `pickFieldA11y` / `useFieldIdentity` — do not reinvent it (`src/lib/field-a11y.ts`).
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 5. `ChatSuggestion` — trigger-character autocomplete over the composer
|
|
138
|
+
|
|
139
|
+
**Group:** `data-entry` · **File:** `src/components/data-entry/chat-suggestion.tsx`
|
|
140
|
+
|
|
141
|
+
| Ant Design X `Suggestion` | **godx `ChatSuggestion`** |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `items` | `items: SuggestionItemProp[]` (supports one level of `children`) |
|
|
144
|
+
| `onSelect` | `onValueChange(value: string)` |
|
|
145
|
+
| `block` | *dropped* — width follows the anchor |
|
|
146
|
+
| render-prop `children({ onTrigger, onKeyDown })` | same shape — it wraps `ChatComposer` |
|
|
147
|
+
| — | `triggerCharacter` (default `'/'`) · `open` / `defaultOpen` / `onOpenChange` |
|
|
148
|
+
|
|
149
|
+
**Rules.** Compose the existing `Command` (cmdk) inside a `Popover` anchored to the composer — do
|
|
150
|
+
**not** hand-roll a listbox; `Command` already ships correct ARIA. What this component owns is the
|
|
151
|
+
part `Command` does not: detecting the trigger character in a textarea, tracking the query as the
|
|
152
|
+
caret moves, and closing on `Escape`/blur/word-break. `Escape` returns focus to the textarea and
|
|
153
|
+
leaves the typed text intact.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 6. Conditional components — prove C3 before writing code
|
|
158
|
+
|
|
159
|
+
Do **not** start these until the ledger is recorded in the PR description.
|
|
160
|
+
|
|
161
|
+
**`ConversationList`** (Ant's `Conversations`). `NavList` already renders selectable rows with
|
|
162
|
+
`aria-current="page"`, an icon column and a badge; `ListRow` covers short entity lists. Before
|
|
163
|
+
building, answer in writing: what does a conversation list own that `NavList` does not?
|
|
164
|
+
The honest candidates are date **grouping** (Today / Yesterday / Last 7 days — via
|
|
165
|
+
`Intl.RelativeTimeFormat` + `Intl.DateTimeFormat`, never hand-rolled buckets), a **per-item
|
|
166
|
+
overflow menu**, and **inline rename**. If those land as `NavList` props instead, that is the
|
|
167
|
+
better outcome — extend `NavList` and close this out.
|
|
168
|
+
|
|
169
|
+
**`ThoughtChain`** (Ant's `ThoughtChain` / `Think`). `Timeline` already has a 3-state per-item
|
|
170
|
+
`status` (`done`/`current`/`pending`), an icon rail and ordinal/status variants; `Collapsible`
|
|
171
|
+
owns disclosure. `ThoughtChain` = `Timeline` + per-item collapsible body + a streaming/pending
|
|
172
|
+
state. Strongly prefer adding a `collapsible` affordance to `Timeline` over a new component.
|
|
173
|
+
Whichever way it goes, the reasoning body must be collapsible, and the running step must be
|
|
174
|
+
announced once via `aria-live="polite"` — not on every token.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 7. Per-component definition of done
|
|
179
|
+
|
|
180
|
+
Every component in the build set ships **all** of:
|
|
181
|
+
|
|
182
|
+
1. `src/components/<group>/<name>.tsx` — real primitives only, logical CSS, `t()` for every string
|
|
183
|
+
and `aria-label`, `ref` forwarded, `...props` spread, `className` + `id` accepted.
|
|
184
|
+
2. `XProp` + `XProp as XProps` in `src/props/components/<group>.prop.ts`, **registered in
|
|
185
|
+
`src/props/registry.ts`**.
|
|
186
|
+
3. `src/tokens/components/<name>.css` + an `@import` in `src/tokens/base.css`; names pass
|
|
187
|
+
`check:token-tiers`; control boxes come from the `--control-height` tier.
|
|
188
|
+
4. Keys in `src/i18n/messages/en.json`, `vi.json`, `ja.json` — all three, no exceptions.
|
|
189
|
+
5. Tests in `src/components/<group>/__tests__/`: a behavior test using `@testing-library/user-event`
|
|
190
|
+
**and** a `<name>.a11y.test.tsx` with `expectNoA11yViolations` at **0 violations**.
|
|
191
|
+
6. An `mcp/src/data/components.ts` entry (props / usage / useCases / related / example / rules) —
|
|
192
|
+
`check:mcp-sync` and `check:mcp-orphans` must pass.
|
|
193
|
+
7. A real-screen docs page under `docs/<group>/` (AppShell + PageContainer + real primitives), not
|
|
194
|
+
a bare `Card` snippet.
|
|
195
|
+
8. Export from the group's `index.ts`.
|
|
196
|
+
|
|
197
|
+
Gates, then **only** the touched group's tests:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
pnpm typecheck && pnpm lint && pnpm run audit \
|
|
201
|
+
&& pnpm check:prop-vocabulary && pnpm check:mcp-sync && pnpm check:mcp-orphans \
|
|
202
|
+
&& pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:example-imports
|
|
203
|
+
pnpm vitest run src/components/<group>/__tests__ --maxWorkers=2
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`pnpm test` and a bare `pnpm vitest run` are **forbidden** — 506 files / 3700+ tests, and several
|
|
207
|
+
agents on one machine takes the load past 90. The full suite is CI's job on the PR.
|