@godxjp/ui 23.0.0 → 23.2.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.
Files changed (93) hide show
  1. package/dist/app/date-format-labels.d.ts +14 -2
  2. package/dist/app/date-format-labels.js +1 -1
  3. package/dist/app/date-formats.d.ts +14 -2
  4. package/dist/app/date-formats.js +9 -2
  5. package/dist/components/data-display/card.d.ts +45 -2
  6. package/dist/components/data-display/card.js +4 -2
  7. package/dist/components/data-display/data-table.d.ts +8 -1
  8. package/dist/components/data-display/data-table.js +9 -2
  9. package/dist/components/data-display/feature-list.d.ts +26 -0
  10. package/dist/components/data-display/feature-list.js +29 -0
  11. package/dist/components/data-display/index.d.ts +6 -0
  12. package/dist/components/data-display/index.js +6 -0
  13. package/dist/components/data-display/swatch.d.ts +24 -0
  14. package/dist/components/data-display/swatch.js +19 -0
  15. package/dist/components/data-display/thumbnail.d.ts +37 -0
  16. package/dist/components/data-display/thumbnail.js +20 -0
  17. package/dist/components/data-entry/checkbox.js +1 -1
  18. package/dist/components/data-entry/form-field.js +2 -2
  19. package/dist/components/data-entry/input.js +1 -1
  20. package/dist/components/data-entry/number-input.js +27 -6
  21. package/dist/components/data-entry/radio.js +1 -1
  22. package/dist/components/data-entry/switch.js +1 -1
  23. package/dist/components/feedback/dialog.js +2 -2
  24. package/dist/components/feedback/index.d.ts +2 -2
  25. package/dist/components/feedback/index.js +19 -1
  26. package/dist/components/feedback/sheet.js +2 -22
  27. package/dist/components/feedback/skeleton.d.ts +33 -3
  28. package/dist/components/feedback/skeleton.js +154 -3
  29. package/dist/components/layout/flex.d.ts +1 -1
  30. package/dist/components/layout/flex.js +17 -5
  31. package/dist/components/layout/sidebar.js +9 -2
  32. package/dist/components/navigation/filter-bar.js +2 -1
  33. package/dist/components/navigation/tabs-scroll.d.ts +27 -0
  34. package/dist/components/navigation/tabs-scroll.js +36 -1
  35. package/dist/components/navigation/tabs.d.ts +2 -2
  36. package/dist/components/navigation/tabs.js +60 -16
  37. package/dist/i18n/messages/en.json +10 -1
  38. package/dist/i18n/messages/ja.json +10 -1
  39. package/dist/i18n/messages/vi.json +10 -1
  40. package/dist/lib/breakpoint-token.d.ts +8 -0
  41. package/dist/lib/breakpoint-token.js +29 -0
  42. package/dist/props/components/data-display.prop.d.ts +101 -1
  43. package/dist/props/components/feedback.prop.d.ts +85 -1
  44. package/dist/props/components/layout.prop.d.ts +72 -1
  45. package/dist/props/components/navigation.prop.d.ts +80 -2
  46. package/dist/props/registry.d.ts +124 -1
  47. package/dist/props/registry.js +140 -0
  48. package/dist/props/vocabulary/data.prop.d.ts +5 -0
  49. package/dist/props/vocabulary/index.d.ts +1 -1
  50. package/dist/styles/alert-layout.css +139 -0
  51. package/dist/styles/card-layout.css +48 -0
  52. package/dist/styles/control.css +41 -5
  53. package/dist/styles/data-display-layout.css +88 -0
  54. package/dist/styles/focus-ring.css +28 -0
  55. package/dist/styles/form-layout.css +4 -0
  56. package/dist/styles/layout.css +2 -1
  57. package/dist/styles/navigation-layout.css +89 -5
  58. package/dist/styles/shell-layout.css +22 -0
  59. package/dist/styles/table-layout.css +5 -2
  60. package/dist/tokens/components/card.css +2 -0
  61. package/dist/tokens/components/control.css +8 -3
  62. package/dist/tokens/components/data-display.css +23 -0
  63. package/dist/tokens/components/feedback.css +18 -0
  64. package/dist/tokens/components/navigation.css +4 -0
  65. package/dist/tokens/foundation.css +18 -16
  66. package/docs/CONSUMER-RULES.md +8 -3
  67. package/docs/CUSTOMER-THEMING.md +18 -8
  68. package/docs/DATETIME.md +11 -2
  69. package/docs/DESIGN-AUTHORITY.md +2 -1
  70. package/docs/FRAME-COVERAGE-REPORT.md +29 -3
  71. package/docs/SPACING.md +35 -5
  72. package/docs/TOKENS.md +1 -1
  73. package/docs/assets/shot-landscape.svg +12 -0
  74. package/docs/assets/shot-portrait.svg +10 -0
  75. package/docs/data-display/card/index.tsx +40 -0
  76. package/docs/data-display/data-table/index.tsx +17 -2
  77. package/docs/data-display/feature-list.tsx +159 -0
  78. package/docs/data-display/swatch.tsx +107 -0
  79. package/docs/data-display/table.tsx +1 -1
  80. package/docs/data-display/thumbnail.tsx +128 -0
  81. package/docs/data-entry/segmented-in-filter-row.tsx +77 -0
  82. package/docs/feedback/skeleton.tsx +57 -0
  83. package/docs/layout/flex.tsx +29 -0
  84. package/docs/layout/sidebar.tsx +66 -10
  85. package/docs/navigation/dropdown-menu.tsx +4 -4
  86. package/docs/navigation/tabs.tsx +131 -0
  87. package/docs/roadmap/ai-chat-components.md +1 -2
  88. package/docs/roadmap/tree-components.md +0 -1
  89. package/package.json +12 -21
  90. package/scripts/_agent-setup.mjs +18 -0
  91. package/scripts/consumer-rule.md +16 -0
  92. package/scripts/guinea-pig-skill.md +12 -6
  93. package/scripts/ui-audit.mjs +184 -7
@@ -0,0 +1,107 @@
1
+ import {
2
+ Card,
3
+ CardContent,
4
+ CardDescription,
5
+ CardHeader,
6
+ CardTitle,
7
+ ListRow,
8
+ Swatch,
9
+ } from "@godxjp/ui/data-display";
10
+ import { Text } from "@godxjp/ui/general";
11
+ import { Flex, PageContainer } from "@godxjp/ui/layout";
12
+
13
+ const BRAND = {
14
+ name: "株式会社山田製作所",
15
+ primary: "#0071bd",
16
+ secondary: "#c0392f",
17
+ };
18
+
19
+ const CATEGORIES = [
20
+ { name: "定例会議", color: "#7c3aed" },
21
+ { name: "出張", color: "#0f9d58" },
22
+ { name: "締切", color: "#c0392f" },
23
+ { name: "予備", color: "#ffffff" },
24
+ ];
25
+
26
+ /**
27
+ * Swatch — a read-only sample of ONE colour a person chose.
28
+ * Composed only from real @godxjp/ui components.
29
+ */
30
+ export default function Demo() {
31
+ return (
32
+ <PageContainer title="Swatch" subtitle="利用者が選んだ色を、そのまま見せる読み取り専用の見本">
33
+ <Flex direction="col" gap="lg">
34
+ <Card>
35
+ <CardHeader>
36
+ <CardTitle level={2}>ブランドカラー</CardTitle>
37
+ <CardDescription>
38
+ 組織が登録した色は tone ではなく値です。見本に名前を渡すと、見える文字を増やさずに
39
+ 読み上げられます。
40
+ </CardDescription>
41
+ </CardHeader>
42
+ <CardContent>
43
+ <Flex direction="col" gap="sm">
44
+ <Flex align="center" gap="sm">
45
+ <Swatch color={BRAND.primary} aria-label={`プライマリカラー: ${BRAND.primary}`} />
46
+ <Text weight="medium">{BRAND.name}</Text>
47
+ <Text tone="muted" mono size="xs">
48
+ {BRAND.primary}
49
+ </Text>
50
+ </Flex>
51
+ <Flex align="center" gap="sm">
52
+ <Swatch
53
+ color={BRAND.secondary}
54
+ aria-label={`セカンダリカラー: ${BRAND.secondary}`}
55
+ />
56
+ <Text weight="medium">{BRAND.name}</Text>
57
+ <Text tone="muted" mono size="xs">
58
+ {BRAND.secondary}
59
+ </Text>
60
+ </Flex>
61
+ </Flex>
62
+ </CardContent>
63
+ </Card>
64
+
65
+ <Card>
66
+ <CardHeader>
67
+ <CardTitle level={2}>一覧の先頭マークとして</CardTitle>
68
+ <CardDescription>
69
+ 行のラベルが色の意味を言っているので、見本は aria-hidden
70
+ のままにします。同じことを二度読み上げないためです。最後の「予備」は白で、
71
+ 白いカードの上でも見本の輪郭が残ることを示します。
72
+ </CardDescription>
73
+ </CardHeader>
74
+ <CardContent>
75
+ <Flex direction="col" gap="none">
76
+ {CATEGORIES.map((category) => (
77
+ <ListRow
78
+ key={category.name}
79
+ leading={<Swatch color={category.color} />}
80
+ title={category.name}
81
+ description={category.color}
82
+ />
83
+ ))}
84
+ </Flex>
85
+ </CardContent>
86
+ </Card>
87
+
88
+ <Card>
89
+ <CardHeader>
90
+ <CardTitle level={2}>色の形式は問いません</CardTitle>
91
+ <CardDescription>
92
+ hex でも rgb() でも oklch() でも、CSS の色として有効ならそのまま塗ります。
93
+ 値はスタイルシートには入らず、要素の上にだけ乗ります。
94
+ </CardDescription>
95
+ </CardHeader>
96
+ <CardContent>
97
+ <Flex align="center" gap="md" wrap>
98
+ <Swatch color="#7c3aed" aria-label="hex: #7c3aed" />
99
+ <Swatch color="rgb(15 157 88)" aria-label="rgb: rgb(15 157 88)" />
100
+ <Swatch color="oklch(0.72 0.15 45)" aria-label="oklch: oklch(0.72 0.15 45)" />
101
+ </Flex>
102
+ </CardContent>
103
+ </Card>
104
+ </Flex>
105
+ </PageContainer>
106
+ );
107
+ }
@@ -181,7 +181,7 @@ export default function Demo() {
181
181
  full frame. The suppression above is scoped to a flush body, never to `bordered`.
182
182
  </CardDescription>
183
183
  </CardHeader>
184
- <CardContent>
184
+ <CardContent flush>
185
185
  <Table bordered>
186
186
  <TableHeader>
187
187
  <TableRow>
@@ -0,0 +1,128 @@
1
+ import {
2
+ Card,
3
+ CardContent,
4
+ CardDescription,
5
+ CardHeader,
6
+ CardTitle,
7
+ Thumbnail,
8
+ } from "@godxjp/ui/data-display";
9
+ import { Flex, PageContainer } from "@godxjp/ui/layout";
10
+
11
+ import coverTerrain from "../assets/cover-terrain.svg";
12
+ import portraitIris from "../assets/portrait-iris.svg";
13
+ import shotLandscape from "../assets/shot-landscape.svg";
14
+ import shotPortrait from "../assets/shot-portrait.svg";
15
+
16
+ /* Committed SVG files under docs/assets, imported so the bundler rewrites each URL against
17
+ * PREVIEW_BASE. A docs page must never fetch a third-party image: on CI the request never
18
+ * settles, `networkidle` never fires, and the page load times out. */
19
+
20
+ /** Four DIFFERENT intrinsic ratios — the case AspectRatio cannot express with one number. */
21
+ const SHOTS = [
22
+ { src: shotPortrait, width: 360, height: 640, alt: "モバイル版の一覧画面" },
23
+ { src: shotLandscape, width: 960, height: 540, alt: "デスクトップ版のダッシュボード" },
24
+ { src: coverTerrain, width: 480, height: 270, alt: "ストア掲載用のカバー画像" },
25
+ { src: portraitIris, width: 96, height: 96, alt: "アプリアイコン" },
26
+ ];
27
+
28
+ /**
29
+ * Thumbnail — 高さは固定、幅は画像そのものの比率。囲みは画像に直接かかる hairline。
30
+ *
31
+ * AspectRatio は比率を固定して幅 100% に広げるので、比率の違う数枚を並べるとレターボックスか
32
+ * 切り抜きになる。Avatar は人・組織の識別マーク、Card は囲みと画像のあいだに padding を入れる、
33
+ * CardCover は Card の中のスロット。Composed only from real @godxjp/ui components.
34
+ */
35
+ export default function Demo() {
36
+ return (
37
+ <PageContainer title="Thumbnail" subtitle="高さ固定・幅は実寸比 · 画像に直接かかる hairline">
38
+ <Flex direction="col" gap="lg">
39
+ <Card>
40
+ <CardHeader>
41
+ <CardTitle level={2}>比率の違う画像を 1 行に並べる</CardTitle>
42
+ <CardDescription>
43
+ 縦長・横長・ワイド・正方形が同じ行に並んでも、高さはそろい、幅はそれぞれの比率の
44
+ まま。切り抜きもレターボックスも起きません。狭い画面では折り返します。
45
+ </CardDescription>
46
+ </CardHeader>
47
+ <CardContent>
48
+ <Flex gap="sm" wrap align="start">
49
+ {SHOTS.map((shot) => (
50
+ <Thumbnail key={shot.alt} size="lg" {...shot} />
51
+ ))}
52
+ </Flex>
53
+ </CardContent>
54
+ </Card>
55
+
56
+ <Card>
57
+ <CardHeader>
58
+ <CardTitle level={2}>size · sm · md(既定)· lg</CardTitle>
59
+ <CardDescription>
60
+ sm 64px は添付ファイルの行に、md 96px はギャラリー行に、lg 160px は画像そのものが
61
+ 本文である審査画面に。px 指定はありません。
62
+ </CardDescription>
63
+ </CardHeader>
64
+ <CardContent>
65
+ <Flex direction="col" gap="md">
66
+ <Flex gap="sm" wrap align="start">
67
+ <Thumbnail size="sm" {...SHOTS[0]} />
68
+ <Thumbnail size="sm" {...SHOTS[1]} />
69
+ </Flex>
70
+ <Flex gap="sm" wrap align="start">
71
+ <Thumbnail {...SHOTS[0]} />
72
+ <Thumbnail {...SHOTS[1]} />
73
+ </Flex>
74
+ <Flex gap="sm" wrap align="start">
75
+ <Thumbnail size="lg" {...SHOTS[0]} />
76
+ <Thumbnail size="lg" {...SHOTS[1]} />
77
+ </Flex>
78
+ </Flex>
79
+ </CardContent>
80
+ </Card>
81
+
82
+ <Card>
83
+ <CardHeader>
84
+ <CardTitle level={2}>読み込み前に幅を確保する</CardTitle>
85
+ <CardDescription>
86
+ 幅が実寸比のままということは、画像が届くまで幅が決まらないということでもあります。
87
+ 元ファイルの width / height をそのまま渡すと、ブラウザが比率を先に知るので、
88
+ 高さが固定されている分だけ最初の描画から最終的な幅になります。
89
+ </CardDescription>
90
+ </CardHeader>
91
+ <CardContent>
92
+ <Flex gap="sm" wrap align="start">
93
+ <Thumbnail
94
+ src={shotPortrait}
95
+ width={360}
96
+ height={640}
97
+ alt="幅を先に確保した縦長の画面"
98
+ />
99
+ <Thumbnail
100
+ src={shotLandscape}
101
+ width={960}
102
+ height={540}
103
+ alt="幅を先に確保した横長の画面"
104
+ loading="lazy"
105
+ />
106
+ </Flex>
107
+ </CardContent>
108
+ </Card>
109
+
110
+ <Card>
111
+ <CardHeader>
112
+ <CardTitle level={2}>alt は省略できない</CardTitle>
113
+ <CardDescription>
114
+ 型の上で必須です。ページが既に述べていることしか写っていない画像には alt=&quot;&quot;
115
+ を渡します —— 空文字は著者が下す「決定」で、属性そのものを書き忘れるのとは違います
116
+ (WCAG 1.1.1)。
117
+ </CardDescription>
118
+ </CardHeader>
119
+ <CardContent>
120
+ <Flex gap="sm" wrap align="start">
121
+ <Thumbnail src={coverTerrain} width={480} height={270} alt="" />
122
+ </Flex>
123
+ </CardContent>
124
+ </Card>
125
+ </Flex>
126
+ </PageContainer>
127
+ );
128
+ }
@@ -0,0 +1,77 @@
1
+ import {
2
+ Badge,
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from "@godxjp/ui/data-display";
9
+ import { Segmented } from "@godxjp/ui/data-entry";
10
+ import { Button, Text } from "@godxjp/ui/general";
11
+ import { Flex, PageContainer } from "@godxjp/ui/layout";
12
+
13
+ /**
14
+ * The composition a consumer actually filters a list with: a Segmented that carries a count badge
15
+ * per option, sharing a WRAPPING flex row with the other filter controls, inside a Card.
16
+ *
17
+ * It is a separate frame from `segmented.tsx` because the row is the variable. `#four-with-counts`
18
+ * over there puts the track directly in a CardContent, where it is a block box and shrinks to the
19
+ * column; here it is a FLEX ITEM, and a flex item's `min-inline-size` defaults to `auto` — its
20
+ * max-content width — so nothing makes it give ground to the row.
21
+ */
22
+ const OPTIONS = [
23
+ { value: "all", label: "すべて", count: 133 },
24
+ { value: "ginou", label: "技能実習", count: 82 },
25
+ { value: "tokutei", label: "特定技能", count: 51 },
26
+ { value: "ikusei", label: "育成就労", count: 0 },
27
+ ].map(({ value, label, count }) => ({
28
+ value,
29
+ label: (
30
+ <>
31
+ {label}
32
+ <Badge as="span" variant="secondary">
33
+ {count}
34
+ </Badge>
35
+ </>
36
+ ),
37
+ }));
38
+
39
+ export default function SegmentedInFilterRow() {
40
+ return (
41
+ <PageContainer title="Segmented in a filter row" subtitle="4 択 + 件数バッジ · Flex wrap の中">
42
+ <Card id="four-in-wrap-row">
43
+ <CardHeader>
44
+ <CardTitle level={2}>制度の絞り込み</CardTitle>
45
+ <CardDescription>
46
+ `check:segmented-wrap` がこのカードも測ります。トラックは行の中でも縮み、393px
47
+ でラベルも件数も切り詰めません。
48
+ </CardDescription>
49
+ </CardHeader>
50
+ <CardContent>
51
+ <Flex gap="sm" align="center" wrap>
52
+ <Segmented aria-label="制度" defaultValue="all" options={OPTIONS} />
53
+ <Button variant="outline" size="sm">
54
+ 絞り込みを解除
55
+ </Button>
56
+ </Flex>
57
+ </CardContent>
58
+ </Card>
59
+
60
+ <Card id="four-in-wrap-row-alone">
61
+ <CardHeader>
62
+ <CardTitle level={2}>同じトラック、行の中に一つだけ</CardTitle>
63
+ <CardDescription>
64
+ <Text size="sm" tone="muted">
65
+ 兄弟が無くても行は行。フレックス項目としての縮み方を切り分けるための対照。
66
+ </Text>
67
+ </CardDescription>
68
+ </CardHeader>
69
+ <CardContent>
70
+ <Flex gap="sm" align="center" wrap>
71
+ <Segmented aria-label="制度(単独)" defaultValue="all" options={OPTIONS} />
72
+ </Flex>
73
+ </CardContent>
74
+ </Card>
75
+ </PageContainer>
76
+ );
77
+ }
@@ -1,6 +1,12 @@
1
1
  import {
2
2
  Skeleton,
3
+ SkeletonArticle,
4
+ SkeletonAvatar,
5
+ SkeletonButton,
3
6
  SkeletonDetail,
7
+ SkeletonImage,
8
+ SkeletonInput,
9
+ SkeletonNode,
4
10
  SkeletonRows,
5
11
  SkeletonStat,
6
12
  SkeletonTable,
@@ -14,6 +20,8 @@ import { Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
14
20
  * SkeletonTable · pre-mount DataTable placeholder (rows, columns props).
15
21
  * SkeletonStat · StatCard/KPI tile placeholder (no props; use in ResponsiveGrid).
16
22
  * SkeletonDetail · single-record detail placeholder (title + metadata rows, no props).
23
+ * SkeletonArticle · avatar + heading + paragraph (Ant Design's own Skeleton shape).
24
+ * SkeletonAvatar/Button/Input/Node/Image · the shaped presets, also on the Skeleton namespace.
17
25
  * Never use a spinner overlay on skeletonable content.
18
26
  */
19
27
  export default function Demo() {
@@ -48,6 +56,55 @@ export default function Demo() {
48
56
  </CardContent>
49
57
  </Card>
50
58
 
59
+ <Card>
60
+ <CardHeader>
61
+ <CardTitle level={2}>SkeletonArticle · avatar + heading + paragraph</CardTitle>
62
+ <CardDescription>
63
+ Ant Design's own Skeleton shape. Defaults follow it exactly: no avatar, a 38% heading
64
+ and three body lines; with an avatar the heading drops to 50% and the body to two
65
+ lines. `active` swaps the resting pulse for the travelling sheen · `round` pills every
66
+ line · `loading={false}` renders children instead.
67
+ </CardDescription>
68
+ </CardHeader>
69
+ <CardContent>
70
+ <Flex direction="col" gap="lg">
71
+ <SkeletonArticle />
72
+ <SkeletonArticle avatar />
73
+ <SkeletonArticle avatar active round paragraph={{ rows: 4, width: ["90%", "80%"] }} />
74
+ <SkeletonArticle avatar paragraph={false} title={{ width: "14rem" }} />
75
+ </Flex>
76
+ </CardContent>
77
+ </Card>
78
+
79
+ <Card>
80
+ <CardHeader>
81
+ <CardTitle level={2}>Shaped presets · the boxes the controls occupy</CardTitle>
82
+ <CardDescription>
83
+ Each preset carries the box of the control it stands in for, from the --control-height
84
+ tier · SkeletonButton is two heights wide, SkeletonInput five. Reachable as named
85
+ exports or through the Skeleton namespace (Skeleton.Button…).
86
+ </CardDescription>
87
+ </CardHeader>
88
+ <CardContent>
89
+ <Flex direction="col" gap="md">
90
+ <Flex direction="row" gap="sm" align="center" wrap>
91
+ <SkeletonAvatar size="sm" />
92
+ <SkeletonAvatar />
93
+ <SkeletonAvatar size="lg" shape="square" />
94
+ <SkeletonButton size="sm" />
95
+ <SkeletonButton />
96
+ <SkeletonButton size="lg" shape="pill" active />
97
+ <SkeletonInput size="sm" />
98
+ </Flex>
99
+ <SkeletonInput block />
100
+ <Flex direction="row" gap="sm" align="center" wrap>
101
+ <SkeletonNode />
102
+ <SkeletonImage active />
103
+ </Flex>
104
+ </Flex>
105
+ </CardContent>
106
+ </Card>
107
+
51
108
  <Card>
52
109
  <CardHeader>
53
110
  <CardTitle>SkeletonRows · generic repeated rows</CardTitle>
@@ -280,6 +280,35 @@ export default function Demo() {
280
280
  </Flex>
281
281
  </CardContent>
282
282
  </Card>
283
+ <Card>
284
+ <CardHeader>
285
+ <CardTitle level={2}>刻みにない幅で折りたたむ</CardTitle>
286
+ <CardDescription>
287
+ hideBelow の 4 段(40/48/64/80rem)に無い幅は hideBelowRaw / hideFromRaw
288
+ で指定します。900px ちょうどでは「広い側」だけが残り、両方消える隙間はありません。
289
+ </CardDescription>
290
+ </CardHeader>
291
+ <CardContent>
292
+ <Flex align="center" gap="sm">
293
+ <Flex id="raw-wide-nav" hideBelowRaw={900} align="center" gap="sm">
294
+ <Button variant="ghost" size="sm">
295
+ 機能
296
+ </Button>
297
+ <Button variant="ghost" size="sm">
298
+ 料金
299
+ </Button>
300
+ <Button variant="ghost" size="sm">
301
+ 導入事例
302
+ </Button>
303
+ </Flex>
304
+ <Flex id="raw-narrow-nav" hideFromRaw={900} align="center" gap="sm">
305
+ <Button variant="outline" size="sm">
306
+ メニュー
307
+ </Button>
308
+ </Flex>
309
+ </Flex>
310
+ </CardContent>
311
+ </Card>
283
312
  <Card>
284
313
  <CardHeader>
285
314
  <CardTitle level={2}>軽い強調と行アクション</CardTitle>
@@ -39,6 +39,7 @@ import {
39
39
  PanelLeftClose,
40
40
  PanelLeftOpen,
41
41
  Search,
42
+ ChevronsUpDown,
42
43
  } from "lucide-react";
43
44
 
44
45
  /**
@@ -229,18 +230,31 @@ export default function Demo() {
229
230
  color: "hsl(var(--primary))",
230
231
  }}
231
232
  onProductClick={() => undefined}
232
- footer={
233
- <Flex direction="col" gap="xs">
234
- <Text as="div" weight="medium">
235
- 山田 太郎
236
- </Text>
237
- <Flex align="center" gap="xs">
238
- <span className="bg-success size-1.5 rounded-full" />
239
- <Text size="xs" tone="muted">
240
- オンライン
233
+ /*
234
+ * footer · brand と同じ「関数を渡すと EFFECTIVE な collapsed が来る」形。自前の collapsed
235
+ * を読むと、AppShell が同じ Sidebar をドロワーにも渡す(ドロワーは展開表示に戻す)ため
236
+ * 全幅のドロワーの中でアイコンだけの足元になります。
237
+ */
238
+ footer={(railCollapsed) =>
239
+ railCollapsed ? (
240
+ <Flex justify="center">
241
+ <Avatar className="size-7 shrink-0">
242
+ <AvatarFallback>山</AvatarFallback>
243
+ </Avatar>
244
+ </Flex>
245
+ ) : (
246
+ <Flex direction="col" gap="xs">
247
+ <Text as="div" weight="medium">
248
+ 山田 太郎
241
249
  </Text>
250
+ <Flex align="center" gap="xs">
251
+ <span className="bg-success size-1.5 rounded-full" />
252
+ <Text size="xs" tone="muted">
253
+ オンライン
254
+ </Text>
255
+ </Flex>
242
256
  </Flex>
243
- </Flex>
257
+ )
244
258
  }
245
259
  />
246
260
  );
@@ -574,6 +588,46 @@ export default function Demo() {
574
588
  </CardContent>
575
589
  </Card>
576
590
 
591
+ {/* trailingIcon · 行の末尾に置く 16px の「開くよ」グリフ。badge(件数ピル)とは別枠。 */}
592
+ <Card>
593
+ <CardHeader>
594
+ <CardTitle level={2}>trailingIcon プロップ(末尾のグリフ)</CardTitle>
595
+ <CardDescription>
596
+ ワークスペース切替のような「押すと何か開く」行の末尾に置く 16px
597
+ のグリフです。件数ピルの badge とは別の枠で、背景も角丸も描きません · badge に ⌃⌄
598
+ を入れると .sb-badge の灰色ピル(実測 36×24、中の SVG は 24×24)に
599
+ なってしまうため、サイズを DS 側で固定した専用の枠にしています。 icon
600
+ と同じくコンポーネントを渡す形なので、SVG の寸法は 16px に固定されます。
601
+ 折りたたみレールでは badge と同じように隠れます。
602
+ </CardDescription>
603
+ </CardHeader>
604
+ <CardContent>
605
+ <Card className="h-64 w-64 overflow-hidden">
606
+ <CardContent flush>
607
+ <Flex direction="col">
608
+ <Sidebar
609
+ ariaLabel="切替行つきのナビゲーション"
610
+ activeId={brandActiveId}
611
+ onSelect={setBrandActiveId}
612
+ sections={BRAND_SECTIONS}
613
+ aria-label="trailingIcon 例のナビゲーション"
614
+ footer={
615
+ <SidebarItem
616
+ item={{
617
+ id: "workspace",
618
+ label: "アクメ株式会社",
619
+ icon: Building2,
620
+ trailingIcon: ChevronsUpDown,
621
+ }}
622
+ />
623
+ }
624
+ />
625
+ </Flex>
626
+ </CardContent>
627
+ </Card>
628
+ </CardContent>
629
+ </Card>
630
+
577
631
  {/* Feature notes */}
578
632
  <Card>
579
633
  <CardHeader>
@@ -590,6 +644,8 @@ export default function Demo() {
590
644
  "badgeTone で未読(neutral)と自分宛て(destructive)を色だけで区別",
591
645
  "disabled=true で項目を非活性化(クリック不可)",
592
646
  "footer prop でスクロール外にユーザー情報を固定",
647
+ "footer は brand と同じく関数も受け取る · 実効の collapsed が渡る",
648
+ "trailingIcon で行の末尾に 16px のグリフ(切替の ⌃⌄ など)を置ける",
593
649
  "linkComponent · ルーター Link は「要素だけ」渡す。行の中身はライブラリが組み立てる",
594
650
  "linkComponent は葉・サブメニュー・折りたたみレール・フライアウトの全てに適用される",
595
651
  "グループのトリガーは aria-expanded を持つ開閉ボタンのままなので linkComponent は適用されない",
@@ -70,7 +70,7 @@ export default function Demo() {
70
70
  </CardDescription>
71
71
  </CardHeader>
72
72
  <CardContent>
73
- <Flex direction="row" align="center" gap="md">
73
+ <Flex direction="row" align="center" gap="md" wrap>
74
74
  <Text>JE-0042 · 売上計上 ¥480,000</Text>
75
75
  <DropdownMenu>
76
76
  <DropdownMenuTrigger asChild>
@@ -102,7 +102,7 @@ export default function Demo() {
102
102
  </CardDescription>
103
103
  </CardHeader>
104
104
  <CardContent>
105
- <Flex direction="row" align="center" gap="md">
105
+ <Flex direction="row" align="center" gap="md" wrap>
106
106
  <DropdownMenu trigger={["contextMenu"]}>
107
107
  <DropdownMenuTrigger asChild>
108
108
  <Button variant="outline">仕訳 JE-0042 を右クリック</Button>
@@ -130,7 +130,7 @@ export default function Demo() {
130
130
  </CardDescription>
131
131
  </CardHeader>
132
132
  <CardContent>
133
- <Flex direction="row" align="center" gap="md">
133
+ <Flex direction="row" align="center" gap="md" wrap>
134
134
  <DropdownMenu trigger={["hover"]}>
135
135
  <DropdownMenuTrigger asChild>
136
136
  <Button variant="outline">エクスポート</Button>
@@ -171,7 +171,7 @@ export default function Demo() {
171
171
  </CardDescription>
172
172
  </CardHeader>
173
173
  <CardContent>
174
- <Flex direction="row" align="center" gap="md">
174
+ <Flex direction="row" align="center" gap="md" wrap>
175
175
  <span className="text-sm">
176
176
  現在のステータス:{" "}
177
177
  <strong>