Data Tableデータテーブル
構造化されたデータを表形式で表示・操作するコンポーネントです。ソート、選択、カスタムレンダリングの機能も提供します。
更新 2025/7/10
基本的な使用方法
DataTableの基本的な使用方法です。データと列定義を渡すことで基本的なテーブルが実装できます。
ID | 氏名 | 部署 | 役職 | 給与 | |
---|---|---|---|---|---|
1 | 田中太郎 | 開発 | エンジニア | 450000 | |
2 | 佐藤花子 | 営業 | マネージャー | 520000 | |
3 | 山田次郎 | 開発 | シニアエンジニア | 680000 | |
4 | 鈴木美穂 | 人事 | アシスタント | 380000 |
import { DataTable } from "@serendie/ui";
export type Employee = {
id: number;
name: string;
department: string;
position: string;
salary: number;
};
const employees: Employee[] = [
{
id: 1,
name: "田中太郎",
department: "開発",
position: "エンジニア",
salary: 450000,
},
{
id: 2,
name: "佐藤花子",
department: "営業",
position: "マネージャー",
salary: 520000,
},
{
id: 3,
name: "山田次郎",
department: "開発",
position: "シニアエンジニア",
salary: 680000,
},
{
id: 4,
name: "鈴木美穂",
department: "人事",
position: "アシスタント",
salary: 380000,
},
];
export function BasicSample() {
const columnHelper = DataTable.createColumnHelper<Employee>();
const columns = [
columnHelper.accessor("id", {
header: "ID",
enableSorting: true,
}),
columnHelper.accessor("name", {
header: "氏名",
enableSorting: true,
}),
columnHelper.accessor("department", {
header: "部署",
enableSorting: true,
}),
columnHelper.accessor("position", {
header: "役職",
enableSorting: true,
}),
columnHelper.accessor("salary", {
header: "給与",
enableSorting: true,
}),
];
return <DataTable<Employee> data={employees} columns={columns} />;
}
カラム形式の指定
DataTableを使用する際の最初のステップは、テーブルのカラム形式を指定するためのColumnHelperの作成を行うことです。ColumnHelperはTypeScriptの型安全性を保ちながら、列の定義を簡単に行うためのヘルパー関数です。
ID | 商品名 | カテゴリ | 価格(円) | 在庫状況 | |
---|---|---|---|---|---|
1 | ノートパソコン | 電子機器 | 89000 | true | |
2 | マウス | 周辺機器 | 2500 | true | |
3 | キーボード | 周辺機器 | 8500 | false | |
4 | モニター | 電子機器 | 35000 | true |
import { DataTable } from "@serendie/ui";
export type Product = {
id: number;
name: string;
category: string;
price: number;
inStock: boolean;
};
const products: Product[] = [
{
id: 1,
name: "ノートパソコン",
category: "電子機器",
price: 89000,
inStock: true,
},
{ id: 2, name: "マウス", category: "周辺機器", price: 2500, inStock: true },
{
id: 3,
name: "キーボード",
category: "周辺機器",
price: 8500,
inStock: false,
},
{
id: 4,
name: "モニター",
category: "電子機器",
price: 35000,
inStock: true,
},
];
export function ColumnHelperSample() {
const columnHelper = DataTable.createColumnHelper<Product>();
const columns = [
columnHelper.accessor("id", {
header: "ID",
enableSorting: true,
}),
columnHelper.accessor("name", {
header: "商品名",
enableSorting: true,
}),
columnHelper.accessor("category", {
header: "カテゴリ",
enableSorting: true,
}),
columnHelper.accessor("price", {
header: "価格(円)",
enableSorting: true,
}),
columnHelper.accessor("inStock", {
header: "在庫状況",
enableSorting: true,
meta: {
getType: (row: Product) => {
return row.inStock ? "success" : "error";
},
},
}),
];
return <DataTable<Product> data={products} columns={columns} />;
}
ソート機能
列ヘッダーをクリックすることで昇順・降順のソートが可能です。onSortingChangeを使用することでソート状態の変更を監視することが可能です。
地域 | 営業担当 | 売上金額 | 日付 | |
---|---|---|---|---|
東京 | 高橋 | 1200000 | 2025-01-15 | |
大阪 | 伊藤 | 950000 | 2025-01-18 | |
名古屋 | 中村 | 800000 | 2025-01-22 | |
福岡 | 小林 | 1350000 | 2025-01-25 | |
東京 | 松本 | 750000 | 2025-01-28 |
import { DataTable } from "@serendie/ui";
export type Sales = {
id: number;
region: string;
salesperson: string;
amount: number;
date: string;
};
const salesData: Sales[] = [
{
id: 1,
region: "東京",
salesperson: "高橋",
amount: 1200000,
date: "2025-01-15",
},
{
id: 2,
region: "大阪",
salesperson: "伊藤",
amount: 950000,
date: "2025-01-18",
},
{
id: 3,
region: "名古屋",
salesperson: "中村",
amount: 800000,
date: "2025-01-22",
},
{
id: 4,
region: "福岡",
salesperson: "小林",
amount: 1350000,
date: "2025-01-25",
},
{
id: 5,
region: "東京",
salesperson: "松本",
amount: 750000,
date: "2025-01-28",
},
];
export function SortingSample() {
const columnHelper = DataTable.createColumnHelper<Sales>();
const columns = [
columnHelper.accessor("region", {
header: "地域",
enableSorting: true,
sortDescFirst: true,
}),
columnHelper.accessor("salesperson", {
header: "営業担当",
enableSorting: true,
}),
columnHelper.accessor("amount", {
header: "売上金額",
enableSorting: true,
}),
columnHelper.accessor("date", {
header: "日付",
enableSorting: true,
}),
];
return <DataTable<Sales> data={salesData} columns={columns} />;
}
選択機能
enableRowSelectionプロパティで行選択機能の有効・無効を切り替えできます。選択状態の変更はonRowSelectionChangeコールバックで監視できます。
行選択機能あり
タスク名 | ステータス | 担当者 | |
---|---|---|---|
UI設計 | 完了 | 田中 | |
API開発 | 進行中 | 佐藤 | |
テスト作成 | 未着手 | 山田 | |
デプロイ準備 | 未着手 | 鈴木 |
行選択機能なし
タスク名 | ステータス | 担当者 |
---|---|---|
UI設計 | 完了 | 田中 |
API開発 | 進行中 | 佐藤 |
テスト作成 | 未着手 | 山田 |
デプロイ準備 | 未着手 | 鈴木 |
import { DataTable } from "@serendie/ui";
import { css } from "@serendie/ui/css";
export type Task = {
id: number;
title: string;
status: "完了" | "進行中" | "未着手";
assignee: string;
};
const tasks: Task[] = [
{ id: 1, title: "UI設計", status: "完了", assignee: "田中" },
{ id: 2, title: "API開発", status: "進行中", assignee: "佐藤" },
{ id: 3, title: "テスト作成", status: "未着手", assignee: "山田" },
{ id: 4, title: "デプロイ準備", status: "未着手", assignee: "鈴木" },
];
export function SelectionSample() {
const columnHelper = DataTable.createColumnHelper<Task>();
const columns = [
columnHelper.accessor("title", {
header: "タスク名",
enableSorting: true,
}),
columnHelper.accessor("status", {
header: "ステータス",
enableSorting: true,
meta: {
getType: (row: Task) => {
if (row.status === "完了") return "success";
if (row.status === "進行中") return "notice";
return "default";
},
},
}),
columnHelper.accessor("assignee", {
header: "担当者",
enableSorting: true,
}),
];
return (
<div
className={css({
display: "flex",
flexDirection: "column",
gap: "sd.system.dimension.spacing.extraLarge",
})}
>
<div>
<h3
className={css({
marginBottom: "sd.system.dimension.spacing.medium",
})}
>
行選択機能あり
</h3>
<DataTable<Task>
data={tasks}
columns={columns}
enableRowSelection={true}
onRowSelectionChange={(selection) =>
console.log("選択されたタスク:", selection)
}
/>
</div>
<div>
<h3
className={css({
marginBottom: "sd.system.dimension.spacing.medium",
})}
>
行選択機能なし
</h3>
<DataTable<Task>
data={tasks}
columns={columns}
enableRowSelection={false}
/>
</div>
</div>
);
}
イベントハンドリング
onRowSelectionChangeやonSortingChangeなどのコールバック関数を使用して、ユーザーのインタラクションに応答できます。この例では、選択された行に基づいてボタンを有効化し、モーダルダイアログで詳細を表示します。
注文を選択してボタンを押すと、選択された注文の詳細がモーダルで表示されます。
注文ID | 顧客名 | 商品 | 数量 | 合計金額 | ステータス | |
---|---|---|---|---|---|---|
1001 | A | ノートPC | 5 | 450000 | 処理中 | |
1002 | B | プリンター | 2 | 80000 | 配送中 | |
1003 | C | タブレット | 10 | 250000 | 完了 | |
1004 | D | モニター | 3 | 105000 | 処理中 |
選択された注文
注文が選択されていません。
import { Badge, DataTable } from "@serendie/ui";
import { Button } from "@serendie/ui";
import { ModalDialog } from "@serendie/ui";
import { List, ListItem } from "@serendie/ui";
import { css } from "@serendie/ui/css";
import { useState } from "react";
import { SerendieSymbol } from "@serendie/symbols";
type RowSelectionState = Record<string, boolean>;
type OnRowSelectionChange = (
updaterOrValue:
| RowSelectionState
| ((old: RowSelectionState) => RowSelectionState)
) => void;
export type Order = {
id: number;
customer: string;
product: string;
quantity: number;
total: number;
status: "処理中" | "配送中" | "完了";
};
const orders: Order[] = [
{
id: 1001,
customer: "A",
product: "ノートPC",
quantity: 5,
total: 450000,
status: "処理中",
},
{
id: 1002,
customer: "B",
product: "プリンター",
quantity: 2,
total: 80000,
status: "配送中",
},
{
id: 1003,
customer: "C",
product: "タブレット",
quantity: 10,
total: 250000,
status: "完了",
},
{
id: 1004,
customer: "D",
product: "モニター",
quantity: 3,
total: 105000,
status: "処理中",
},
];
export function CallbacksSample() {
const [selectedRows, setSelectedRows] = useState<RowSelectionState>({});
const [isModalOpen, setIsModalOpen] = useState(false);
const columnHelper = DataTable.createColumnHelper<Order>();
const columns = [
columnHelper.accessor("id", {
header: "注文ID",
enableSorting: true,
}),
columnHelper.accessor("customer", {
header: "顧客名",
enableSorting: true,
}),
columnHelper.accessor("product", {
header: "商品",
enableSorting: true,
}),
columnHelper.accessor("quantity", {
header: "数量",
enableSorting: true,
}),
columnHelper.accessor("total", {
header: "合計金額",
enableSorting: true,
}),
columnHelper.accessor("status", {
header: "ステータス",
enableSorting: true,
meta: {
getType: (row: Order) => {
if (row.status === "完了") return "success";
if (row.status === "配送中") return "notice";
return "default";
},
},
}),
];
const handleRowSelectionChange: OnRowSelectionChange = (updaterOrValue) => {
const selection =
typeof updaterOrValue === "function"
? updaterOrValue(selectedRows)
: updaterOrValue;
console.log("選択された注文:", selection);
setSelectedRows(selection);
};
const selectedOrderIds = Object.keys(selectedRows).filter(
(key) => selectedRows[key]
);
const selectedOrders = orders.filter((order) =>
selectedOrderIds.includes(order.id.toString())
);
const handleShowModal = () => {
if (selectedOrderIds.length > 0) {
setIsModalOpen(true);
}
};
const handleCloseModal = () => {
setIsModalOpen(false);
};
return (
<div>
<div
className={css({ marginBottom: "sd.system.dimension.spacing.medium" })}
>
<p
className={css({
marginBottom: "sd.system.dimension.spacing.small",
color: "sd.system.color.component.onSurface",
})}
>
注文を選択してボタンを押すと、選択された注文の詳細がモーダルで表示されます。
</p>
<Button
onClick={handleShowModal}
disabled={selectedOrderIds.length === 0}
>
選択した注文を確認 ({selectedOrderIds.length}件)
</Button>
</div>
<DataTable<Order>
data={orders}
columns={columns}
enableRowSelection={true}
getRowId={(row) => row.id.toString()}
onRowSelectionChange={handleRowSelectionChange}
state={{
rowSelection: selectedRows,
}}
initialState={{
sorting: [{ id: "total", desc: true }],
}}
/>
<ModalDialog
isOpen={isModalOpen}
title="選択された注文"
submitButtonLabel="完了"
onButtonClick={handleCloseModal}
onOpenChange={(details) => {
if (!details.open) {
setIsModalOpen(false);
}
}}
>
{selectedOrders.length > 0 ? (
<div>
<p
className={css({
marginBottom: "sd.system.dimension.spacing.medium",
})}
>
{selectedOrders.length}件の注文が選択されています:
</p>
<List>
{selectedOrders.map((order) => (
<ListItem
key={order.id}
title={`注文ID: ${order.id}`}
description={`顧客: ${order.customer} | 商品: ${order.product}`}
leftIcon={<SerendieSymbol name="package" />}
isLargeLeftIcon
>
<div
className={css({
display: "flex",
justifyContent: "space-between",
alignItems: "center",
gap: "sd.system.dimension.spacing.small",
})}
>
<Badge
size="small"
styleColor={
order.status === "完了"
? "green"
: order.status === "配送中"
? "yellow"
: "gray"
}
>
{order.status}
</Badge>
<span>
{`数量: ${order.quantity}個 | ¥${order.total.toLocaleString()}`}
</span>
</div>
</ListItem>
))}
</List>
</div>
) : (
<p>注文が選択されていません。</p>
)}
</ModalDialog>
</div>
);
}
異なるデータ型への対応
ColumnHelperは型パラメータを受け取るため、異なるデータ型でも型安全にテーブルを作成できます。meta.getTypeで動的なスタイリングも可能です。
ID | 名前 | メールアドレス | 役割 | アクティブ | 最終ログイン | |
---|---|---|---|---|---|---|
1 | 田中太郎 | [email protected] | admin | true | 2025-07-10 09:30 | |
2 | 佐藤花子 | [email protected] | user | true | 2025-07-10 14:15 | |
3 | 山田次郎 | [email protected] | guest | false | 2025-07-08 16:45 | |
4 | 鈴木美穂 | [email protected] | user | true | 2025-07-10 11:20 |
import { DataTable } from "@serendie/ui";
export type User = {
id: number;
name: string;
email: string;
role: "admin" | "user" | "guest";
isActive: boolean;
lastLogin: string;
};
const users: User[] = [
{
id: 1,
name: "田中太郎",
email: "[email protected]",
role: "admin",
isActive: true,
lastLogin: "2025-07-10 09:30",
},
{
id: 2,
name: "佐藤花子",
email: "[email protected]",
role: "user",
isActive: true,
lastLogin: "2025-07-10 14:15",
},
{
id: 3,
name: "山田次郎",
email: "[email protected]",
role: "guest",
isActive: false,
lastLogin: "2025-07-08 16:45",
},
{
id: 4,
name: "鈴木美穂",
email: "[email protected]",
role: "user",
isActive: true,
lastLogin: "2025-07-10 11:20",
},
];
export function DataTypeSample() {
const userColumnHelper = DataTable.createColumnHelper<User>();
const columns = [
userColumnHelper.accessor("id", {
header: "ID",
enableSorting: true,
}),
userColumnHelper.accessor("name", {
header: "名前",
enableSorting: true,
}),
userColumnHelper.accessor("email", {
header: "メールアドレス",
enableSorting: true,
}),
userColumnHelper.accessor("role", {
header: "役割",
enableSorting: true,
meta: {
getType: (row: User) => {
if (row.role === "admin") return "success";
if (row.role === "guest") return "notice";
return "default";
},
},
}),
userColumnHelper.accessor("isActive", {
header: "アクティブ",
enableSorting: true,
meta: {
getType: (row: User) => {
return row.isActive ? "success" : "error";
},
},
}),
userColumnHelper.accessor("lastLogin", {
header: "最終ログイン",
enableSorting: true,
}),
];
return (
<DataTable<User>
data={users}
columns={columns}
enableRowSelection={true}
enableSorting={true}
/>
);
}