@godxjp/ui 19.3.1 → 19.4.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 (116) hide show
  1. package/dist/components/data-display/badge.d.ts +9 -1
  2. package/dist/components/data-display/badge.js +3 -2
  3. package/dist/components/data-display/index.d.ts +2 -0
  4. package/dist/components/data-display/index.js +2 -0
  5. package/dist/components/data-display/popover.d.ts +13 -1
  6. package/dist/components/data-display/popover.js +3 -1
  7. package/dist/components/data-display/table.d.ts +19 -2
  8. package/dist/components/data-display/table.js +4 -1
  9. package/dist/components/data-display/timeline-grid.d.ts +18 -0
  10. package/dist/components/data-display/timeline-grid.js +222 -0
  11. package/dist/components/data-entry/cascader.js +4 -6
  12. package/dist/components/data-entry/checkbox.js +1 -1
  13. package/dist/components/data-entry/search-select.js +5 -7
  14. package/dist/components/data-entry/select.js +32 -28
  15. package/dist/components/data-entry/tree-select.js +4 -6
  16. package/dist/components/general/inert-background.d.ts +5 -0
  17. package/dist/components/general/inert-background.js +48 -0
  18. package/dist/components/layout/auth-shell.d.ts +4 -2
  19. package/dist/components/layout/auth-shell.js +7 -1
  20. package/dist/components/layout/centered-shell.d.ts +2 -2
  21. package/dist/components/layout/flex.d.ts +1 -1
  22. package/dist/components/layout/flex.js +2 -1
  23. package/dist/components/layout/index.d.ts +3 -0
  24. package/dist/components/layout/index.js +2 -0
  25. package/dist/components/layout/mobile-shell.d.ts +21 -0
  26. package/dist/components/layout/mobile-shell.js +43 -0
  27. package/dist/components/layout/sidebar-link.d.ts +1 -1
  28. package/dist/components/navigation/context-menu.js +13 -9
  29. package/dist/components/navigation/dropdown-menu.js +17 -13
  30. package/dist/inertia/index.d.ts +1 -1
  31. package/dist/lib/variants.d.ts +3 -3
  32. package/dist/lib/variants.js +1 -0
  33. package/dist/props/components/data-display.prop.d.ts +65 -0
  34. package/dist/props/components/data-entry.prop.d.ts +1 -1
  35. package/dist/props/components/layout.prop.d.ts +93 -6
  36. package/dist/props/registry.d.ts +109 -2
  37. package/dist/props/registry.js +143 -0
  38. package/dist/props/vocabulary/data.prop.d.ts +6 -0
  39. package/dist/props/vocabulary/index.d.ts +3 -3
  40. package/dist/props/vocabulary/layout.prop.d.ts +17 -2
  41. package/dist/props/vocabulary/shared.prop.d.ts +7 -0
  42. package/dist/styles/control.css +18 -1
  43. package/dist/styles/data-display-layout.css +167 -0
  44. package/dist/styles/layout.css +7 -0
  45. package/dist/styles/shell-layout.css +148 -0
  46. package/dist/styles/table-layout.css +23 -4
  47. package/dist/tokens/components/control.css +1 -0
  48. package/dist/tokens/components/data-display.css +20 -0
  49. package/dist/tokens/components/shell.css +22 -0
  50. package/dist/tokens/components/table.css +2 -0
  51. package/docs/SPACING.md +19 -6
  52. package/docs/charts/compact-bar-trend.tsx +5 -6
  53. package/docs/data-display/avatar.tsx +7 -9
  54. package/docs/data-display/badge.tsx +1 -1
  55. package/docs/data-display/card/index.tsx +6 -4
  56. package/docs/data-display/data-table/examples/approval-queue.tsx +1 -1
  57. package/docs/data-display/data-table/index.tsx +1 -1
  58. package/docs/data-display/permission-matrix.tsx +2 -2
  59. package/docs/data-display/scroll-area.tsx +1 -1
  60. package/docs/data-display/table.tsx +1 -1
  61. package/docs/data-display/timeline-grid.tsx +119 -0
  62. package/docs/data-entry/branch-scope-picker.tsx +1 -1
  63. package/docs/data-entry/input-otp.tsx +1 -1
  64. package/docs/data-entry/textarea.tsx +1 -1
  65. package/docs/data-entry/toggle-count.tsx +1 -1
  66. package/docs/feedback/alert.tsx +1 -1
  67. package/docs/feedback/banner.tsx +1 -1
  68. package/docs/foundation/email-tokens.tsx +1 -1
  69. package/docs/general/activity.tsx +1 -1
  70. package/docs/layout/auth-account-summary.tsx +1 -1
  71. package/docs/layout/auth-recovery/examples/mobile-390.tsx +1 -1
  72. package/docs/layout/auth-recovery/examples/password-recovery.tsx +0 -2
  73. package/docs/layout/auth-shell-context.tsx +1 -1
  74. package/docs/layout/auth-shell-device.tsx +2 -2
  75. package/docs/layout/auth-shell-registration.tsx +1 -1
  76. package/docs/layout/auth-shell.tsx +1 -1
  77. package/docs/layout/master-detail.tsx +2 -2
  78. package/docs/layout/mobile-shell.tsx +101 -0
  79. package/docs/layout/page-container.tsx +6 -6
  80. package/docs/layout/responsive-grid.tsx +2 -2
  81. package/docs/layout/separator.tsx +2 -2
  82. package/docs/layout/service-role-panel.tsx +1 -1
  83. package/docs/layout/sidebar.tsx +6 -6
  84. package/docs/layout/split-pane.tsx +1 -1
  85. package/docs/layout/topbar.tsx +2 -8
  86. package/docs/navigation/filter-bar.tsx +2 -2
  87. package/docs/navigation/pagination.tsx +2 -2
  88. package/docs/navigation/steps.tsx +1 -1
  89. package/docs/navigation/tabs.tsx +1 -1
  90. package/docs/showcase/acme-portal.tsx +11 -12
  91. package/docs/showcase/acme-website.tsx +7 -7
  92. package/docs/showcase/case1-warehouse-dashboard.tsx +11 -12
  93. package/docs/showcase/case2-employee-me.tsx +10 -16
  94. package/docs/showcase/case4-login.tsx +144 -156
  95. package/docs/showcase/case5-shift-calendar.tsx +181 -280
  96. package/docs/showcase/case6-agency-handy.tsx +446 -443
  97. package/docs/showcase/futurelastic-web.tsx +9 -8
  98. package/docs/showcase/org-switcher.tsx +18 -11
  99. package/docs/showcase/permission-matrix.tsx +13 -13
  100. package/docs/showcase/public-landing.tsx +1 -1
  101. package/docs/showcase/settings-account-sections.tsx +2 -2
  102. package/docs/showcase/settings-security-mfa.tsx +3 -3
  103. package/docs/showcase/table-approval-queue.tsx +1 -1
  104. package/docs/showcase/table-bulk-actions.tsx +49 -52
  105. package/docs/showcase/table-conditional-format.tsx +1 -1
  106. package/docs/showcase/table-crud-list.tsx +1 -1
  107. package/docs/showcase/table-expandable-rows.tsx +101 -98
  108. package/docs/showcase/table-filter-chips.tsx +78 -66
  109. package/docs/showcase/table-footer-totals.tsx +23 -19
  110. package/docs/showcase/table-grouped-subtotals.tsx +7 -4
  111. package/docs/showcase/table-master-detail.tsx +12 -14
  112. package/docs/showcase/table-states.tsx +34 -29
  113. package/docs/showcase/table-sticky-columns.tsx +3 -2
  114. package/docs/showcase/table-tree-rows.tsx +22 -15
  115. package/docs/showcase/table-view-tabs.tsx +12 -14
  116. package/package.json +7 -6
@@ -251,7 +251,6 @@ const EMPLOYEES: Employee[] = [
251
251
  // ── Shared cell padding to match compact DataTable density ─────────────────────
252
252
 
253
253
  const CELL = "align-middle";
254
- const HEAD = "px-3";
255
254
 
256
255
  function StatusBadge({ status }: { status: ShiftStatus }) {
257
256
  return (
@@ -265,103 +264,105 @@ function StatusBadge({ status }: { status: ShiftStatus }) {
265
264
 
266
265
  function DetailPanel({ employee }: { employee: Employee }) {
267
266
  return (
268
- <div className="border-s-primary bg-muted/30 border-s-[3px] px-4 py-3">
269
- <Flex direction="col" gap="md">
270
- <Flex direction="row" wrap align="center" gap="md">
271
- <Text size="sm" weight="medium">
272
- {employee.name} · {employee.dept}
273
- </Text>
274
- <Flex align="center" gap="xs">
275
- <Text size="xs" tone="muted">
276
- <MapPin className="size-3.5" aria-hidden="true" />
277
- {employee.site}
267
+ <Card className="border-s-primary bg-muted/30 border-s-[3px]">
268
+ <CardContent solo>
269
+ <Flex direction="col" gap="md">
270
+ <Flex direction="row" wrap align="center" gap="md">
271
+ <Text size="sm" weight="medium">
272
+ {employee.name} · {employee.dept}
278
273
  </Text>
274
+ <Flex align="center" gap="xs">
275
+ <Text size="xs" tone="muted">
276
+ <MapPin className="size-3.5" aria-hidden="true" />
277
+ {employee.site}
278
+ </Text>
279
+ </Flex>
279
280
  </Flex>
280
- </Flex>
281
281
 
282
- {/* KPI mini-row inside the panel — real StatCard primitives */}
283
- <Flex direction="row" wrap gap="sm">
284
- <StatCard label="出勤日数" value={employee.workDays} layout="inline" />
285
- <StatCard
286
- label="遅刻回数"
287
- value={employee.lateCount}
288
- layout="inline"
289
- inverse
290
- delta={employee.lateCount === 0 ? "問題なし" : "要確認"}
291
- />
292
- <StatCard
293
- label="残業 累計"
294
- value={<Text tabular>{employee.overtime}</Text>}
295
- layout="inline"
296
- />
297
- </Flex>
282
+ {/* KPI mini-row inside the panel — real StatCard primitives */}
283
+ <Flex direction="row" wrap gap="sm">
284
+ <StatCard label="出勤日数" value={employee.workDays} layout="inline" />
285
+ <StatCard
286
+ label="遅刻回数"
287
+ value={employee.lateCount}
288
+ layout="inline"
289
+ inverse
290
+ delta={employee.lateCount === 0 ? "問題なし" : "要確認"}
291
+ />
292
+ <StatCard
293
+ label="残業 累計"
294
+ value={<Text tabular>{employee.overtime}</Text>}
295
+ layout="inline"
296
+ />
297
+ </Flex>
298
298
 
299
- {/* Per-day breakdown — quiet nested list, real Card chrome */}
300
- <Card>
301
- <CardHeader>
302
- <CardTitle level={2} className="text-[var(--font-size-xs)]">
303
- 今週の打刻
304
- </CardTitle>
305
- <CardAction>
306
- <Flex align="center" gap="xs">
307
- <Text size="xs" tone="muted">
308
- <Clock className="size-3.5" aria-hidden="true" />
309
- 直近 3 日
310
- </Text>
311
- </Flex>
312
- </CardAction>
313
- </CardHeader>
314
- <CardContent flush>
315
- <Table>
316
- <TableHeader className="bg-secondary">
317
- <TableRow>
318
- <TableHead className={HEAD}>日付</TableHead>
319
- <TableHead className={HEAD}>状態</TableHead>
320
- <TableHead className={HEAD}>出勤</TableHead>
321
- <TableHead className={HEAD}>退勤</TableHead>
322
- <TableHead className={HEAD}>休憩</TableHead>
323
- <TableHead className={`${HEAD} text-end`}>実働</TableHead>
324
- <TableHead className={HEAD}>備考</TableHead>
325
- </TableRow>
326
- </TableHeader>
327
- <TableBody>
328
- {employee.days.map((d) => (
329
- <TableRow key={d.date}>
330
- <TableCell className={`${CELL} text-xs whitespace-nowrap tabular-nums`}>
331
- {d.date}({d.weekday})
332
- </TableCell>
333
- <TableCell className={CELL}>
334
- <StatusBadge status={d.status} />
335
- </TableCell>
336
- <TableCell className={`${CELL} tabular-nums`}>{d.clockIn}</TableCell>
337
- <TableCell className={`${CELL} tabular-nums`}>{d.clockOut}</TableCell>
338
- <TableCell
339
- className={`${CELL} text-muted-foreground inline-flex items-center gap-1 tabular-nums`}
340
- >
341
- <Coffee className="size-3.5" aria-hidden="true" />
342
- {d.break}
343
- </TableCell>
344
- <TableCell className={`${CELL} text-end font-medium tabular-nums`}>
345
- {d.work}
346
- </TableCell>
347
- <TableCell className={`${CELL} text-muted-foreground text-xs`}>
348
- {d.note ?? "—"}
349
- </TableCell>
299
+ {/* Per-day breakdown — quiet nested list, real Card chrome */}
300
+ <Card>
301
+ <CardHeader>
302
+ <CardTitle level={2} className="text-[var(--font-size-xs)]">
303
+ 今週の打刻
304
+ </CardTitle>
305
+ <CardAction>
306
+ <Flex align="center" gap="xs">
307
+ <Text size="xs" tone="muted">
308
+ <Clock className="size-3.5" aria-hidden="true" />
309
+ 直近 3 日
310
+ </Text>
311
+ </Flex>
312
+ </CardAction>
313
+ </CardHeader>
314
+ <CardContent flush>
315
+ <Table>
316
+ <TableHeader className="bg-secondary">
317
+ <TableRow>
318
+ <TableHead>日付</TableHead>
319
+ <TableHead>状態</TableHead>
320
+ <TableHead>出勤</TableHead>
321
+ <TableHead>退勤</TableHead>
322
+ <TableHead>休憩</TableHead>
323
+ <TableHead className="text-end">実働</TableHead>
324
+ <TableHead>備考</TableHead>
350
325
  </TableRow>
351
- ))}
352
- </TableBody>
353
- </Table>
354
- </CardContent>
355
- </Card>
326
+ </TableHeader>
327
+ <TableBody>
328
+ {employee.days.map((d) => (
329
+ <TableRow key={d.date}>
330
+ <TableCell className={`${CELL} text-xs whitespace-nowrap tabular-nums`}>
331
+ {d.date}({d.weekday})
332
+ </TableCell>
333
+ <TableCell className={CELL}>
334
+ <StatusBadge status={d.status} />
335
+ </TableCell>
336
+ <TableCell className={`${CELL} tabular-nums`}>{d.clockIn}</TableCell>
337
+ <TableCell className={`${CELL} tabular-nums`}>{d.clockOut}</TableCell>
338
+ <TableCell className={`${CELL} text-muted-foreground tabular-nums`}>
339
+ <Flex align="center" gap="xs">
340
+ <Coffee className="size-3.5" aria-hidden="true" />
341
+ {d.break}
342
+ </Flex>
343
+ </TableCell>
344
+ <TableCell className={`${CELL} text-end font-medium tabular-nums`}>
345
+ {d.work}
346
+ </TableCell>
347
+ <TableCell className={`${CELL} text-muted-foreground text-xs`}>
348
+ {d.note ?? "—"}
349
+ </TableCell>
350
+ </TableRow>
351
+ ))}
352
+ </TableBody>
353
+ </Table>
354
+ </CardContent>
355
+ </Card>
356
356
 
357
- <Flex direction="row" gap="sm">
358
- <Button size="sm" variant="outline">
359
- 勤怠詳細を開く
360
- </Button>
361
- <Button size="sm">承認する</Button>
357
+ <Flex direction="row" gap="sm">
358
+ <Button size="sm" variant="outline">
359
+ 勤怠詳細を開く
360
+ </Button>
361
+ <Button size="sm">承認する</Button>
362
+ </Flex>
362
363
  </Flex>
363
- </Flex>
364
- </div>
364
+ </CardContent>
365
+ </Card>
365
366
  );
366
367
  }
367
368
 
@@ -381,12 +382,12 @@ function ExpandableList() {
381
382
  <Table>
382
383
  <TableHeader className="bg-secondary sticky top-0 z-10">
383
384
  <TableRow>
384
- <TableHead className={`${HEAD} w-10`} aria-label="展開" />
385
- <TableHead className={HEAD}>従業員</TableHead>
386
- <TableHead className={HEAD}>部署</TableHead>
387
- <TableHead className={`${HEAD} text-center`}>直近の状態</TableHead>
388
- <TableHead className={`${HEAD} text-end`}>出勤日数</TableHead>
389
- <TableHead className={`${HEAD} text-end`}>残業 累計</TableHead>
385
+ <TableHead className="w-10" aria-label="展開" />
386
+ <TableHead>従業員</TableHead>
387
+ <TableHead>部署</TableHead>
388
+ <TableHead className="text-center">直近の状態</TableHead>
389
+ <TableHead className="text-end">出勤日数</TableHead>
390
+ <TableHead className="text-end">残業 累計</TableHead>
390
391
  </TableRow>
391
392
  </TableHeader>
392
393
  <TableBody>
@@ -434,7 +435,9 @@ function ExpandableList() {
434
435
  </TableRow>
435
436
  {isOpen && (
436
437
  <TableRow className="hover:bg-transparent">
437
- <TableCell colSpan={COLSPAN} className="p-0" id={panelId}>
438
+ {/* `flush` — the detail panel owns its own inset, so it spans the full cell
439
+ width instead of being indented by the cell padding. */}
440
+ <TableCell flush colSpan={COLSPAN} id={panelId}>
438
441
  <DetailPanel employee={emp} />
439
442
  </TableCell>
440
443
  </TableRow>
@@ -27,7 +27,14 @@ import * as React from "react";
27
27
  import { X, Filter } from "lucide-react";
28
28
 
29
29
  import { Button, Text } from "@godxjp/ui/general";
30
- import { Badge, DataTable, type ColumnDef } from "@godxjp/ui/data-display";
30
+ import {
31
+ Badge,
32
+ Card,
33
+ CardContent,
34
+ DataTable,
35
+ EmptyState,
36
+ type ColumnDef,
37
+ } from "@godxjp/ui/data-display";
31
38
  import { SearchInput, Select } from "@godxjp/ui/data-entry";
32
39
  import { Flex, PageContainer } from "@godxjp/ui/layout";
33
40
  import type { SortStateProp } from "@godxjp/ui/props";
@@ -256,7 +263,7 @@ function FilterChip({
256
263
  onRemove: () => void;
257
264
  }) {
258
265
  return (
259
- <Badge tone={tone ?? "neutral"} variant="outline" className="gap-1 pe-1">
266
+ <Badge tone={tone ?? "neutral"} variant="outline">
260
267
  {label}
261
268
  <Button
262
269
  variant="ghost"
@@ -354,64 +361,67 @@ export default function Demo() {
354
361
  </Flex>
355
362
 
356
363
  {/* Active-filter chip bar — visible at rest because filters are seeded */}
357
- <Flex
358
- direction="row"
359
- wrap
360
- align="center"
361
- gap="xs"
362
- className="border-border bg-secondary/30 min-h-9 rounded-md border px-2.5 py-1.5"
363
- >
364
- <Flex direction="row" align="center" gap="xs" className="text-muted-foreground pe-1">
365
- <Filter className="size-3.5" aria-hidden="true" />
366
- <Text size="xs">適用中</Text>
367
- </Flex>
364
+ <Card>
365
+ <CardContent solo>
366
+ <Flex direction="row" wrap align="center" gap="xs">
367
+ <Flex direction="row" align="center" gap="xs" className="text-muted-foreground">
368
+ <Filter className="size-3.5" aria-hidden="true" />
369
+ <Text size="xs">適用中</Text>
370
+ </Flex>
368
371
 
369
- {!hasFilters && (
370
- <Text size="xs" tone="muted">
371
- 条件なし · 全 {ROWS.length} 件
372
- </Text>
373
- )}
372
+ {!hasFilters && (
373
+ <Text size="xs" tone="muted">
374
+ 条件なし · 全 {ROWS.length} 件
375
+ </Text>
376
+ )}
374
377
 
375
- {query.trim() && (
376
- <FilterChip
377
- label={`検索: ${query.trim()}`}
378
- onRemove={() => {
379
- setQuery("");
380
- }}
381
- />
382
- )}
383
- {status && (
384
- <FilterChip
385
- tone={STATUS_META[status].tone}
386
- label={`状態: ${STATUS_META[status].label}`}
387
- onRemove={() => {
388
- setStatus(undefined);
389
- }}
390
- />
391
- )}
392
- {dept && (
393
- <FilterChip
394
- label={`部署: ${dept}`}
395
- onRemove={() => {
396
- setDept(undefined);
397
- }}
398
- />
399
- )}
400
- {ot !== "any" && (
401
- <FilterChip
402
- label={ot === "ot" ? "残業あり" : "残業 60分以上"}
403
- onRemove={() => {
404
- setOt("any");
405
- }}
406
- />
407
- )}
378
+ {query.trim() && (
379
+ <FilterChip
380
+ label={`検索: ${query.trim()}`}
381
+ onRemove={() => {
382
+ setQuery("");
383
+ }}
384
+ />
385
+ )}
386
+ {status && (
387
+ <FilterChip
388
+ tone={STATUS_META[status].tone}
389
+ label={`状態: ${STATUS_META[status].label}`}
390
+ onRemove={() => {
391
+ setStatus(undefined);
392
+ }}
393
+ />
394
+ )}
395
+ {dept && (
396
+ <FilterChip
397
+ label={`部署: ${dept}`}
398
+ onRemove={() => {
399
+ setDept(undefined);
400
+ }}
401
+ />
402
+ )}
403
+ {ot !== "any" && (
404
+ <FilterChip
405
+ label={ot === "ot" ? "残業あり" : "残業 60分以上"}
406
+ onRemove={() => {
407
+ setOt("any");
408
+ }}
409
+ />
410
+ )}
408
411
 
409
- {hasFilters && (
410
- <Button variant="ghost" size="sm" className="ms-auto h-6 text-xs" onClick={clearAll}>
411
- すべて解除
412
- </Button>
413
- )}
414
- </Flex>
412
+ {hasFilters && (
413
+ <Button
414
+ variant="ghost"
415
+ size="sm"
416
+ className="ms-auto h-6 text-xs"
417
+ onClick={clearAll}
418
+ >
419
+ すべて解除
420
+ </Button>
421
+ )}
422
+ </Flex>
423
+ </CardContent>
424
+ </Card>
415
425
 
416
426
  {/* The filtered table */}
417
427
  <DataTable
@@ -425,15 +435,17 @@ export default function Demo() {
425
435
  onSortChange={setSort}
426
436
  density="compact"
427
437
  empty={
428
- <Flex direction="col" align="center" gap="xs" className="py-8">
429
- <Text weight="medium">該当する勤怠記録がありません</Text>
430
- <Text size="xs" tone="muted">
431
- フィルター条件を解除すると全件を表示します
432
- </Text>
433
- <Button variant="outline" size="sm" onClick={clearAll}>
434
- フィルターを解除
435
- </Button>
436
- </Flex>
438
+ <EmptyState
439
+ variant="section"
440
+ titleAs="p"
441
+ title="該当する勤怠記録がありません"
442
+ description="フィルター条件を解除すると全件を表示します"
443
+ action={
444
+ <Button variant="outline" size="sm" onClick={clearAll}>
445
+ フィルターを解除
446
+ </Button>
447
+ }
448
+ />
437
449
  }
438
450
  >
439
451
  <DataTable.Toolbar>
@@ -289,25 +289,29 @@ export default function Demo() {
289
289
  </CardContent>
290
290
  </Card>
291
291
 
292
- <Flex direction="row" align="center" gap="xs" className="mt-3">
293
- <Text size="xs" tone="muted">
294
- 残業 凡例:
295
- </Text>
296
- <Badge tone="success" variant="outline">
297
- ~15h
298
- </Badge>
299
- <Badge tone="info" variant="outline">
300
- 15h~
301
- </Badge>
302
- <Badge tone="warning" variant="outline">
303
- 25h~
304
- </Badge>
305
- <Text size="xs" tone="muted" className="ms-2">
306
- 遅刻:
307
- </Text>
308
- <Badge tone="destructive" variant="outline">
309
- 回数
310
- </Badge>
292
+ <Flex direction="row" align="center" wrap gap="md">
293
+ <Flex direction="row" align="center" gap="xs">
294
+ <Text size="xs" tone="muted">
295
+ 残業 凡例:
296
+ </Text>
297
+ <Badge tone="success" variant="outline">
298
+ ~15h
299
+ </Badge>
300
+ <Badge tone="info" variant="outline">
301
+ 15h~
302
+ </Badge>
303
+ <Badge tone="warning" variant="outline">
304
+ 25h~
305
+ </Badge>
306
+ </Flex>
307
+ <Flex direction="row" align="center" gap="xs">
308
+ <Text size="xs" tone="muted">
309
+ 遅刻:
310
+ </Text>
311
+ <Badge tone="destructive" variant="outline">
312
+ 回数
313
+ </Badge>
314
+ </Flex>
311
315
  </Flex>
312
316
  </PageContainer>
313
317
  );
@@ -226,18 +226,18 @@ function GroupHeaderRow({
226
226
  const Chevron = open ? ChevronDown : ChevronRight;
227
227
  return (
228
228
  <TableRow className="bg-secondary hover:bg-secondary border-b">
229
- <TableCell className="ps-2 pe-0" colSpan={2}>
229
+ <TableCell colSpan={2}>
230
230
  <Button
231
231
  variant="ghost"
232
232
  size="sm"
233
233
  onClick={onToggle}
234
234
  aria-expanded={open}
235
235
  aria-controls={`grp-${group.id}`}
236
- className="h-7 gap-2 font-medium"
236
+ className="h-7 font-medium"
237
237
  >
238
238
  <Chevron className="text-muted-foreground size-4" aria-hidden="true" />
239
239
  {group.label}
240
- <Badge variant="outline" tone="neutral" className="ms-1">
240
+ <Badge variant="outline" tone="neutral">
241
241
  {group.members.length}名
242
242
  </Badge>
243
243
  </Button>
@@ -266,7 +266,10 @@ function GroupHeaderRow({
266
266
  function MemberRow({ m }: { m: Employee }) {
267
267
  return (
268
268
  <TableRow>
269
- <TableCell className="text-muted-foreground ps-10 font-mono text-xs">{m.id}</TableCell>
269
+ {/* `indent` the detail row sits one hierarchy level under its group header. */}
270
+ <TableCell indent={1} className="text-muted-foreground font-mono text-xs">
271
+ {m.id}
272
+ </TableCell>
270
273
  <TableCell>{m.name}</TableCell>
271
274
  <TableCell className="text-end tabular-nums">{m.days}日</TableCell>
272
275
  <TableCell className="text-end tabular-nums">{fmtH(m.work)}</TableCell>
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Composition map (intent → real @godxjp/ui primitive):
10
10
  * resizable split ........ ResizablePanelGroup + ResizablePanel + ResizableHandle
11
- * fixed-track split ...... MasterDetail (tokenized rail + stacking threshold, gh#223)
11
+ * fixed-track split ...... MasterDetail (tokenized rail + stacking threshold, )
12
12
  * master list ............ DataTable (compact, clickable rows, selected row)
13
13
  * status cell ............ Badge tone (出勤/遅刻/早退/休暇 — fixed signaling)
14
14
  * detail header .......... Avatar + heading + Badge + action Buttons
@@ -398,7 +398,7 @@ export default function Demo() {
398
398
 
399
399
  {/* Detail pane */}
400
400
  <ResizablePanel defaultSize="58%" minSize="34%">
401
- <div className="h-full overflow-auto p-5">
401
+ <CardContent solo className="h-full overflow-auto">
402
402
  {selected ? (
403
403
  <EmployeeDetail employee={selected} />
404
404
  ) : (
@@ -410,7 +410,7 @@ export default function Demo() {
410
410
  />
411
411
  </Flex>
412
412
  )}
413
- </div>
413
+ </CardContent>
414
414
  </ResizablePanel>
415
415
  </ResizablePanelGroup>
416
416
  </CardContent>
@@ -442,13 +442,11 @@ export default function Demo() {
442
442
  />
443
443
  }
444
444
  >
445
- <div className="rounded-md border border-dashed p-8">
446
- <EmptyState
447
- icon={UserRound}
448
- title="従業員を選択してください"
449
- description="一覧から行を選ぶと詳細が表示されます。"
450
- />
451
- </div>
445
+ <EmptyState
446
+ icon={UserRound}
447
+ title="従業員を選択してください"
448
+ description="一覧から行を選ぶと詳細が表示されます。"
449
+ />
452
450
  </MasterDetail>
453
451
  </CardContent>
454
452
  </Card>
@@ -472,10 +470,10 @@ export default function Demo() {
472
470
  selected={new Set(["E-1042"])}
473
471
  onRowClick={() => {}}
474
472
  />
475
- <Separator />
476
- <div className="p-5">
477
- <EmployeeDetail employee={EMPLOYEES[0]} />
478
- </div>
473
+ </CardContent>
474
+ <Separator />
475
+ <CardContent solo>
476
+ <EmployeeDetail employee={EMPLOYEES[0]} />
479
477
  </CardContent>
480
478
  </Card>
481
479
  </Flex>