DataTable
ソート・フィルタ・検索・ページネーション対応のデータテーブル。
インポート
個別サブパス(バンドルサイズを抑えたい場合):
基本的な使い方
検索・ソート付き
ヘッダーをクリックしてソート、検索ボックスでフィルタリングできます。
フィルター付き
フィルターボタンで条件付きフィルタリングができます。
ページネーション付き
行アクション付き
各行にアクションボタンを追加できます。
カスタムアクション付き
検索ボックスの横にカスタムボタンを追加できます。
空状態
データがない場合のカスタム表示。
ローディング状態
行選択(チェックボックス)とonRowClickの併用
rowKey + selectedKeys + onSelectionChange を指定するとチェックボックス列が表示され、行選択に対応します。行クリックの挙動はonRowClickの有無で変わります。
| 状態 | 行本体のクリック | チェックボックス |
|---|---|---|
selectableのみ(onRowClick無し) | 選択をトグル | 選択をトグル |
selectable + onRowClickあり | onRowClickのみを呼び出す(選択はトグルしない) | 選択をトグル(行クリックへ伝播しない) |
onRowClickのみ(selectable無し) | onRowClickのみを呼び出す | - |
| どちらも無し | 何も起きない(cursor-pointerも付与されない) | - |
selectable + onRowClickを併用する場合、チェックボックスのクリックは行クリックへ伝播せず、互いに干渉しません。行が選択・クリック操作可能なとき(selectableまたはonRowClickのいずれか)はキーボード操作(Tab移動 + Enter/Space)にも対応します。選択中の行にはdata-state="selected"が付与され、Tableの標準スタイル(data-[state=selected]:bg-muted)で表示されます。
カラム幅指定
DataTableColumn の width / minWidth / maxWidth でカラムごとの幅を指定できます。widthは既存のプリセット('xs'|'sm'|'md'|'lg'|'xl'|'auto')に加え、number(px)や任意のCSS幅文字列('40%'等)も指定できます。いずれか1つでも指定した列があるとcolgroupを自動レンダリングします(何も指定しない場合は従来どおり)。
列幅のドラッグリサイズ(resizableColumns)
resizableColumns を指定すると、ヘッダー境界にドラッグ用ハンドルが表示され、列幅を変更できます。キーボード操作(フォーカス後に左右矢印キー、16pxずつ)にも対応しています。幅は内部で管理される非制御実装で、onColumnWidthsChange で変更後の幅(カラムID→px)を受け取れます。minWidth(デフォルト60px)/ maxWidth で下限・上限を指定できます。
ドラッグリサイズが不要な静的な幅指定のみでよい場合は、resizableColumnsを省略しwidth/minWidth/maxWidthだけを指定してください。
サーバー駆動のフィルタ・ソート(manualFiltering / manualSorting)
サーバーAPI側で検索・フィルタ・ソートを適用したdataを渡す場合、DataTableはデフォルトでもローカル側に同じ処理を再適用してしまいます(二重適用)。manualFiltering / manualSortingを指定すると、該当するローカル処理をスキップし、渡されたdataをそのまま表示します。検索欄・フィルターUI・ヘッダークリックのUI自体は変わらず動作し、onSearchChange / onFiltersChange / onSortChangeでサーバーへリクエストを送る使い方を想定しています。
Props
DataTable
| Prop | Type | Default | Description |
|---|---|---|---|
columns* | DataTableColumn[] | - | カラム定義の配列 |
data* | T[] | - | テーブルに表示するデータ |
filterItems | FilterItem[] | - | フィルター項目の配列 |
onFiltersChange | (filters: ActiveFilter[]) => void | - | フィルター変更時のコールバック |
searchPlaceholder | string | '検索...' | 検索ボックスのプレースホルダー |
onSearchChange | (search: string) => void | - | 検索文字列変更時のコールバック |
hideSearch | boolean | false | 検索ボックスを非表示にする |
pagination | PaginationInfo | - | ページネーション設定 |
onPageChange | (page: number) => void | - | ページ変更時のコールバック |
loading | boolean | false | ローディング状態 |
emptyState | { icon, title, description, action } | - | データがない場合の表示設定 |
customActions | ReactNode | - | 検索ボックス横のカスタムアクション |
onRowClick | (item: T) => void | - | 行クリック時のコールバック |
rowActions | (item: T) => ReactNode | - | 各行のアクションレンダラー |
resizableColumns | boolean | false | ヘッダー境界のドラッグ(またはフォーカス後の左右矢印キー)で列幅を変更できるようにする |
onColumnWidthsChange | (widths: Record<string, number>) => void | - | 列幅が変化した際に、カラムID→px(number)のレコードを通知するコールバック(非制御。保存したい利用側向け) |
manualFiltering | boolean | false | サーバー側で検索・カスタムフィルターを適用済みの場合、ローカルでの再適用をスキップする(二重適用防止) |
manualSorting | boolean | false | サーバー側でソートを適用済みの場合、ローカルでの再適用をスキップする(二重適用防止) |
className | string | - | 追加のクラス名 |
DataTableColumn
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | - | カラムの一意な識別子 |
header* | string | - | ヘッダーに表示するテキスト |
accessorKey | keyof T | - | データオブジェクトのキー |
cell | (data: T) => ReactNode | - | カスタムセルレンダラー |
width | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'auto' | number | string | - | カラムの幅。プリセットに加え、number(px)や任意のCSS幅文字列(例: '40%')を指定できる |
minWidth | number | string | 60(resizableColumns使用時) | カラムの最小幅(px数値、または px文字列)。resizableColumnsでのリサイズ下限としても使われる |
maxWidth | number | string | - | カラムの最大幅(px数値、または px文字列)。resizableColumnsでのリサイズ上限としても使われる |
sortable | boolean | false | ソート可能にする |
align | 'left' | 'center' | 'right' | 'left' | テキストの配置 |
FilterItem
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | - | フィルターの一意な識別子 |
label* | string | - | フィルターのラベル |
field* | keyof T | - | フィルター対象のフィールド |
type | 'text' | 'email' | 'phone' | 'url' | 'number' | 'date' | - | フィルタータイプ |
icon | ReactNode | - | カスタムアイコン |
conditions | Array<{ label, condition }> | - | フィルター条件の配列 |
PaginationInfo
| Prop | Type | Default | Description |
|---|---|---|---|
page* | number | - | 現在のページ番号(1始まり) |
limit* | number | - | 1ページあたりの件数 |
total* | number | - | 総件数 |
totalPages* | number | - | 総ページ数 |