@godxjp/ui 20.2.1 → 21.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/components/data-display/chat-bubble.d.ts +30 -0
- package/dist/components/data-display/chat-bubble.js +229 -0
- package/dist/components/data-display/descriptions.js +4 -1
- package/dist/components/data-display/index.d.ts +4 -2
- package/dist/components/data-display/index.js +5 -2
- package/dist/components/data-display/popover.js +3 -2
- 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/index.d.ts +4 -0
- package/dist/components/data-entry/index.js +4 -0
- 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/i18n/messages/en.json +45 -0
- package/dist/i18n/messages/ja.json +43 -0
- package/dist/i18n/messages/vi.json +44 -0
- package/dist/lib/tree.d.ts +53 -0
- package/dist/lib/tree.js +155 -0
- package/dist/props/components/data-display.prop.d.ts +187 -1
- package/dist/props/components/data-entry.prop.d.ts +127 -0
- package/dist/props/registry.d.ts +140 -3
- package/dist/props/registry.js +202 -3
- package/dist/styles/control.css +7 -0
- package/dist/styles/data-display-layout.css +309 -55
- package/dist/styles/data-entry-layout.css +64 -0
- package/dist/styles/shell-layout.css +2 -1
- 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 +0 -6
- package/dist/tokens/components/descriptions.css +4 -0
- package/dist/tokens/components/shell.css +1 -0
- package/dist/tokens/components/tree.css +27 -0
- package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
- package/docs/FRAME-COVERAGE-REPORT.md +8 -3
- package/docs/data-display/chat-bubble.tsx +397 -0
- package/docs/data-display/timeline.tsx +46 -0
- package/docs/data-display/tree.tsx +394 -0
- package/docs/data-entry/chat-composer.tsx +464 -0
- package/docs/data-entry/chat-suggestion.tsx +301 -0
- package/docs/roadmap/ai-chat-components.md +207 -0
- package/docs/roadmap/antd-parity.md +154 -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 +344 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
- package/docs/roadmap/parity-backlog.md +79 -0
- package/docs/roadmap/tree-components.md +151 -0
- package/docs/showcase/table-tree-rows.tsx +4 -4
- package/package.json +5 -3
- package/dist/components/data-display/tree-list.d.ts +0 -13
- package/dist/components/data-display/tree-list.js +0 -26
- package/docs/data-display/tree-list.tsx +0 -107
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Bot, RotateCcw, User } from "lucide-react";
|
|
3
|
+
import {
|
|
4
|
+
Avatar,
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardDescription,
|
|
8
|
+
CardFooter,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
ChatBubble,
|
|
12
|
+
ChatBubbleList,
|
|
13
|
+
type ChatBubbleProp,
|
|
14
|
+
type ChatMessageProp,
|
|
15
|
+
} from "@godxjp/ui/data-display";
|
|
16
|
+
import { formatAppTime } from "@godxjp/ui/datetime";
|
|
17
|
+
import { Button, Text } from "@godxjp/ui/general";
|
|
18
|
+
import { Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ChatBubble · ChatBubbleList — 会話の1発言と、その発言が流れるフィード。
|
|
22
|
+
*
|
|
23
|
+
* ChatBubbleList が持っているのは「一番下に貼りつく」挙動です。読み手がすでに最下部にいる間だけ
|
|
24
|
+
* 新着に追従し、履歴を読み返そうとスクロールを上げた瞬間に追従をやめて、戻るためのボタンを出しま
|
|
25
|
+
* す。読んでいる途中で最下部へ引き戻すのは、機械が勝手に文脈を変える操作(WCAG 3.2.5)です。
|
|
26
|
+
*
|
|
27
|
+
* 入力欄は ChatComposer(data-entry)が担当します。このページはフィード側だけを扱います。
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const ASSISTANT_AVATAR = (
|
|
31
|
+
<Avatar aria-hidden="true" appearance="tinted" shape="square">
|
|
32
|
+
<Bot />
|
|
33
|
+
</Avatar>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const USER_AVATAR = (
|
|
37
|
+
<Avatar aria-hidden="true" shape="square">
|
|
38
|
+
<User />
|
|
39
|
+
</Avatar>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/** 1つのフィードの見た目を1か所で決める。各メッセージ側の指定が常に優先されます。 */
|
|
43
|
+
const ROLES: Record<string, Partial<ChatBubbleProp>> = {
|
|
44
|
+
assistant: {
|
|
45
|
+
placement: "start",
|
|
46
|
+
variant: "filled",
|
|
47
|
+
header: "アシスタント",
|
|
48
|
+
avatar: ASSISTANT_AVATAR,
|
|
49
|
+
},
|
|
50
|
+
user: {
|
|
51
|
+
placement: "end",
|
|
52
|
+
variant: "outlined",
|
|
53
|
+
header: "自分",
|
|
54
|
+
avatar: USER_AVATAR,
|
|
55
|
+
},
|
|
56
|
+
system: {
|
|
57
|
+
placement: "start",
|
|
58
|
+
variant: "borderless",
|
|
59
|
+
tone: "info",
|
|
60
|
+
size: "sm",
|
|
61
|
+
header: "システム",
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Times are formatted INSIDE the component, not at module scope: formatAppTime reads the
|
|
67
|
+
* AppProvider's locale and IANA timezone, and at module-evaluation time that context has not been
|
|
68
|
+
* synced yet — a module-level call silently formats against the pre-provider default and drifts
|
|
69
|
+
* from every other time on the page.
|
|
70
|
+
*/
|
|
71
|
+
const AT = (hhmm: string) => formatAppTime(`2026-09-10T${hhmm}:00+07:00`);
|
|
72
|
+
|
|
73
|
+
const conversation = (): ChatMessageProp[] => [
|
|
74
|
+
{
|
|
75
|
+
id: "c1",
|
|
76
|
+
role: "assistant",
|
|
77
|
+
content: "おはようございます。今日は何をお手伝いしましょうか。",
|
|
78
|
+
footer: AT("09:02"),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "c2",
|
|
82
|
+
role: "user",
|
|
83
|
+
content: "先週受領した請求書のうち、まだ承認されていないものを一覧にしてください。",
|
|
84
|
+
footer: AT("09:03"),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "c3",
|
|
88
|
+
role: "system",
|
|
89
|
+
content: "検索ツールを実行しました(対象 128 件 / 該当 7 件)。",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "c4",
|
|
93
|
+
role: "assistant",
|
|
94
|
+
content:
|
|
95
|
+
"未承認の請求書は 7 件です。合計 1,284,000 円。\n最も古いものは 9月1日受領の株式会社青葉商事 分で、支払期限まであと 3 日です。",
|
|
96
|
+
footer: AT("09:03"),
|
|
97
|
+
typing: { step: 2, interval: 40 },
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "c5",
|
|
101
|
+
role: "user",
|
|
102
|
+
content: "その 7 件を承認待ちのまま経理へ回してください。",
|
|
103
|
+
footer: AT("09:05"),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "c6",
|
|
107
|
+
role: "assistant",
|
|
108
|
+
loading: true,
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
/** 上へ戻って読み返せるだけの長さがあるフィード。追従が切れる様子を実際に確かめる用。 */
|
|
113
|
+
const LONG_THREAD: ChatMessageProp[] = Array.from({ length: 24 }, (_, index) => ({
|
|
114
|
+
id: `t${String(index)}`,
|
|
115
|
+
role: index % 2 === 0 ? "assistant" : "user",
|
|
116
|
+
content:
|
|
117
|
+
index % 2 === 0
|
|
118
|
+
? `${String(index + 1)} 件目の集計が終わりました。差異は見つかりません。`
|
|
119
|
+
: `${String(index + 1)} 件目もお願いします。`,
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
const RTL_THREAD: ChatMessageProp[] = [
|
|
123
|
+
{ id: "r1", role: "assistant", content: "كيف يمكنني المساعدة؟" },
|
|
124
|
+
{ id: "r2", role: "user", content: "لخّص فواتير الأسبوع الماضي." },
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
const LTR_THREAD: ChatMessageProp[] = [
|
|
128
|
+
{ id: "l1", role: "assistant", content: "How can I help?" },
|
|
129
|
+
{ id: "l2", role: "user", content: "Summarise last week's invoices." },
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const ARCHIVE: ChatMessageProp[] = Array.from({ length: 12 }, (_, index) => ({
|
|
133
|
+
id: `a${String(index)}`,
|
|
134
|
+
role: index % 2 === 0 ? "user" : "assistant",
|
|
135
|
+
content:
|
|
136
|
+
index % 2 === 0
|
|
137
|
+
? `${String(index + 1)} 番目の質問です。`
|
|
138
|
+
: `${String(index + 1)} 番目の回答です。記録として保存されています。`,
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
export default function Demo() {
|
|
142
|
+
// 「新しい会話」を押すと typing がもう一度先頭から流れる。id が変わる = 別の発言。
|
|
143
|
+
const [run, setRun] = React.useState(0);
|
|
144
|
+
const live = React.useMemo(
|
|
145
|
+
() => conversation().map((m) => (m.id === "c4" ? { ...m, id: `c4-${String(run)}` } : m)),
|
|
146
|
+
[run],
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<PageContainer
|
|
151
|
+
title="ChatBubble · ChatBubbleList"
|
|
152
|
+
subtitle="会話の1発言とフィード · 最下部への貼りつき · typing / loading · placement は論理軸"
|
|
153
|
+
>
|
|
154
|
+
<Flex direction="col" gap="lg">
|
|
155
|
+
<Card>
|
|
156
|
+
<CardHeader>
|
|
157
|
+
<CardTitle level={2}>アシスタントの会話</CardTitle>
|
|
158
|
+
<CardDescription>
|
|
159
|
+
roles で assistant / user / system の既定値を1か所に書き、items は中身だけを持ちま
|
|
160
|
+
す。最後の発言は loading(Skeleton と aria-busy、読み上げ用の文言つき)、その1つ前は
|
|
161
|
+
typing で1文字ずつ流れます。prefers-reduced-motion: reduce の環境では、アニメーショ
|
|
162
|
+
ンは動かず全文がそのまま表示されます。時刻は formatAppTime(Intl.DateTimeFormat と
|
|
163
|
+
AppProvider のタイムゾーン)で整形しています。
|
|
164
|
+
</CardDescription>
|
|
165
|
+
</CardHeader>
|
|
166
|
+
<CardContent>
|
|
167
|
+
<Card variant="outline">
|
|
168
|
+
<CardContent flush>
|
|
169
|
+
<ChatBubbleList
|
|
170
|
+
className="h-96"
|
|
171
|
+
label="アシスタントとの会話"
|
|
172
|
+
items={live}
|
|
173
|
+
roles={ROLES}
|
|
174
|
+
/>
|
|
175
|
+
</CardContent>
|
|
176
|
+
</Card>
|
|
177
|
+
</CardContent>
|
|
178
|
+
<CardFooter>
|
|
179
|
+
<Flex gap="sm" wrap>
|
|
180
|
+
<Button type="button" variant="outline" onClick={() => setRun((n) => n + 1)}>
|
|
181
|
+
<RotateCcw aria-hidden="true" />
|
|
182
|
+
返答をもう一度流す
|
|
183
|
+
</Button>
|
|
184
|
+
<Text size="xs" tone="muted">
|
|
185
|
+
入力欄は ChatComposer(data-entry)が担当します。
|
|
186
|
+
</Text>
|
|
187
|
+
</Flex>
|
|
188
|
+
</CardFooter>
|
|
189
|
+
</Card>
|
|
190
|
+
|
|
191
|
+
<Card>
|
|
192
|
+
<CardHeader>
|
|
193
|
+
<CardTitle level={2}>最下部への貼りつきと、戻るためのボタン</CardTitle>
|
|
194
|
+
<CardDescription>
|
|
195
|
+
下のフィードを少しだけ上へスクロールしてください。追従がその場で止まり、
|
|
196
|
+
フィードの下端に「最新へ移動」ボタンが出ます。離れている間に届いた件数は
|
|
197
|
+
Intl.PluralRules で数えて文言に入ります。ボタンはフォーカスできる本物のボタンなの
|
|
198
|
+
で、キーボードだけでも最新へ戻れます。自動で引き戻すことは一度もありません。
|
|
199
|
+
</CardDescription>
|
|
200
|
+
</CardHeader>
|
|
201
|
+
<CardContent>
|
|
202
|
+
<Card variant="outline">
|
|
203
|
+
<CardContent flush>
|
|
204
|
+
<ChatBubbleList
|
|
205
|
+
className="h-64"
|
|
206
|
+
label="集計の進捗"
|
|
207
|
+
items={LONG_THREAD}
|
|
208
|
+
roles={ROLES}
|
|
209
|
+
/>
|
|
210
|
+
</CardContent>
|
|
211
|
+
</Card>
|
|
212
|
+
</CardContent>
|
|
213
|
+
</Card>
|
|
214
|
+
|
|
215
|
+
<Card>
|
|
216
|
+
<CardHeader>
|
|
217
|
+
<CardTitle level={2}>
|
|
218
|
+
placement は論理軸(dir="rtl" で自動的に反転)
|
|
219
|
+
</CardTitle>
|
|
220
|
+
<CardDescription>
|
|
221
|
+
同じ items と同じ roles を、左が dir="ltr"、右が dir="rtl" で描
|
|
222
|
+
いています。placement="end" の自分の発言は、ltr では右、rtl では左に寄り
|
|
223
|
+
ます。ロケールで分岐したコードは1行もありません。
|
|
224
|
+
</CardDescription>
|
|
225
|
+
</CardHeader>
|
|
226
|
+
<CardContent>
|
|
227
|
+
<ResponsiveGrid columns={{ base: 1, md: 2 }} gap="md">
|
|
228
|
+
<Card variant="outline">
|
|
229
|
+
<CardContent flush>
|
|
230
|
+
<div dir="ltr">
|
|
231
|
+
<ChatBubbleList
|
|
232
|
+
className="h-48"
|
|
233
|
+
label="ltr conversation"
|
|
234
|
+
items={LTR_THREAD}
|
|
235
|
+
roles={ROLES}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
</CardContent>
|
|
239
|
+
</Card>
|
|
240
|
+
<Card variant="outline">
|
|
241
|
+
<CardContent flush>
|
|
242
|
+
<div dir="rtl">
|
|
243
|
+
<ChatBubbleList
|
|
244
|
+
className="h-48"
|
|
245
|
+
label="محادثة"
|
|
246
|
+
items={RTL_THREAD}
|
|
247
|
+
roles={ROLES}
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
</CardContent>
|
|
251
|
+
</Card>
|
|
252
|
+
</ResponsiveGrid>
|
|
253
|
+
</CardContent>
|
|
254
|
+
</Card>
|
|
255
|
+
|
|
256
|
+
<Card>
|
|
257
|
+
<CardHeader>
|
|
258
|
+
<CardTitle level={2}>variant · 3つすべて</CardTitle>
|
|
259
|
+
<CardDescription>
|
|
260
|
+
variant は構造の軸です。filled は既定の面、outlined は 1px の枠だけ、borderless は
|
|
261
|
+
面も枠も持たず本文としてそのまま読ませます。色で状態を示すのは tone の役目で、 variant
|
|
262
|
+
には状態を表す値がありません。Ant Design X の shadow は、このデザインシステ
|
|
263
|
+
ムが影を使わないため意図的にありません。
|
|
264
|
+
</CardDescription>
|
|
265
|
+
</CardHeader>
|
|
266
|
+
<CardContent>
|
|
267
|
+
<Flex direction="col" gap="md">
|
|
268
|
+
<ChatBubble variant="filled" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
269
|
+
filled · 既定の面。相手の発言に使います。
|
|
270
|
+
</ChatBubble>
|
|
271
|
+
<ChatBubble variant="outlined" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
272
|
+
outlined · 面を持たず 1px の枠だけ。
|
|
273
|
+
</ChatBubble>
|
|
274
|
+
<ChatBubble variant="borderless" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
275
|
+
borderless · 長い回答を本文として読ませたいとき。面も枠もありません。
|
|
276
|
+
</ChatBubble>
|
|
277
|
+
<ChatBubble variant="filled" placement="end" header="自分" avatar={USER_AVATAR}>
|
|
278
|
+
filled · placement="end" は自分の発言用の色を持ちます。
|
|
279
|
+
</ChatBubble>
|
|
280
|
+
</Flex>
|
|
281
|
+
</CardContent>
|
|
282
|
+
</Card>
|
|
283
|
+
|
|
284
|
+
<Card>
|
|
285
|
+
<CardHeader>
|
|
286
|
+
<CardTitle level={2}>size · xs / sm / md / lg</CardTitle>
|
|
287
|
+
<CardDescription>
|
|
288
|
+
size は文字の段と内側の余白を同時に動かします。既定は md。"default" という
|
|
289
|
+
値はありません。
|
|
290
|
+
</CardDescription>
|
|
291
|
+
</CardHeader>
|
|
292
|
+
<CardContent>
|
|
293
|
+
<Flex direction="col" gap="md">
|
|
294
|
+
<ChatBubble size="xs" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
295
|
+
xs · 監査ログのように密度を上げたいとき。
|
|
296
|
+
</ChatBubble>
|
|
297
|
+
<ChatBubble size="sm" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
298
|
+
sm · システムの注記など、会話の脇に置くもの。
|
|
299
|
+
</ChatBubble>
|
|
300
|
+
<ChatBubble size="md" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
301
|
+
md · 既定。ふつうの会話。
|
|
302
|
+
</ChatBubble>
|
|
303
|
+
<ChatBubble size="lg" header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
304
|
+
lg · 画面いっぱいのアシスタント面で、長文を読ませるとき。
|
|
305
|
+
</ChatBubble>
|
|
306
|
+
</Flex>
|
|
307
|
+
</CardContent>
|
|
308
|
+
</Card>
|
|
309
|
+
|
|
310
|
+
<Card>
|
|
311
|
+
<CardHeader>
|
|
312
|
+
<CardTitle level={2}>tone · 色だけで状態を伝えない</CardTitle>
|
|
313
|
+
<CardDescription>
|
|
314
|
+
tone は会話そのものではない発言(送信失敗、上限の警告、ツールの結果)のための状態
|
|
315
|
+
です。Alert と同じ役割色を薄く敷き、同時に読み上げ用の状態語(情報 / 成功 / 警告 /
|
|
316
|
+
エラー)を必ず入れます。色を見分けられない読み手にも状態が届きます。
|
|
317
|
+
</CardDescription>
|
|
318
|
+
</CardHeader>
|
|
319
|
+
<CardContent>
|
|
320
|
+
<Flex direction="col" gap="md">
|
|
321
|
+
<ChatBubble header="アシスタント" avatar={ASSISTANT_AVATAR}>
|
|
322
|
+
default · 状態なし。ふつうの発言です。
|
|
323
|
+
</ChatBubble>
|
|
324
|
+
<ChatBubble tone="info" variant="outlined" size="sm" header="システム">
|
|
325
|
+
info · モデルを gpt-5 に切り替えました。
|
|
326
|
+
</ChatBubble>
|
|
327
|
+
<ChatBubble tone="success" variant="outlined" size="sm" header="システム">
|
|
328
|
+
success · 7 件の請求書を経理へ回しました。
|
|
329
|
+
</ChatBubble>
|
|
330
|
+
<ChatBubble tone="warning" variant="outlined" size="sm" header="システム">
|
|
331
|
+
warning · 今月の利用上限の 90% に達しています。
|
|
332
|
+
</ChatBubble>
|
|
333
|
+
<ChatBubble tone="destructive" placement="end" header="自分" avatar={USER_AVATAR}>
|
|
334
|
+
destructive · 送信できませんでした。もう一度お試しください。
|
|
335
|
+
</ChatBubble>
|
|
336
|
+
</Flex>
|
|
337
|
+
</CardContent>
|
|
338
|
+
</Card>
|
|
339
|
+
|
|
340
|
+
<Card>
|
|
341
|
+
<CardHeader>
|
|
342
|
+
<CardTitle level={2}>loading と typing</CardTitle>
|
|
343
|
+
<CardDescription>
|
|
344
|
+
loading は「返答を頼んだが、まだ届いていない」状態です。Skeleton の棒は装飾なので
|
|
345
|
+
支援技術からは隠し、代わりに aria-busy と読み上げ用の1行を置きます。typing は文字列
|
|
346
|
+
の本文だけを流します(ReactNode には流す単位がないので、そのまま全部出ます)。
|
|
347
|
+
流れている間の途中の文字は読み上げ対象から外れ、完成した本文だけが1回だけ読み上げ
|
|
348
|
+
られます。
|
|
349
|
+
</CardDescription>
|
|
350
|
+
</CardHeader>
|
|
351
|
+
<CardContent>
|
|
352
|
+
<Flex direction="col" gap="md">
|
|
353
|
+
<ChatBubble header="アシスタント" avatar={ASSISTANT_AVATAR} loading />
|
|
354
|
+
<ChatBubble
|
|
355
|
+
key={`typing-${String(run)}`}
|
|
356
|
+
header="アシスタント"
|
|
357
|
+
avatar={ASSISTANT_AVATAR}
|
|
358
|
+
typing={{ step: 1, interval: 60 }}
|
|
359
|
+
footer={AT("09:12")}
|
|
360
|
+
>
|
|
361
|
+
typing · 1文字ずつ流れます。上の「返答をもう一度流す」でやり直せます。
|
|
362
|
+
</ChatBubble>
|
|
363
|
+
<ChatBubble header="アシスタント" avatar={ASSISTANT_AVATAR} typing>
|
|
364
|
+
<Text size="sm">
|
|
365
|
+
ReactNode の本文。typing を渡しても流れず、そのまま表示されます。
|
|
366
|
+
</Text>
|
|
367
|
+
</ChatBubble>
|
|
368
|
+
</Flex>
|
|
369
|
+
</CardContent>
|
|
370
|
+
</Card>
|
|
371
|
+
|
|
372
|
+
<Card>
|
|
373
|
+
<CardHeader>
|
|
374
|
+
<CardTitle level={2}>autoScroll={false} · 読み終えた記録</CardTitle>
|
|
375
|
+
<CardDescription>
|
|
376
|
+
保管された記録を引用から開くときは、最新の発言に飛ばされたくありません。autoScroll
|
|
377
|
+
を切ると、フィードは先頭のまま開き、新しい要素が増えても動きません。
|
|
378
|
+
</CardDescription>
|
|
379
|
+
</CardHeader>
|
|
380
|
+
<CardContent>
|
|
381
|
+
<Card variant="outline">
|
|
382
|
+
<CardContent flush>
|
|
383
|
+
<ChatBubbleList
|
|
384
|
+
className="h-64"
|
|
385
|
+
label="保管された会話"
|
|
386
|
+
items={ARCHIVE}
|
|
387
|
+
roles={ROLES}
|
|
388
|
+
autoScroll={false}
|
|
389
|
+
/>
|
|
390
|
+
</CardContent>
|
|
391
|
+
</Card>
|
|
392
|
+
</CardContent>
|
|
393
|
+
</Card>
|
|
394
|
+
</Flex>
|
|
395
|
+
</PageContainer>
|
|
396
|
+
);
|
|
397
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
RangeTimeline,
|
|
10
10
|
type TimelineItem,
|
|
11
11
|
} from "@godxjp/ui/data-display";
|
|
12
|
+
import { Text } from "@godxjp/ui/general";
|
|
12
13
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -53,6 +54,37 @@ const approvalItems: TimelineItem[] = [
|
|
|
53
54
|
{ title: "消費税を計上", location: "経理", status: "pending" },
|
|
54
55
|
];
|
|
55
56
|
|
|
57
|
+
// Fourth shape: `title` is a NODE WITH A WIDTH OF ITS OWN, which is the only shape that can
|
|
58
|
+
// show whether the title actually spans the row. Every case above passes a bare string, and a
|
|
59
|
+
// string is already narrower than the row, so a title that shrink-wraps looks identical to one
|
|
60
|
+
// that fills — the defect hides in plain sight. A Card as the title makes the width visible; the
|
|
61
|
+
// truncating id is the other half, a title whose intrinsic width EXCEEDS the row, which must
|
|
62
|
+
// shrink rather than push `time` off the end. Measured by check:timeline-title-fill.
|
|
63
|
+
const richItems: TimelineItem[] = [
|
|
64
|
+
{
|
|
65
|
+
title: (
|
|
66
|
+
<Card>
|
|
67
|
+
<CardContent>
|
|
68
|
+
<Text weight="medium">請求書 INV-2026-0912 を承認しました</Text>
|
|
69
|
+
</CardContent>
|
|
70
|
+
</Card>
|
|
71
|
+
),
|
|
72
|
+
time: "11:05",
|
|
73
|
+
status: "done",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
title: (
|
|
77
|
+
<Text truncate mono>
|
|
78
|
+
INV-2026-09-0001-APPROVAL-CHAIN-SEGMENT-0007-RECONCILIATION
|
|
79
|
+
</Text>
|
|
80
|
+
),
|
|
81
|
+
// A full CJK timestamp on purpose: it breaks between any two glyphs, so nothing floors its
|
|
82
|
+
// width and a title with an `auto` flex-basis would squeeze it onto three lines.
|
|
83
|
+
time: "2026年9月12日 11:18",
|
|
84
|
+
status: "current",
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
56
88
|
export default function Demo() {
|
|
57
89
|
const { t, locale } = useTranslation();
|
|
58
90
|
const dayLabel = (day: number) =>
|
|
@@ -113,6 +145,20 @@ export default function Demo() {
|
|
|
113
145
|
<Timeline variant="status" items={approvalItems} />
|
|
114
146
|
</CardContent>
|
|
115
147
|
</Card>
|
|
148
|
+
<Card>
|
|
149
|
+
<CardHeader>
|
|
150
|
+
<CardTitle level={2}>タイトルにノードを渡す</CardTitle>
|
|
151
|
+
<CardDescription>
|
|
152
|
+
title は ReactNode です。自身の幅を持つノード(Card
|
|
153
|
+
など)を渡しても行いっぱいに広がり、内容が行より広い場合は time
|
|
154
|
+
を押し出さずに切り詰められます。
|
|
155
|
+
</CardDescription>
|
|
156
|
+
</CardHeader>
|
|
157
|
+
<CardContent>
|
|
158
|
+
<Timeline variant="status" items={richItems} />
|
|
159
|
+
</CardContent>
|
|
160
|
+
</Card>
|
|
161
|
+
|
|
116
162
|
<Card>
|
|
117
163
|
<CardHeader>
|
|
118
164
|
<CardTitle level={2}>RangeTimeline</CardTitle>
|