@godxjp/ui 27.1.0 → 27.4.1
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/badge.d.ts +10 -3
- package/dist/components/data-display/badge.js +14 -3
- package/dist/components/data-display/data-table.d.ts +20 -12
- package/dist/components/data-display/data-table.js +53 -5
- package/dist/components/data-display/range-timeline.d.ts +16 -0
- package/dist/components/data-display/range-timeline.js +115 -80
- package/dist/components/data-display/table.d.ts +16 -0
- package/dist/components/data-display/table.js +2 -0
- package/dist/components/data-entry/checkbox.js +23 -5
- package/dist/components/data-entry/command.d.ts +3 -1
- package/dist/components/data-entry/command.js +10 -2
- package/dist/components/data-entry/form-field.js +9 -8
- package/dist/components/data-entry/index.d.ts +1 -0
- package/dist/components/data-entry/search-select.js +145 -136
- package/dist/components/data-entry/select.js +38 -3
- package/dist/components/general/icon.d.ts +31 -0
- package/dist/components/general/icon.js +23 -0
- package/dist/components/general/index.d.ts +2 -0
- package/dist/components/general/index.js +2 -0
- package/dist/components/layout/flex.d.ts +2 -2
- package/dist/components/layout/flex.js +7 -1
- package/dist/components/layout/org-switcher.js +1 -1
- package/dist/components/layout/responsive-grid.d.ts +8 -2
- package/dist/components/layout/responsive-grid.js +2 -0
- package/dist/components/layout/topbar-item.d.ts +1 -0
- package/dist/components/layout/topbar-item.js +3 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/form/form-field-control.d.ts +1 -1
- package/dist/form/form-field-control.js +32 -23
- package/dist/i18n/messages/en.json +2 -1
- package/dist/i18n/messages/ja.json +2 -1
- package/dist/i18n/messages/vi.json +2 -1
- package/dist/props/components/data-display.prop.d.ts +12 -0
- package/dist/props/components/data-entry.prop.d.ts +10 -0
- package/dist/props/components/form.prop.d.ts +34 -2
- package/dist/props/components/general.prop.d.ts +42 -1
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +49 -0
- package/dist/props/registry.d.ts +57 -3
- package/dist/props/registry.js +62 -2
- package/dist/props/vocabulary/data.prop.d.ts +27 -1
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +11 -0
- package/dist/styles/control.css +44 -1
- package/dist/styles/core.css +2 -0
- package/dist/styles/data-display-layout.css +58 -1
- package/dist/styles/form-layout.css +21 -0
- package/dist/styles/icon-layout.css +37 -0
- package/dist/styles/index.css +2 -0
- package/dist/styles/layout.css +7 -0
- package/dist/styles/navigation-layout.css +5 -1
- package/dist/styles/shell-layout.css +11 -14
- package/dist/styles/table-layout.css +80 -8
- package/dist/styles/text-layout.css +16 -8
- package/dist/tokens/components/control.css +5 -0
- package/dist/tokens/components/data-display.css +6 -0
- package/dist/tokens/components/navigation.css +1 -1
- package/dist/tokens/components/table.css +2 -0
- package/docs/CONSUMER-RULES.md +2 -2
- package/docs/DESIGN-AUTHORITY.md +32 -1
- package/docs/FRAME-COVERAGE-REPORT.md +3 -2
- package/docs/data-display/data-table/examples/server-paged.tsx +156 -0
- package/docs/data-display/data-table/examples/striped-list.tsx +173 -0
- package/docs/data-display/popover.tsx +43 -1
- package/docs/data-display/timeline.tsx +73 -9
- package/docs/data-entry/command.tsx +80 -1
- package/docs/data-entry/form-root.tsx +12 -2
- package/docs/general/icon.tsx +215 -0
- package/docs/layout/flex.tsx +59 -0
- package/docs/layout/responsive-grid.tsx +100 -0
- package/docs/layout/topbar-item.tsx +29 -1
- package/package.json +3 -2
- package/scripts/consumer-rule.md +18 -17
- package/scripts/ui-audit.mjs +128 -0
|
@@ -91,6 +91,10 @@ export default function Demo() {
|
|
|
91
91
|
new Intl.DateTimeFormat(locale, { month: "short", day: "numeric", timeZone: "UTC" }).format(
|
|
92
92
|
Date.UTC(2026, 8, day),
|
|
93
93
|
);
|
|
94
|
+
const monthLabel = (month: number) =>
|
|
95
|
+
new Intl.DateTimeFormat(locale, { year: "numeric", month: "long", timeZone: "UTC" }).format(
|
|
96
|
+
Date.UTC(2026, month, 1),
|
|
97
|
+
);
|
|
94
98
|
return (
|
|
95
99
|
<PageContainer title="Timeline" subtitle="現在のステップを強調する縦型のイベント一覧">
|
|
96
100
|
<Flex direction="col" gap="lg">
|
|
@@ -162,23 +166,83 @@ export default function Demo() {
|
|
|
162
166
|
<Card>
|
|
163
167
|
<CardHeader>
|
|
164
168
|
<CardTitle level={2}>RangeTimeline</CardTitle>
|
|
169
|
+
<CardDescription>
|
|
170
|
+
日単位の軸。行ごとの区切り線と、列ごとの縦線が本体全体に引かれます(bordered は既定で
|
|
171
|
+
true)。土日は columns[].muted
|
|
172
|
+
で本体の高さいっぱいに塗り分け、表示範囲の外にある期間は、その方向の端に矢印付きで示します。
|
|
173
|
+
</CardDescription>
|
|
165
174
|
</CardHeader>
|
|
166
175
|
<CardContent>
|
|
167
176
|
<RangeTimeline
|
|
168
177
|
label={t("rangeTimeline.schedule")}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
+
today={8}
|
|
179
|
+
bands={[{ label: monthLabel(8), units: 14 }]}
|
|
180
|
+
columns={Array.from({ length: 14 }, (_, index) => {
|
|
181
|
+
const weekday = new Date(Date.UTC(2026, 8, index + 1)).getUTCDay();
|
|
182
|
+
return {
|
|
183
|
+
label: new Intl.NumberFormat(locale).format(index + 1),
|
|
184
|
+
units: 1,
|
|
185
|
+
muted: weekday === 0 || weekday === 6,
|
|
186
|
+
};
|
|
187
|
+
})}
|
|
188
|
+
rows={[
|
|
189
|
+
{ id: "design", label: "要件定義", start: 0, end: 3 },
|
|
190
|
+
{ id: "build", label: "実装", start: 3, end: 10 },
|
|
191
|
+
{ id: "review", label: "レビュー", start: 9, end: 12 },
|
|
192
|
+
{ id: "kickoff", label: "キックオフ(前月)", start: -12, end: -9 },
|
|
193
|
+
{ id: "release", label: "リリース(来月)", start: 20, end: 22 },
|
|
194
|
+
].map((row) => ({
|
|
195
|
+
...row,
|
|
196
|
+
startLabel: dayLabel(row.start + 1),
|
|
197
|
+
endLabel: dayLabel(row.end + 1),
|
|
198
|
+
}))}
|
|
199
|
+
/>
|
|
200
|
+
</CardContent>
|
|
201
|
+
</Card>
|
|
202
|
+
|
|
203
|
+
<Card>
|
|
204
|
+
<CardHeader>
|
|
205
|
+
<CardTitle level={2}>月単位の軸(列ごとに units が異なる)</CardTitle>
|
|
206
|
+
<CardDescription>
|
|
207
|
+
units
|
|
208
|
+
は日数です。9月30・10月31・11月30と幅が異なっても、本体の縦線はヘッダーの列の境界に揃います。
|
|
209
|
+
</CardDescription>
|
|
210
|
+
</CardHeader>
|
|
211
|
+
<CardContent>
|
|
212
|
+
<RangeTimeline
|
|
213
|
+
label={t("rangeTimeline.schedule")}
|
|
214
|
+
columns={[
|
|
215
|
+
{ label: monthLabel(8), units: 30 },
|
|
216
|
+
{ label: monthLabel(9), units: 31 },
|
|
217
|
+
{ label: monthLabel(10), units: 30 },
|
|
178
218
|
]}
|
|
219
|
+
rows={[
|
|
220
|
+
{ id: "phase-1", label: "第1期", start: 0, end: 44 },
|
|
221
|
+
{ id: "phase-2", label: "第2期", start: 45, end: 90 },
|
|
222
|
+
].map((row) => ({
|
|
223
|
+
...row,
|
|
224
|
+
startLabel: dayLabel(row.start + 1),
|
|
225
|
+
endLabel: dayLabel(row.end + 1),
|
|
226
|
+
}))}
|
|
227
|
+
/>
|
|
228
|
+
</CardContent>
|
|
229
|
+
</Card>
|
|
230
|
+
|
|
231
|
+
<Card>
|
|
232
|
+
<CardHeader>
|
|
233
|
+
<CardTitle level={2}>罫線なし(bordered=false)</CardTitle>
|
|
234
|
+
<CardDescription>
|
|
235
|
+
ヘッダーの列線だけを残し、本体の罫線を外します。muted の列の塗りは残ります。
|
|
236
|
+
</CardDescription>
|
|
237
|
+
</CardHeader>
|
|
238
|
+
<CardContent>
|
|
239
|
+
<RangeTimeline
|
|
240
|
+
label={t("rangeTimeline.schedule")}
|
|
241
|
+
bordered={false}
|
|
179
242
|
columns={Array.from({ length: 7 }, (_, index) => ({
|
|
180
243
|
label: dayLabel(index + 1),
|
|
181
244
|
units: 1,
|
|
245
|
+
muted: index === 5,
|
|
182
246
|
}))}
|
|
183
247
|
rows={[
|
|
184
248
|
{
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
4
3
|
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
CardDescription,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
Popover,
|
|
10
|
+
PopoverContent,
|
|
11
|
+
PopoverTrigger,
|
|
12
|
+
} from "@godxjp/ui/data-display";
|
|
13
|
+
import {
|
|
14
|
+
Checkbox,
|
|
5
15
|
Command,
|
|
6
16
|
CommandEmpty,
|
|
7
17
|
CommandGroup,
|
|
@@ -10,7 +20,9 @@ import {
|
|
|
10
20
|
CommandList,
|
|
11
21
|
} from "@godxjp/ui/data-entry";
|
|
12
22
|
import { Skeleton } from "@godxjp/ui/feedback";
|
|
23
|
+
import { Button } from "@godxjp/ui/general";
|
|
13
24
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
25
|
+
import { ChevronDown } from "lucide-react";
|
|
14
26
|
|
|
15
27
|
/**
|
|
16
28
|
* Command — キーボードナビゲーション対応のコマンドパレット。
|
|
@@ -30,6 +42,14 @@ const VENDOR_DIRECTORY: Suggestion[] = [
|
|
|
30
42
|
{ value: "v-005", label: "田中物産" },
|
|
31
43
|
];
|
|
32
44
|
|
|
45
|
+
const MEMBERS: Suggestion[] = [
|
|
46
|
+
{ value: "m-001", label: "山田 太郎" },
|
|
47
|
+
{ value: "m-002", label: "佐藤 花子" },
|
|
48
|
+
{ value: "m-003", label: "鈴木 一郎" },
|
|
49
|
+
{ value: "m-004", label: "高橋 美咲" },
|
|
50
|
+
{ value: "m-005", label: "PHAM THAI DUONG(プロジェクト外)" },
|
|
51
|
+
];
|
|
52
|
+
|
|
33
53
|
export default function Demo() {
|
|
34
54
|
// Card 1: grouped quick-select. Controlled `value` pre-highlights an item so the
|
|
35
55
|
// [aria-selected=true] accent is visible at rest, without keyboard interaction.
|
|
@@ -44,6 +64,13 @@ export default function Demo() {
|
|
|
44
64
|
const [loading, setLoading] = useState<boolean>(true);
|
|
45
65
|
const [results, setResults] = useState<Suggestion[]>([]);
|
|
46
66
|
|
|
67
|
+
// Card 4: filter facet — a checklist, so the list is `split` (ruled rows, 0 list padding).
|
|
68
|
+
const [assignees, setAssignees] = useState<string[]>(["m-002"]);
|
|
69
|
+
const toggleAssignee = (member: string) =>
|
|
70
|
+
setAssignees((current) =>
|
|
71
|
+
current.includes(member) ? current.filter((m) => m !== member) : [...current, member],
|
|
72
|
+
);
|
|
73
|
+
|
|
47
74
|
useEffect(() => {
|
|
48
75
|
let active = true;
|
|
49
76
|
setLoading(true);
|
|
@@ -131,6 +158,58 @@ export default function Demo() {
|
|
|
131
158
|
</CardContent>
|
|
132
159
|
</Card>
|
|
133
160
|
|
|
161
|
+
<Card>
|
|
162
|
+
<CardHeader>
|
|
163
|
+
<CardTitle level={2}>担当者フィルター(split)</CardTitle>
|
|
164
|
+
<CardDescription>
|
|
165
|
+
Popover + Command に split を付けると、行間に区切り線・リスト余白 0・行は popover
|
|
166
|
+
の端まで届く一つの箱として描画されます。チェックボックスで複数選択(選択中:{" "}
|
|
167
|
+
{assignees.length} 名)。
|
|
168
|
+
</CardDescription>
|
|
169
|
+
</CardHeader>
|
|
170
|
+
<CardContent>
|
|
171
|
+
<Popover>
|
|
172
|
+
<PopoverTrigger asChild>
|
|
173
|
+
<Button variant="outline">
|
|
174
|
+
担当者{assignees.length > 0 ? ` (${assignees.length})` : ""}
|
|
175
|
+
<ChevronDown aria-hidden="true" />
|
|
176
|
+
</Button>
|
|
177
|
+
</PopoverTrigger>
|
|
178
|
+
<PopoverContent flush align="start">
|
|
179
|
+
<Command label="担当者を絞り込む" split>
|
|
180
|
+
<CommandInput placeholder="担当者 を絞り込む" />
|
|
181
|
+
<CommandList>
|
|
182
|
+
<CommandEmpty>該当する担当者がいません。</CommandEmpty>
|
|
183
|
+
<CommandGroup>
|
|
184
|
+
{MEMBERS.map((member) => {
|
|
185
|
+
const checked = assignees.includes(member.value);
|
|
186
|
+
return (
|
|
187
|
+
<CommandItem
|
|
188
|
+
key={member.value}
|
|
189
|
+
value={member.label}
|
|
190
|
+
aria-checked={checked}
|
|
191
|
+
onSelect={() => toggleAssignee(member.value)}
|
|
192
|
+
>
|
|
193
|
+
{/* The ROW is the option and owns the click; the box only mirrors it,
|
|
194
|
+
so it leaves the tab ring (TreeSelect does the same). */}
|
|
195
|
+
<Checkbox
|
|
196
|
+
checked={checked}
|
|
197
|
+
tabIndex={-1}
|
|
198
|
+
onCheckedChange={() => toggleAssignee(member.value)}
|
|
199
|
+
aria-labelledby={`assignee-${member.value}`}
|
|
200
|
+
/>
|
|
201
|
+
<span id={`assignee-${member.value}`}>{member.label}</span>
|
|
202
|
+
</CommandItem>
|
|
203
|
+
);
|
|
204
|
+
})}
|
|
205
|
+
</CommandGroup>
|
|
206
|
+
</CommandList>
|
|
207
|
+
</Command>
|
|
208
|
+
</PopoverContent>
|
|
209
|
+
</Popover>
|
|
210
|
+
</CardContent>
|
|
211
|
+
</Card>
|
|
212
|
+
|
|
134
213
|
<Card>
|
|
135
214
|
<CardHeader>
|
|
136
215
|
<CardTitle level={2}>勤怠アクションパレット(Cmd+K 想定)</CardTitle>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { Alert, AlertDescription } from "@godxjp/ui/feedback";
|
|
4
|
-
import { Input } from "@godxjp/ui/data-entry";
|
|
4
|
+
import { Checkbox, Input } from "@godxjp/ui/data-entry";
|
|
5
5
|
import { Button } from "@godxjp/ui/general";
|
|
6
6
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
7
7
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
@@ -10,12 +10,13 @@ import { FormFieldControl, FormRoot, useZodForm } from "@godxjp/ui/form";
|
|
|
10
10
|
const schema = z.object({
|
|
11
11
|
name: z.string().min(1, "組織名を入力してください"),
|
|
12
12
|
email: z.string().email("有効なメールアドレスを入力してください"),
|
|
13
|
+
is_shared: z.boolean(),
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
export default function Demo() {
|
|
16
17
|
const [status, setStatus] = useState<"idle" | "pending" | "success" | "server-error">("idle");
|
|
17
18
|
const form = useZodForm(schema, {
|
|
18
|
-
defaultValues: { name: "株式会社アクメ", email: "billing@example.jp" },
|
|
19
|
+
defaultValues: { name: "株式会社アクメ", email: "billing@example.jp", is_shared: false },
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
async function submit() {
|
|
@@ -70,6 +71,15 @@ export default function Demo() {
|
|
|
70
71
|
/>
|
|
71
72
|
)}
|
|
72
73
|
</FormFieldControl>
|
|
74
|
+
{/* Boolean field (antd `valuePropName="checked"`): the label is the Checkbox's children,
|
|
75
|
+
on the same line as the box — no label row above it. */}
|
|
76
|
+
<FormFieldControl name="is_shared" valuePropName="checked">
|
|
77
|
+
{(field) => (
|
|
78
|
+
<Checkbox {...field} disabled={status === "pending"}>
|
|
79
|
+
請求書をプロジェクトメンバーに共有する
|
|
80
|
+
</Checkbox>
|
|
81
|
+
)}
|
|
82
|
+
</FormFieldControl>
|
|
73
83
|
<Flex gap="sm" wrap>
|
|
74
84
|
<Button type="submit" loading={status === "pending"}>
|
|
75
85
|
保存
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrowUpRight,
|
|
3
|
+
Check,
|
|
4
|
+
Download,
|
|
5
|
+
FileWarning,
|
|
6
|
+
Globe,
|
|
7
|
+
Lock,
|
|
8
|
+
ShieldCheck,
|
|
9
|
+
Smartphone,
|
|
10
|
+
Unlock,
|
|
11
|
+
} from "lucide-react";
|
|
12
|
+
|
|
13
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
14
|
+
import { Button, Heading, Icon, Text } from "@godxjp/ui/general";
|
|
15
|
+
import { Flex, PageContainer, Separator } from "@godxjp/ui/layout";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Icon · a glyph on the `--icon-size-*` scale, and the only supported way to draw a standalone one.
|
|
19
|
+
*
|
|
20
|
+
* A lucide component ships `width="24" height="24"`. Four rules in this library override that
|
|
21
|
+
* (`.ui-button svg`, the menu row, the topbar cell, the ListRow leading slot) and nothing else
|
|
22
|
+
* does · so a glyph in a `Text`, a table cell or an `<a>` draws at 24px beside 14px type, 1.7× the
|
|
23
|
+
* text it annotates. `docs/CONSUMER-RULES.md` §3/§8 forbid the `size-4` / `w-[16px]` escape, which
|
|
24
|
+
* left a consumer with 38 oversized glyphs and no supported fix (gh#712).
|
|
25
|
+
*
|
|
26
|
+
* `Icon` renders ONTO the glyph · the sized element is the `<svg>` itself · so it composes
|
|
27
|
+
* anywhere a bare glyph does and adds no box to the row. Decorative (`aria-hidden`) by default;
|
|
28
|
+
* `label` switches it to `role="img"` with that accessible name.
|
|
29
|
+
*
|
|
30
|
+
* Composed only from real @godxjp/ui components. All copy is consumer-owned and localized.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** The nine steps, with the px each resolves to at a 16px root · docs/TOKENS.md. */
|
|
34
|
+
const SIZES = [
|
|
35
|
+
{ size: "2xs", px: "10px", note: "アバターの在席ドット" },
|
|
36
|
+
{ size: "xs", px: "12px", note: "表の並べ替えカーソル" },
|
|
37
|
+
{ size: "sm", px: "14px", note: 'size="sm" のコントロール' },
|
|
38
|
+
{ size: "md", px: "16px", note: "既定のアイコン段" },
|
|
39
|
+
{ size: "lg", px: "20px", note: "アラート・ランチャー" },
|
|
40
|
+
{ size: "xl", px: "24px", note: "アップロードタイル" },
|
|
41
|
+
{ size: "2xl", px: "36px", note: "StatCard のメダリオン" },
|
|
42
|
+
{ size: "3xl", px: "40px", note: "ドロップゾーン" },
|
|
43
|
+
{ size: "4xl", px: "48px", note: "空状態の枠" },
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
/** `tone` shares Text's vocabulary · one table for the glyph and the label beside it. */
|
|
47
|
+
const TONES = [
|
|
48
|
+
{ tone: "default", label: "default · 前景色" },
|
|
49
|
+
{ tone: "muted", label: "muted · 補足" },
|
|
50
|
+
{ tone: "primary", label: "primary · 主要導線" },
|
|
51
|
+
{ tone: "success", label: "success · 有効" },
|
|
52
|
+
{ tone: "warning", label: "warning · 要確認" },
|
|
53
|
+
{ tone: "destructive", label: "destructive · 失敗" },
|
|
54
|
+
{ tone: "info", label: "info · 情報" },
|
|
55
|
+
{ tone: "inherit", label: "inherit · 面の色を継承" },
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
/** The real screen: a security settings list whose status is carried by a glyph beside 14px text. */
|
|
59
|
+
const SESSIONS = [
|
|
60
|
+
{
|
|
61
|
+
id: "s1",
|
|
62
|
+
device: "MacBook Pro · 東京",
|
|
63
|
+
detail: "2026-09-17 09:12 · 192.0.2.24",
|
|
64
|
+
secure: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "s2",
|
|
68
|
+
device: "iPhone 15 · 大阪",
|
|
69
|
+
detail: "2026-09-16 21:40 · 198.51.100.7",
|
|
70
|
+
secure: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "s3",
|
|
74
|
+
device: "Windows PC · 未確認の拠点",
|
|
75
|
+
detail: "2026-09-15 02:05 · 203.0.113.19",
|
|
76
|
+
secure: false,
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
export default function Demo() {
|
|
81
|
+
return (
|
|
82
|
+
<PageContainer
|
|
83
|
+
title="Icon"
|
|
84
|
+
subtitle='--icon-size-* の9段にグリフを載せる唯一の方法 · 既定は装飾(aria-hidden)· label で role="img"'
|
|
85
|
+
>
|
|
86
|
+
<Flex direction="col" gap="lg">
|
|
87
|
+
{/* ── The real screen · the defect this primitive exists for: a status glyph inline with
|
|
88
|
+
14px body text, where no context rule would ever have sized it. ── */}
|
|
89
|
+
<Card>
|
|
90
|
+
<CardHeader>
|
|
91
|
+
<CardTitle level={2}>サインイン中の端末</CardTitle>
|
|
92
|
+
</CardHeader>
|
|
93
|
+
<CardContent>
|
|
94
|
+
<Flex direction="col" gap="md">
|
|
95
|
+
{SESSIONS.map((session) => (
|
|
96
|
+
<Flex key={session.id} direction="row" gap="sm" align="center" justify="between">
|
|
97
|
+
<Flex direction="row" gap="sm" align="center">
|
|
98
|
+
<Icon
|
|
99
|
+
as={session.secure ? ShieldCheck : FileWarning}
|
|
100
|
+
size="lg"
|
|
101
|
+
tone={session.secure ? "success" : "warning"}
|
|
102
|
+
label={session.secure ? "保護された接続" : "確認が必要な接続"}
|
|
103
|
+
/>
|
|
104
|
+
<Flex direction="col">
|
|
105
|
+
<Text weight="medium">{session.device}</Text>
|
|
106
|
+
{/* A decorative glyph INSIDE 14px text · `sm` puts it on the type's own step. */}
|
|
107
|
+
<Text size="xs" tone="muted">
|
|
108
|
+
<Icon as={Globe} size="xs" /> {session.detail}
|
|
109
|
+
</Text>
|
|
110
|
+
</Flex>
|
|
111
|
+
</Flex>
|
|
112
|
+
<Button variant="outline" size="sm">
|
|
113
|
+
<Icon as={session.secure ? Lock : Unlock} size="sm" />
|
|
114
|
+
サインアウト
|
|
115
|
+
</Button>
|
|
116
|
+
</Flex>
|
|
117
|
+
))}
|
|
118
|
+
</Flex>
|
|
119
|
+
</CardContent>
|
|
120
|
+
</Card>
|
|
121
|
+
|
|
122
|
+
{/* ── Every step of the scale ── */}
|
|
123
|
+
<Card>
|
|
124
|
+
<CardHeader>
|
|
125
|
+
<CardTitle level={2}>size · 9段のアイコンスケール</CardTitle>
|
|
126
|
+
</CardHeader>
|
|
127
|
+
<CardContent>
|
|
128
|
+
<Flex direction="col" gap="sm">
|
|
129
|
+
{SIZES.map((step) => (
|
|
130
|
+
<Flex key={step.size} direction="row" gap="md" align="center">
|
|
131
|
+
<Flex direction="row" gap="sm" align="center">
|
|
132
|
+
<Icon as={Smartphone} size={step.size} />
|
|
133
|
+
</Flex>
|
|
134
|
+
<Text size="sm" mono>
|
|
135
|
+
{step.size}
|
|
136
|
+
</Text>
|
|
137
|
+
<Text size="sm" tone="muted" mono>
|
|
138
|
+
{step.px}
|
|
139
|
+
</Text>
|
|
140
|
+
<Text size="sm" tone="muted">
|
|
141
|
+
{step.note}
|
|
142
|
+
</Text>
|
|
143
|
+
</Flex>
|
|
144
|
+
))}
|
|
145
|
+
</Flex>
|
|
146
|
+
</CardContent>
|
|
147
|
+
</Card>
|
|
148
|
+
|
|
149
|
+
{/* ── Every tone, beside the Text that shares the vocabulary ── */}
|
|
150
|
+
<Card>
|
|
151
|
+
<CardHeader>
|
|
152
|
+
<CardTitle level={2}>tone · Text と同じ意味の語彙</CardTitle>
|
|
153
|
+
</CardHeader>
|
|
154
|
+
<CardContent>
|
|
155
|
+
<Flex direction="col" gap="sm">
|
|
156
|
+
{TONES.map((entry) => (
|
|
157
|
+
<Flex key={entry.tone} direction="row" gap="sm" align="center">
|
|
158
|
+
<Icon as={Check} size="md" tone={entry.tone} />
|
|
159
|
+
<Text size="sm" tone={entry.tone}>
|
|
160
|
+
{entry.label}
|
|
161
|
+
</Text>
|
|
162
|
+
</Flex>
|
|
163
|
+
))}
|
|
164
|
+
</Flex>
|
|
165
|
+
</CardContent>
|
|
166
|
+
</Card>
|
|
167
|
+
|
|
168
|
+
{/* ── Where it composes · inside Text, inside a Button, standalone in a link ── */}
|
|
169
|
+
<Card>
|
|
170
|
+
<CardHeader>
|
|
171
|
+
<CardTitle level={2}>合成 · Text の中・Button の中・単独</CardTitle>
|
|
172
|
+
</CardHeader>
|
|
173
|
+
<CardContent>
|
|
174
|
+
<Flex direction="col" gap="md">
|
|
175
|
+
<Flex direction="col" gap="xs">
|
|
176
|
+
<Heading level={3}>Text の中</Heading>
|
|
177
|
+
<Text size="sm">
|
|
178
|
+
請求書は <Icon as={Lock} size="sm" tone="muted" /> 暗号化されて保管されます。
|
|
179
|
+
</Text>
|
|
180
|
+
</Flex>
|
|
181
|
+
|
|
182
|
+
<Separator />
|
|
183
|
+
|
|
184
|
+
<Flex direction="col" gap="xs">
|
|
185
|
+
<Heading level={3}>Button の中 · 明示した段が Button の既定に勝つ</Heading>
|
|
186
|
+
<Flex direction="row" gap="sm" align="center">
|
|
187
|
+
<Button size="sm">
|
|
188
|
+
<Icon as={Download} size="sm" />
|
|
189
|
+
書き出す
|
|
190
|
+
</Button>
|
|
191
|
+
<Button variant="outline">
|
|
192
|
+
<Icon as={Download} size="lg" />
|
|
193
|
+
大きめのグリフ
|
|
194
|
+
</Button>
|
|
195
|
+
</Flex>
|
|
196
|
+
</Flex>
|
|
197
|
+
|
|
198
|
+
<Separator />
|
|
199
|
+
|
|
200
|
+
<Flex direction="col" gap="xs">
|
|
201
|
+
<Heading level={3}>単独 · 名前を持つグリフ</Heading>
|
|
202
|
+
<Flex direction="row" gap="sm" align="center">
|
|
203
|
+
<Text as="a" href="#icon-standalone" size="sm" tone="primary">
|
|
204
|
+
監査ログを開く <Icon as={ArrowUpRight} size="sm" />
|
|
205
|
+
</Text>
|
|
206
|
+
<Icon as={ShieldCheck} size="md" tone="success" label="二要素認証は有効です" />
|
|
207
|
+
</Flex>
|
|
208
|
+
</Flex>
|
|
209
|
+
</Flex>
|
|
210
|
+
</CardContent>
|
|
211
|
+
</Card>
|
|
212
|
+
</Flex>
|
|
213
|
+
</PageContainer>
|
|
214
|
+
);
|
|
215
|
+
}
|
package/docs/layout/flex.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CardDescription,
|
|
6
6
|
CardHeader,
|
|
7
7
|
CardTitle,
|
|
8
|
+
ListRow,
|
|
8
9
|
} from "@godxjp/ui/data-display";
|
|
9
10
|
import { Button, Text } from "@godxjp/ui/general";
|
|
10
11
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
@@ -309,6 +310,64 @@ export default function Demo() {
|
|
|
309
310
|
</Flex>
|
|
310
311
|
</CardContent>
|
|
311
312
|
</Card>
|
|
313
|
+
<Card>
|
|
314
|
+
<CardHeader>
|
|
315
|
+
<CardTitle level={2}>marker="none" · 装飾のない意味的リスト</CardTitle>
|
|
316
|
+
<CardDescription>
|
|
317
|
+
as="ul" は既定で中黒と字下げが付きます。設定メニューのような「行の一覧」では
|
|
318
|
+
marker="none" を指定すると、ul と li の意味づけと gap トークンは残したまま、
|
|
319
|
+
中黒も字下げも消えます。行は ListRow as="li"
|
|
320
|
+
で、区切り線は行が自分で描きます。
|
|
321
|
+
</CardDescription>
|
|
322
|
+
</CardHeader>
|
|
323
|
+
<CardContent flush>
|
|
324
|
+
<Flex id="semantic-list" as="ul" marker="none" direction="col" gap="none">
|
|
325
|
+
<ListRow
|
|
326
|
+
as="li"
|
|
327
|
+
title="二要素認証"
|
|
328
|
+
description="認証アプリ · 有効"
|
|
329
|
+
trailing={
|
|
330
|
+
<Button size="xs" variant="outline">
|
|
331
|
+
管理
|
|
332
|
+
</Button>
|
|
333
|
+
}
|
|
334
|
+
/>
|
|
335
|
+
<ListRow
|
|
336
|
+
as="li"
|
|
337
|
+
title="パスキー"
|
|
338
|
+
description="2 件登録済み"
|
|
339
|
+
trailing={
|
|
340
|
+
<Button size="xs" variant="outline">
|
|
341
|
+
管理
|
|
342
|
+
</Button>
|
|
343
|
+
}
|
|
344
|
+
/>
|
|
345
|
+
<ListRow
|
|
346
|
+
as="li"
|
|
347
|
+
title="回復コード"
|
|
348
|
+
description="未発行"
|
|
349
|
+
trailing={
|
|
350
|
+
<Button size="xs" variant="outline">
|
|
351
|
+
発行
|
|
352
|
+
</Button>
|
|
353
|
+
}
|
|
354
|
+
/>
|
|
355
|
+
</Flex>
|
|
356
|
+
</CardContent>
|
|
357
|
+
<CardContent>
|
|
358
|
+
<Flex id="semantic-list-gap" as="ul" marker="none" gap="sm" wrap>
|
|
359
|
+
<Flex as="li">
|
|
360
|
+
<Badge variant="outline">監査ログ</Badge>
|
|
361
|
+
</Flex>
|
|
362
|
+
<Flex as="li">
|
|
363
|
+
<Badge variant="outline">IP 制限</Badge>
|
|
364
|
+
</Flex>
|
|
365
|
+
<Flex as="li">
|
|
366
|
+
<Badge variant="outline">SSO</Badge>
|
|
367
|
+
</Flex>
|
|
368
|
+
</Flex>
|
|
369
|
+
</CardContent>
|
|
370
|
+
</Card>
|
|
312
371
|
<Card>
|
|
313
372
|
<CardHeader>
|
|
314
373
|
<CardTitle level={2}>軽い強調と行アクション</CardTitle>
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
1
2
|
import {
|
|
2
3
|
Card,
|
|
3
4
|
CardContent,
|
|
4
5
|
CardDescription,
|
|
5
6
|
CardHeader,
|
|
6
7
|
CardTitle,
|
|
8
|
+
ScrollArea,
|
|
7
9
|
StatCard,
|
|
8
10
|
} from "@godxjp/ui/data-display";
|
|
11
|
+
import {
|
|
12
|
+
Select,
|
|
13
|
+
SelectContent,
|
|
14
|
+
SelectItem,
|
|
15
|
+
SelectTrigger,
|
|
16
|
+
SelectValue,
|
|
17
|
+
} from "@godxjp/ui/data-entry";
|
|
9
18
|
import { Text } from "@godxjp/ui/general";
|
|
10
19
|
import { Flex, PageContainer, ResponsiveGrid, SplitPane } from "@godxjp/ui/layout";
|
|
11
20
|
|
|
@@ -20,7 +29,25 @@ import { Flex, PageContainer, ResponsiveGrid, SplitPane } from "@godxjp/ui/layou
|
|
|
20
29
|
* recognised collection shape instead of hand-rolling the breakpoint map. Composed only from
|
|
21
30
|
* real @godxjp/ui components.
|
|
22
31
|
*/
|
|
32
|
+
const KANBAN_LANES = [
|
|
33
|
+
{ key: "todo", title: "未着手" },
|
|
34
|
+
{ key: "doing", title: "進行中" },
|
|
35
|
+
{ key: "done", title: "完了" },
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
type KanbanLane = (typeof KANBAN_LANES)[number]["key"];
|
|
39
|
+
|
|
40
|
+
const KANBAN_TASKS: { id: string; title: string; lane: KanbanLane; priority: string }[] = [
|
|
41
|
+
{ id: "T-101", title: "請求書テンプレートの改訂", lane: "todo", priority: "high" },
|
|
42
|
+
{ id: "T-102", title: "取引先マスタの重複整理", lane: "todo", priority: "medium" },
|
|
43
|
+
{ id: "T-103", title: "月次締めチェックリスト", lane: "doing", priority: "low" },
|
|
44
|
+
];
|
|
45
|
+
|
|
23
46
|
export default function Demo() {
|
|
47
|
+
// Kanban: which lane each card is in, moved by native drag and drop.
|
|
48
|
+
const [tasks, setTasks] = useState(KANBAN_TASKS);
|
|
49
|
+
const moveTask = (id: string, lane: KanbanLane) =>
|
|
50
|
+
setTasks((current) => current.map((task) => (task.id === id ? { ...task, lane } : task)));
|
|
24
51
|
return (
|
|
25
52
|
<PageContainer
|
|
26
53
|
title="ResponsiveGrid"
|
|
@@ -255,6 +282,79 @@ export default function Demo() {
|
|
|
255
282
|
</CardContent>
|
|
256
283
|
</Card>
|
|
257
284
|
|
|
285
|
+
<Card>
|
|
286
|
+
<CardHeader>
|
|
287
|
+
<CardTitle level={2}>
|
|
288
|
+
flow="columns" align="stretch" · カンバンのレーン
|
|
289
|
+
</CardTitle>
|
|
290
|
+
<CardDescription>
|
|
291
|
+
align="stretch"(antd Row
|
|
292
|
+
align)で全レーンが最も高いレーンと同じ高さになり、空の
|
|
293
|
+
「完了」レーンも全高のドロップ先になる。省略時は columns フローが start、rows フローが
|
|
294
|
+
stretch のまま。カードはドラッグでき、中の Select
|
|
295
|
+
はドラッグ画像をカードの外へ広げない。
|
|
296
|
+
</CardDescription>
|
|
297
|
+
</CardHeader>
|
|
298
|
+
<CardContent>
|
|
299
|
+
<ScrollArea orientation="horizontal">
|
|
300
|
+
<ResponsiveGrid flow="columns" align="stretch">
|
|
301
|
+
{KANBAN_LANES.map((lane) => (
|
|
302
|
+
<Card
|
|
303
|
+
key={lane.key}
|
|
304
|
+
data-lane={lane.key}
|
|
305
|
+
onDragOver={(event) => event.preventDefault()}
|
|
306
|
+
onDrop={(event) => {
|
|
307
|
+
event.preventDefault();
|
|
308
|
+
moveTask(event.dataTransfer.getData("text/plain"), lane.key);
|
|
309
|
+
}}
|
|
310
|
+
>
|
|
311
|
+
<CardHeader>
|
|
312
|
+
<CardTitle>{lane.title}</CardTitle>
|
|
313
|
+
</CardHeader>
|
|
314
|
+
<CardContent>
|
|
315
|
+
<Flex direction="col" gap="sm">
|
|
316
|
+
{tasks
|
|
317
|
+
.filter((task) => task.lane === lane.key)
|
|
318
|
+
.map((task) => (
|
|
319
|
+
<Card
|
|
320
|
+
key={task.id}
|
|
321
|
+
draggable
|
|
322
|
+
data-task={task.id}
|
|
323
|
+
onDragStart={(event) =>
|
|
324
|
+
event.dataTransfer.setData("text/plain", task.id)
|
|
325
|
+
}
|
|
326
|
+
>
|
|
327
|
+
<CardContent>
|
|
328
|
+
<Flex direction="col" gap="xs">
|
|
329
|
+
<Text tone="muted">{task.id}</Text>
|
|
330
|
+
<Text>{task.title}</Text>
|
|
331
|
+
<Select
|
|
332
|
+
aria-label={`${task.id} 優先度`}
|
|
333
|
+
name={`priority-${task.id}`}
|
|
334
|
+
defaultValue={task.priority}
|
|
335
|
+
>
|
|
336
|
+
<SelectTrigger aria-label={`${task.id} 優先度`} size="sm">
|
|
337
|
+
<SelectValue />
|
|
338
|
+
</SelectTrigger>
|
|
339
|
+
<SelectContent>
|
|
340
|
+
<SelectItem value="high">高</SelectItem>
|
|
341
|
+
<SelectItem value="medium">中</SelectItem>
|
|
342
|
+
<SelectItem value="low">低</SelectItem>
|
|
343
|
+
</SelectContent>
|
|
344
|
+
</Select>
|
|
345
|
+
</Flex>
|
|
346
|
+
</CardContent>
|
|
347
|
+
</Card>
|
|
348
|
+
))}
|
|
349
|
+
</Flex>
|
|
350
|
+
</CardContent>
|
|
351
|
+
</Card>
|
|
352
|
+
))}
|
|
353
|
+
</ResponsiveGrid>
|
|
354
|
+
</ScrollArea>
|
|
355
|
+
</CardContent>
|
|
356
|
+
</Card>
|
|
357
|
+
|
|
258
358
|
<Card>
|
|
259
359
|
<CardHeader>
|
|
260
360
|
<CardTitle level={2}>一覧と補助情報 2:1</CardTitle>
|