/** * Showcase · table-conditional-format — 条件付き書式 (V19) * * Conditional row / cell formatting by threshold on a 勤怠 (attendance) summary: * - 遅刻 (late arrivals) ≥ 5回 → 行全体を danger(茜) で淡くティント * - 早退 (early leave) > 2.0h → セルを attention(朱) で強調 * - 残業 (overtime) ≥ 45h → セルを warning(山吹) で注意喚起 * * すべて semantic token 経由(bg-destructive/10・text-attention・text-warning-foreground)で、 * raw hex は一切使わない。閾値判定は純粋関数に集約し、書式は token クラスにマップする。 * * GAP: DataTable の ColumnDef は per-row / per-cell の className フックを持たない * (render は ReactNode を返すのみ)。行全体のティントには @godxjp/ui が同梱する * 実 primitive の Table / TableRow ファミリ(DataTable 内部と同一)で組み立て、 * TableRow に token クラスを渡す。セル単位の書式は ColumnDef でも表現できるため、 * 下段で DataTable + render の cell-only 版も併示する。 */ import * as React from "react"; import { Badge, Card, CardContent, CardHeader, CardTitle, DataTable, type ColumnDef, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@godxjp/ui/data-display"; import { Text } from "@godxjp/ui/general"; import { Flex, PageContainer } from "@godxjp/ui/layout"; import { cn } from "@godxjp/ui/lib/utils"; // ── 勤怠データ(当月締め · 製造部 第2ライン) ──────────────────────────────── type Attendance = { id: string; name: string; dept: string; workdays: number; // 出勤日数 late: number; // 遅刻回数 earlyLeaveHours: number; // 早退時間 (h) overtime: number; // 残業時間 (h) status: "approved" | "pending" | "review"; }; const ROWS: Attendance[] = [ { id: "E-1042", name: "田中 美咲", dept: "製造1課", workdays: 20, late: 1, earlyLeaveHours: 0, overtime: 12.5, status: "approved", }, { id: "E-1043", name: "佐藤 健", dept: "製造1課", workdays: 19, late: 6, earlyLeaveHours: 1.5, overtime: 8, status: "review", }, { id: "E-1044", name: "鈴木 陽子", dept: "製造2課", workdays: 20, late: 0, earlyLeaveHours: 2.5, overtime: 31, status: "pending", }, { id: "E-1045", name: "高橋 大輔", dept: "製造2課", workdays: 18, late: 3, earlyLeaveHours: 0, overtime: 46.5, status: "review", }, { id: "E-1046", name: "伊藤 さくら", dept: "検査課", workdays: 20, late: 0, earlyLeaveHours: 0, overtime: 4, status: "approved", }, { id: "E-1047", name: "渡辺 翔", dept: "検査課", workdays: 17, late: 8, earlyLeaveHours: 3.0, overtime: 22, status: "review", }, { id: "E-1048", name: "山本 直樹", dept: "出荷課", workdays: 20, late: 2, earlyLeaveHours: 0.5, overtime: 15, status: "approved", }, { id: "E-1049", name: "中村 あおい", dept: "出荷課", workdays: 19, late: 5, earlyLeaveHours: 2.25, overtime: 48, status: "review", }, ]; // ── 閾値(しきい値) ─────────────────────────────────────────────────────── const LATE_DANGER = 5; // 遅刻 ≥ 5回 → 行ティント (danger 茜) const EARLY_ATTENTION = 2; // 早退 > 2.0h → セル強調 (attention 朱) const OVERTIME_WARNING = 45; // 残業 ≥ 45h → セル注意 (warning 山吹) const isLateRow = (r: Attendance) => r.late >= LATE_DANGER; const isEarlyCell = (r: Attendance) => r.earlyLeaveHours > EARLY_ATTENTION; const isOvertimeCell = (r: Attendance) => r.overtime >= OVERTIME_WARNING; const num = new Intl.NumberFormat("ja-JP", { maximumFractionDigits: 2 }); const hours = (h: number) => `${num.format(h)}h`; // ── 凡例(しきい値の色対応を明示) ───────────────────────────────────────── const LEGEND: Array<{ swatch: string; label: string }> = [ { swatch: "bg-destructive/15 border-destructive/40", label: `遅刻 ${LATE_DANGER}回以上 → 行を強調(茜)`, }, { swatch: "bg-attention/15 border-attention/40", label: `早退 ${EARLY_ATTENTION}.0h 超 → セル強調(朱)`, }, { swatch: "bg-warning/15 border-warning/40", label: `残業 ${OVERTIME_WARNING}h 以上 → セル注意(山吹)`, }, ]; function Legend() { return ( {LEGEND.map((l) => ( ))} ); } export default function Demo() { // ── 下段 DataTable 用カラム(セル単位の条件付き書式は render で表現可能) ── const columns: ColumnDef[] = [ { key: "id", header: "社員番号", width: "w-24" }, { key: "name", header: "氏名" }, { key: "dept", header: "部署", hiddenOnMobile: true }, { key: "late", header: "遅刻", align: "right", sortable: true, render: (r) => ( = LATE_DANGER ? "destructive" : "default"} weight={r.late >= LATE_DANGER ? "medium" : "regular"} > {r.late}回 ), }, { key: "earlyLeaveHours", header: "早退", align: "right", sortable: true, render: (r) => isEarlyCell(r) ? ( {hours(r.earlyLeaveHours)} ) : ( {hours(r.earlyLeaveHours)} ), }, { key: "overtime", header: "残業", align: "right", sortable: true, render: (r) => isOvertimeCell(r) ? ( {hours(r.overtime)} ) : ( {hours(r.overtime)} ), }, { key: "status", header: "承認", align: "center", render: (r) => , }, ]; return ( {/* ── 行レベル + セルレベルの条件付き書式(実 Table primitive で合成) ── */} 勤怠サマリ · 製造部(当月締め) 対象 {ROWS.length}名 · 2026-05 社員番号 氏名 部署 出勤 遅刻 早退 残業 承認 {ROWS.map((r) => { const lateRow = isLateRow(r); return ( {r.id} {r.name} {lateRow && ( 遅刻多 )} {r.dept} {r.workdays}日 {/* 遅刻セル: しきい値以上は茜文字で前景強調 */} {r.late}回 {/* 早退セル: > 2.0h は attention(朱) で背景+前景強調 */} {isEarlyCell(r) ? ( {hours(r.earlyLeaveHours)} ) : ( {hours(r.earlyLeaveHours)} )} {/* 残業セル: ≥ 45h は warning(山吹) で注意 */} {isOvertimeCell(r) ? ( {hours(r.overtime)} ) : ( {hours(r.overtime)} )} ); })}
{/* ── DataTable 版(セル単位の条件付き書式は ColumnDef.render で表現) ── */} DataTable 版 · セル単位の条件付き書式 ColumnDef.render 内でしきい値判定 → token クラスを付与。行全体のティントは ColumnDef では表現できないため上段の実 Table 合成を参照(gapNotes 参照)。 r.id}>
); }