@godxjp/ui 27.7.0 → 27.9.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 (37) hide show
  1. package/dist/components/data-display/range-timeline.d.ts +19 -0
  2. package/dist/components/data-display/range-timeline.js +2 -0
  3. package/dist/components/data-display/tree.js +2 -0
  4. package/dist/components/data-entry/upload.js +17 -15
  5. package/dist/components/layout/app-shell.d.ts +1 -1
  6. package/dist/components/layout/app-shell.js +8 -2
  7. package/dist/components/layout/topbar-item.d.ts +2 -0
  8. package/dist/components/layout/topbar-item.js +34 -4
  9. package/dist/components/layout/topbar.d.ts +1 -1
  10. package/dist/components/layout/topbar.js +2 -0
  11. package/dist/contracts/measurement.json +1 -1
  12. package/dist/props/components/data-display.prop.d.ts +11 -0
  13. package/dist/props/components/data-entry.prop.d.ts +6 -3
  14. package/dist/props/components/layout.prop.d.ts +56 -0
  15. package/dist/props/registry.d.ts +28 -4
  16. package/dist/props/registry.js +44 -4
  17. package/dist/styles/control.css +6 -7
  18. package/dist/styles/data-display-layout.css +25 -7
  19. package/dist/styles/data-entry-layout.css +13 -0
  20. package/dist/styles/layout.css +68 -4
  21. package/dist/styles/shell-layout.css +43 -1
  22. package/dist/tokens/components/control.css +5 -2
  23. package/dist/tokens/components/data-display.css +10 -1
  24. package/dist/tokens/components/shell.css +3 -0
  25. package/dist/tokens/components/tree.css +2 -0
  26. package/docs/DESIGN-AUTHORITY.md +20 -0
  27. package/docs/FRAME-COVERAGE-REPORT.md +1 -1
  28. package/docs/data-display/timeline.tsx +77 -1
  29. package/docs/data-display/tree.tsx +94 -0
  30. package/docs/data-entry/calendar.tsx +3 -2
  31. package/docs/general/icon.tsx +3 -1
  32. package/docs/layout/app-shell-narrow-bar.tsx +205 -0
  33. package/docs/layout/app-shell.tsx +10 -0
  34. package/docs/layout/topbar-item.tsx +37 -0
  35. package/docs/layout/topbar.tsx +11 -1
  36. package/package.json +2 -2
  37. package/scripts/cli.mjs +3 -0
@@ -448,6 +448,7 @@
448
448
 
449
449
  .ui-upload-row {
450
450
  display: flex;
451
+ flex-wrap: wrap;
451
452
  align-items: center;
452
453
  gap: var(--upload-row-space-gap);
453
454
  padding-inline: var(--upload-row-space-inline);
@@ -462,6 +463,18 @@
462
463
  flex: 1 1 0%;
463
464
  }
464
465
 
466
+ .ui-upload-row-actions {
467
+ display: flex;
468
+ flex-wrap: wrap;
469
+ align-items: center;
470
+ gap: var(--upload-row-space-gap);
471
+ margin-inline-start: auto;
472
+ }
473
+
474
+ .ui-upload-row-actions:empty {
475
+ display: none;
476
+ }
477
+
465
478
  .ui-upload-row-icon {
466
479
  inline-size: var(--upload-row-icon-size);
467
480
  block-size: var(--upload-row-icon-size);
@@ -1450,6 +1450,7 @@
1450
1450
 
1451
1451
  @media (width < 40rem) {
1452
1452
  .ui-flex[data-hide-below="sm"],
1453
+ .app-topbar-brand[data-hide-below="sm"],
1453
1454
  .ui-topbar-item[data-hide-below="sm"],
1454
1455
  [data-slot="table-head"][data-hide-below="sm"],
1455
1456
  [data-slot="table-cell"][data-hide-below="sm"] {
@@ -1459,6 +1460,7 @@
1459
1460
 
1460
1461
  @media (width < 48rem) {
1461
1462
  .ui-flex[data-hide-below="md"],
1463
+ .app-topbar-brand[data-hide-below="md"],
1462
1464
  .ui-topbar-item[data-hide-below="md"],
1463
1465
  [data-slot="table-head"][data-hide-below="md"],
1464
1466
  [data-slot="table-cell"][data-hide-below="md"] {
@@ -1468,6 +1470,7 @@
1468
1470
 
1469
1471
  @media (width < 64rem) {
1470
1472
  .ui-flex[data-hide-below="lg"],
1473
+ .app-topbar-brand[data-hide-below="lg"],
1471
1474
  .ui-topbar-item[data-hide-below="lg"],
1472
1475
  [data-slot="table-head"][data-hide-below="lg"],
1473
1476
  [data-slot="table-cell"][data-hide-below="lg"] {
@@ -1477,6 +1480,7 @@
1477
1480
 
1478
1481
  @media (width < 80rem) {
1479
1482
  .ui-flex[data-hide-below="xl"],
1483
+ .app-topbar-brand[data-hide-below="xl"],
1480
1484
  .ui-topbar-item[data-hide-below="xl"],
1481
1485
  [data-slot="table-head"][data-hide-below="xl"],
1482
1486
  [data-slot="table-cell"][data-hide-below="xl"] {
@@ -1485,28 +1489,88 @@
1485
1489
  }
1486
1490
 
1487
1491
  @media (width >= 40rem) {
1488
- .ui-flex[data-hide-from="sm"] {
1492
+ .ui-flex[data-hide-from="sm"],
1493
+ .app-topbar-brand[data-hide-from="sm"],
1494
+ .ui-topbar-item-icon[data-hide-from="sm"] {
1489
1495
  display: none;
1490
1496
  }
1491
1497
  }
1492
1498
 
1493
1499
  @media (width >= 48rem) {
1494
- .ui-flex[data-hide-from="md"] {
1500
+ .ui-flex[data-hide-from="md"],
1501
+ .app-topbar-brand[data-hide-from="md"],
1502
+ .ui-topbar-item-icon[data-hide-from="md"] {
1495
1503
  display: none;
1496
1504
  }
1497
1505
  }
1498
1506
 
1499
1507
  @media (width >= 64rem) {
1500
- .ui-flex[data-hide-from="lg"] {
1508
+ .ui-flex[data-hide-from="lg"],
1509
+ .app-topbar-brand[data-hide-from="lg"],
1510
+ .ui-topbar-item-icon[data-hide-from="lg"] {
1501
1511
  display: none;
1502
1512
  }
1503
1513
  }
1504
1514
 
1505
1515
  @media (width >= 80rem) {
1506
- .ui-flex[data-hide-from="xl"] {
1516
+ .ui-flex[data-hide-from="xl"],
1517
+ .app-topbar-brand[data-hide-from="xl"],
1518
+ .ui-topbar-item-icon[data-hide-from="xl"] {
1507
1519
  display: none;
1508
1520
  }
1509
1521
  }
1522
+
1523
+ @media (width < 40rem) {
1524
+ .ui-topbar-item-label[data-hide-below="sm"] {
1525
+ position: absolute;
1526
+ inset-block-start: 0;
1527
+ inset-inline-start: 0;
1528
+ inline-size: var(--stroke-hairline);
1529
+ block-size: var(--stroke-hairline);
1530
+ overflow: hidden;
1531
+ clip-path: inset(50%);
1532
+ white-space: nowrap;
1533
+ }
1534
+ }
1535
+
1536
+ @media (width < 48rem) {
1537
+ .ui-topbar-item-label[data-hide-below="md"] {
1538
+ position: absolute;
1539
+ inset-block-start: 0;
1540
+ inset-inline-start: 0;
1541
+ inline-size: var(--stroke-hairline);
1542
+ block-size: var(--stroke-hairline);
1543
+ overflow: hidden;
1544
+ clip-path: inset(50%);
1545
+ white-space: nowrap;
1546
+ }
1547
+ }
1548
+
1549
+ @media (width < 64rem) {
1550
+ .ui-topbar-item-label[data-hide-below="lg"] {
1551
+ position: absolute;
1552
+ inset-block-start: 0;
1553
+ inset-inline-start: 0;
1554
+ inline-size: var(--stroke-hairline);
1555
+ block-size: var(--stroke-hairline);
1556
+ overflow: hidden;
1557
+ clip-path: inset(50%);
1558
+ white-space: nowrap;
1559
+ }
1560
+ }
1561
+
1562
+ @media (width < 80rem) {
1563
+ .ui-topbar-item-label[data-hide-below="xl"] {
1564
+ position: absolute;
1565
+ inset-block-start: 0;
1566
+ inset-inline-start: 0;
1567
+ inline-size: var(--stroke-hairline);
1568
+ block-size: var(--stroke-hairline);
1569
+ overflow: hidden;
1570
+ clip-path: inset(50%);
1571
+ white-space: nowrap;
1572
+ }
1573
+ }
1510
1574
  }
1511
1575
 
1512
1576
  @layer components {
@@ -737,6 +737,28 @@
737
737
  align-items: center;
738
738
  }
739
739
 
740
+ .app-topbar-logo {
741
+ max-inline-size: var(--app-shell-brand-max-inline-size);
742
+ min-inline-size: 0;
743
+ flex: 0 1 auto;
744
+ overflow: clip;
745
+ overflow-clip-margin: var(--focus-ring-clip-margin);
746
+ }
747
+
748
+ .app-topbar-brand {
749
+ display: contents;
750
+ }
751
+
752
+ @media (width < 40rem) {
753
+ .app-topbar-logo {
754
+ max-inline-size: var(--app-shell-brand-compact-max-inline-size);
755
+ }
756
+
757
+ .app-topbar-logo [data-slot="logo-wordmark"] {
758
+ display: none;
759
+ }
760
+ }
761
+
740
762
  .app-topbar-custom {
741
763
  display: flex;
742
764
  min-width: 0;
@@ -1997,12 +2019,32 @@
1997
2019
  flex: 1 1 0%;
1998
2020
  justify-content: center;
1999
2021
  }
2022
+
2000
2023
  .ui-topbar-end {
2001
2024
  max-width: 100%;
2002
- flex: 0 0 auto;
2025
+ flex: 0 1 auto;
2003
2026
  margin-inline-start: auto;
2004
2027
  }
2005
2028
 
2029
+ .ui-topbar[data-overflow="scroll"] {
2030
+ overflow-x: auto;
2031
+ overscroll-behavior-inline: contain;
2032
+ scrollbar-width: none;
2033
+ }
2034
+
2035
+ .ui-topbar[data-overflow="scroll"]::-webkit-scrollbar {
2036
+ display: none;
2037
+ }
2038
+
2039
+ .ui-topbar[data-overflow="scroll"] > .ui-topbar-end {
2040
+ max-inline-size: none;
2041
+ min-inline-size: auto;
2042
+ }
2043
+
2044
+ .ui-topbar[data-overflow="scroll"] > .ui-topbar-start {
2045
+ min-inline-size: var(--topbar-item-min-width);
2046
+ }
2047
+
2006
2048
  .ui-topbar-item {
2007
2049
  display: inline-flex;
2008
2050
  min-width: var(--topbar-item-min-width);
@@ -281,14 +281,17 @@
281
281
  --tree-select-empty-space-block: var(--space-6);
282
282
 
283
283
  --calendar-space-inset: var(--space-3);
284
+
284
285
  --calendar-month-space-gap: var(--space-4);
285
286
  --calendar-caption-space-inline: var(--space-8);
286
287
  --calendar-caption-font-size: var(--font-size-sm);
287
288
  --calendar-nav-space-block-start: var(--space-3);
288
- --calendar-nav-space-inline: var(--space-1);
289
+
290
+ --calendar-nav-space-inline: var(--calendar-space-inset);
289
291
 
290
292
  --calendar-nav-rest-alpha: 0.7;
291
- --calendar-grid-space-block-start: var(--space-4);
293
+
294
+ --calendar-grid-space-block-start: var(--space-3);
292
295
  --calendar-week-space-block-start: var(--space-2);
293
296
  --calendar-weekday-font-size: var(--font-size-xs);
294
297
  --calendar-weekday-radius: var(--radius-md);
@@ -132,9 +132,12 @@
132
132
  --thumbnail-radius: var(--radius);
133
133
 
134
134
  --timeline-dot-done-background: initial;
135
+ --timeline-dot-done-foreground: initial;
135
136
  --timeline-dot-current-background: initial;
136
137
  --timeline-line-completed-background: initial;
137
138
 
139
+ --timeline-dot-current-ring-width: var(--stroke-xl);
140
+
138
141
  --qr-code-foreground: 0 0% 0%;
139
142
  --qr-code-background: 0 0% 100%;
140
143
  --qr-code-size-xs: 6rem;
@@ -173,9 +176,15 @@
173
176
 
174
177
  :root {
175
178
  --range-timeline-label-width: var(--app-shell-sidebar-width);
176
- --range-timeline-unit-width: calc(var(--control-height-sm) * 2);
179
+
180
+ --range-timeline-unit-width: var(--range-timeline-unit-width-default);
181
+
182
+ --range-timeline-unit-width-compact: calc(var(--control-height-sm) * 1.5);
183
+ --range-timeline-unit-width-default: calc(var(--control-height-sm) * 2);
184
+ --range-timeline-unit-width-comfortable: calc(var(--control-height-sm) * 2.5);
177
185
  --range-timeline-row-height: var(--control-height-lg);
178
186
  --range-timeline-bar-height: var(--control-height-sm);
187
+
179
188
  --range-timeline-border-color: var(--border);
180
189
 
181
190
  --range-timeline-grid-color: initial;
@@ -43,6 +43,9 @@
43
43
  --app-shell-bar-inset-compact: var(--space-page-compact-x);
44
44
  --app-shell-bar-gap: var(--space-3);
45
45
 
46
+ --app-shell-brand-max-inline-size: none;
47
+ --app-shell-brand-compact-max-inline-size: var(--app-shell-bar-height);
48
+
46
49
  --app-shell-sidebar-width: 16rem;
47
50
  --app-shell-sidebar-collapsed-width: 4rem;
48
51
 
@@ -19,6 +19,8 @@
19
19
 
20
20
  --tree-line-color: initial;
21
21
 
22
+ --tree-divider-color: initial;
23
+
22
24
  --tree-node-hover-background: initial;
23
25
  --tree-node-selected-background: initial;
24
26
  --tree-node-selected-foreground: initial;
@@ -298,6 +298,26 @@ bottomEnd | none` (`TablePaginationPositionProp`, default `['bottomEnd']`), the
298
298
  purpose: omitted, `flow="columns"` keeps `start` and `flow="rows"` keeps the grid's stretch, so
299
299
  no existing grid moves; antd's `Row` default is `top` in every case.
300
300
 
301
+ - **`Tree.divided` — a new capability, named from this library, not from antd (gh#732).** antd's
302
+ `Tree` has no horizontal rule between nodes at all (only `showLine`, the VERTICAL parent/child
303
+ rail), so there is no antd spelling to port and nothing to be out of parity with. The name was
304
+ picked from the two the library already uses for the idea, and they are not interchangeable here:
305
+ **`bordered` means a GRID** — `Table` / `DataTable` "outer frame + vertical rules between
306
+ columns", `Descriptions` "bordered table with shaded label cells", `Calendar` "rule the day
307
+ grid", `RangeTimeline` "row rules, header rule, a vertical rule per column" — while the
308
+ horizontal hairline BETWEEN rows of a vertical list is called a **divider** everywhere it exists
309
+ (`--list-row-border` on `ListRow`, `--command-item-divider-color`, `--table-flush-divider-*`,
310
+ `--card-*-divider`). A `Tree` has no columns and no frame, so `bordered` would promise chrome the
311
+ prop does not draw and would collide with a real Tree frame later. `Command.split` is the same
312
+ hairline, but that name is antd `List.split` adopted verbatim (gh#699) and carries a second
313
+ meaning there — zero list padding, rows bled to the panel edge — which `Tree` must not inherit.
314
+ So: `divided`, matching the token `--tree-divider-color`, a positive boolean adjective like
315
+ `striped` (gh#700). It **defaults to `false`**, unlike `RangeTimeline bordered`: a Gantt body is
316
+ unreadable without a grid, whereas `Tree`'s documented chrome default is quiet (cardinal rule
317
+ #44 — `showLine` ships off for the same reason, and the token file states it), and default-on
318
+ would rule every existing tree, including the picker-sized and `size="xs"` outlines where a rule
319
+ every 24px is noise rather than structure. A tree used as page navigation opts in.
320
+
301
321
  - **`RangeTimeline` nested rows take `Tree`'s spelling, not antd's (gh#724).** antd has no Gantt;
302
322
  the nearest names are `Tree expandedKeys` / `defaultExpandedKeys` / `onExpand` and `Table
303
323
  expandable.expandedRowKeys` / `onExpandedRowsChange`. This library already mapped the Tree ones
@@ -45,7 +45,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
45
45
  | CardBar | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
46
46
  | TimeRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
47
47
  | VisuallyHidden | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
48
- | RangeTimeline | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
48
+ | RangeTimeline | **✗** | · | N/A | N/A | · | · | · | · | · | · |
49
49
  | PageContainer | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
50
50
  | Flex | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
51
51
  | ResponsiveGrid | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
@@ -1,3 +1,5 @@
1
+ import type { CSSProperties } from "react";
2
+
1
3
  import { useTranslation } from "@godxjp/ui/i18n";
2
4
  import {
3
5
  Card,
@@ -142,7 +144,10 @@ export default function Demo() {
142
144
  <CardTitle level={2}>承認・消費税トラッカー</CardTitle>
143
145
  <CardDescription>
144
146
  variant="status" は status ごとにグリフを切り替えます(done → チェック、current →
145
- 塗りつぶしドット、pending → 連番)。
147
+ 塗りつぶしドット、pending → 連番)。進捗レールの色は1つだけ:
148
+ done・current・通過済みの線 はすべて --primary で塗られ、current
149
+ はリング(--timeline-dot-current-ring-width)と グリフで区別します。Steps の finish /
150
+ process と同じ約束です。
146
151
  </CardDescription>
147
152
  </CardHeader>
148
153
  <CardContent>
@@ -163,6 +168,31 @@ export default function Demo() {
163
168
  </CardContent>
164
169
  </Card>
165
170
 
171
+ <Card>
172
+ <CardHeader>
173
+ <CardTitle level={2}>進捗レールの色をテーマで戻す</CardTitle>
174
+ <CardDescription>
175
+ done の緑と current の紫という 27.8 までの配色に戻したい場合は、塗りとインクを
176
+ 対にしてテーマで指定します。塗りだけを戻すと、若竹の緑にほぼ白のグリフが乗って 2.18:1
177
+ になり AA を満たしません(gh#643)。同じ要領で --timeline-dot-current-background と
178
+ --timeline-line-completed-background も再着色できます。
179
+ </CardDescription>
180
+ </CardHeader>
181
+ {/* The knobs are declared on the CARD BODY, not on the Timeline: custom properties
182
+ inherit, so one scope retints every Timeline inside it — which is how a service
183
+ theme sets them (once, globally, or under `[data-tenant]`), not per call site. */}
184
+ <CardContent
185
+ style={
186
+ {
187
+ "--timeline-dot-done-background": "hsl(var(--success))",
188
+ "--timeline-dot-done-foreground": "hsl(var(--success-foreground))",
189
+ } as CSSProperties
190
+ }
191
+ >
192
+ <Timeline variant="status" items={approvalItems} />
193
+ </CardContent>
194
+ </Card>
195
+
166
196
  <Card>
167
197
  <CardHeader>
168
198
  <CardTitle level={2}>RangeTimeline</CardTitle>
@@ -236,6 +266,52 @@ export default function Demo() {
236
266
  </CardContent>
237
267
  </Card>
238
268
 
269
+ <Card>
270
+ <CardHeader>
271
+ <CardTitle level={2}>密度(density)— 1日の列幅</CardTitle>
272
+ <CardDescription>
273
+ 同じ31日分の軸を density で三段。compact は1日42px、default
274
+ は従来どおり56px、comfortable は70px です。動くのは列幅だけで、行の高さもバーの高さも
275
+ ラベル列の幅も変わりません。画面に入る日数を増やしたいときは compact
276
+ を選びます(app.css でトークンを書く必要はありません)。
277
+ </CardDescription>
278
+ </CardHeader>
279
+ <CardContent>
280
+ <Flex direction="col" gap="md">
281
+ {(["compact", "default", "comfortable"] as const).map((density) => (
282
+ <div key={density}>
283
+ <Text size="xs" tone="muted">
284
+ density=&quot;{density}&quot;
285
+ </Text>
286
+ <RangeTimeline
287
+ label={t("rangeTimeline.schedule")}
288
+ density={density}
289
+ today={8}
290
+ bands={[{ label: monthLabel(8), units: 31 }]}
291
+ columns={Array.from({ length: 31 }, (_, index) => {
292
+ const weekday = new Date(Date.UTC(2026, 8, index + 1)).getUTCDay();
293
+ return {
294
+ label: new Intl.NumberFormat(locale).format(index + 1),
295
+ units: 1,
296
+ muted: weekday === 0 || weekday === 6,
297
+ };
298
+ })}
299
+ rows={[
300
+ { id: "design", label: "要件定義", start: 0, end: 6 },
301
+ { id: "build", label: "実装", start: 7, end: 22 },
302
+ { id: "review", label: "レビュー", start: 20, end: 29 },
303
+ ].map((row) => ({
304
+ ...row,
305
+ startLabel: dayLabel(row.start + 1),
306
+ endLabel: dayLabel(row.end + 1),
307
+ }))}
308
+ />
309
+ </div>
310
+ ))}
311
+ </Flex>
312
+ </CardContent>
313
+ </Card>
314
+
239
315
  <Card>
240
316
  <CardHeader>
241
317
  <CardTitle level={2}>月単位の軸(列ごとに units が異なる)</CardTitle>
@@ -100,6 +100,48 @@ const fileDetails: Record<string, { size: string; changed: string }> = {
100
100
  "docs/tree.mdx": { size: "4.1 KB", changed: "2026-09-10" },
101
101
  };
102
102
 
103
+ // ── the page index used as NAVIGATION inside a Card (`divided`, gh#732) ──────────────────────
104
+ const wikiPages: TreeNodeProp[] = [
105
+ {
106
+ value: "handbook",
107
+ label: "社内ハンドブック",
108
+ children: [
109
+ { value: "handbook/onboarding", label: "入社手続き", isLeaf: true },
110
+ { value: "handbook/expenses", label: "経費精算", isLeaf: true },
111
+ {
112
+ value: "handbook/security",
113
+ label: "情報セキュリティ",
114
+ children: [
115
+ { value: "handbook/security/passwords", label: "パスワード規程", isLeaf: true },
116
+ { value: "handbook/security/incidents", label: "インシデント報告", isLeaf: true },
117
+ ],
118
+ },
119
+ ],
120
+ },
121
+ {
122
+ value: "product",
123
+ label: "プロダクト仕様",
124
+ children: [
125
+ { value: "product/release", label: "リリース手順", isLeaf: true },
126
+ { value: "product/support", label: "サポート窓口", isLeaf: true },
127
+ ],
128
+ },
129
+ { value: "minutes", label: "議事録", isLeaf: true },
130
+ ];
131
+
132
+ const wikiPageDetails: Record<string, { updated: string; author: string }> = {
133
+ handbook: { updated: "2026-09-01", author: "総務部" },
134
+ "handbook/onboarding": { updated: "2026-08-18", author: "人事部" },
135
+ "handbook/expenses": { updated: "2026-07-30", author: "経理部" },
136
+ "handbook/security": { updated: "2026-09-11", author: "情報システム部" },
137
+ "handbook/security/passwords": { updated: "2026-09-11", author: "情報システム部" },
138
+ "handbook/security/incidents": { updated: "2026-06-02", author: "情報システム部" },
139
+ product: { updated: "2026-09-14", author: "プロダクト部" },
140
+ "product/release": { updated: "2026-09-14", author: "プロダクト部" },
141
+ "product/support": { updated: "2026-05-20", author: "CS部" },
142
+ minutes: { updated: "2026-09-16", author: "総務部" },
143
+ };
144
+
103
145
  // ── card 3 · async branches (fieldNames + loadData + titleRender) ────────────────────────────
104
146
  type Department = { id: string; name: string; leaf?: boolean; units?: Department[] };
105
147
 
@@ -130,6 +172,8 @@ export default function Demo() {
130
172
  const [granted, setGranted] = React.useState<string[]>(["billing.invoice.read"]);
131
173
  // Card 2 — selection is controlled and drives the detail pane.
132
174
  const [openFile, setOpenFile] = React.useState<string | undefined>("src/components/tree.tsx");
175
+ // The page index — the tree IS the navigation, so selection is what the reading pane follows.
176
+ const [openPage, setOpenPage] = React.useState<string | undefined>("handbook/security/passwords");
133
177
  // Card 3 — async children land in state, exactly as a real fetch would.
134
178
  const [departments, setDepartments] = React.useState<Department[]>(initialDepartments);
135
179
  // Card 5 — independent (strict) checks, and a multi-select tree.
@@ -239,6 +283,56 @@ export default function Demo() {
239
283
  </CardContent>
240
284
  </Card>
241
285
 
286
+ <Card>
287
+ <CardHeader>
288
+ <CardTitle level={2}>ページ一覧をナビゲーションに使う(divided)</CardTitle>
289
+ <CardDescription>
290
+ ドキュメント/wiki のページ一覧のように、ツリーそのものが画面のナビゲーションになる
291
+ ときは divided で行間に罫を引きます。罫は各行の block-start に 1
292
+ 本ずつ、外側の最初の行にだけ引かれません。 深さに関係なく行の幅いっぱいに走るので
293
+ (インデントは行の padding であって margin ではありません)、 子行が「表の中の表」に
294
+ 見えることはありません。色は --tree-divider-color(既定 hsl(var(--border)))。 divided
295
+ は既定オフで、このページの他のカードがその既定の姿です。
296
+ </CardDescription>
297
+ </CardHeader>
298
+ <CardContent>
299
+ <Flex gap="lg" wrap>
300
+ <Card className="w-72">
301
+ <CardHeader>
302
+ <CardTitle level={3}>ページ一覧</CardTitle>
303
+ </CardHeader>
304
+ <CardContent flush>
305
+ <Tree
306
+ aria-label="ページ一覧"
307
+ divided
308
+ treeData={wikiPages}
309
+ defaultExpandAll
310
+ value={openPage}
311
+ onValueChange={(next) => setOpenPage(next as string | undefined)}
312
+ />
313
+ </CardContent>
314
+ </Card>
315
+ <Flex direction="col" gap="sm">
316
+ {openPage && wikiPageDetails[openPage] ? (
317
+ <Descriptions
318
+ items={[
319
+ { label: "パス", children: openPage },
320
+ { label: "最終更新", children: wikiPageDetails[openPage].updated },
321
+ { label: "更新者", children: wikiPageDetails[openPage].author },
322
+ ]}
323
+ />
324
+ ) : (
325
+ <EmptyState
326
+ icon={FolderTree}
327
+ title="ページが選択されていません"
328
+ description="左の一覧からページを選んでください。"
329
+ />
330
+ )}
331
+ </Flex>
332
+ </Flex>
333
+ </CardContent>
334
+ </Card>
335
+
242
336
  <Card>
243
337
  <CardHeader>
244
338
  <CardTitle level={2}>組織ツリー(loadData · fieldNames · titleRender)</CardTitle>
@@ -77,8 +77,9 @@ export default function Demo() {
77
77
  罫線 (bordered) · 既定で表示 / bordered={"{false}"} で解除
78
78
  </CardTitle>
79
79
  <CardDescription>
80
- 日と日の間に罫線を引くのが既定。線色は --calendar-grid-border-color (既定 --input の
81
- 50%、背景に対し約 1.7:1)。選択日・今日・範囲の始点/中間/終点・無効日・前後月の日が
80
+ 日と日の間に罫線を引くのが既定。線色は --calendar-grid-border-color (既定 --border、
81
+ 背景に対し約 1.15:1)。表の行やカードの枠と同じ段なので、罫線が枠より濃くなることは
82
+ ない。選択日・今日・範囲の始点/中間/終点・無効日・前後月の日が
82
83
  罫線の中でも判別できる。罫線なしの浮いたボタン表示にしたい場合のみ bordered=
83
84
  {"{false}"} を渡す。
84
85
  </CardDescription>
@@ -183,7 +183,9 @@ export default function Demo() {
183
183
 
184
184
  <Flex direction="col" gap="xs">
185
185
  <Heading level={3}>Button の中 · 明示した段が Button の既定に勝つ</Heading>
186
- <Flex direction="row" gap="sm" align="center">
186
+ {/* 320px では 2 つのラベル付き Button が 1 行に収まらない。Button はラベル幅より
187
+ 縮まないので、行を折り返させるのは並べた側の責任(`wrap`)。 */}
188
+ <Flex direction="row" gap="sm" align="center" wrap>
187
189
  <Button size="sm">
188
190
  <Icon as={Download} size="sm" />
189
191
  書き出す