MUI DataGridとdnd-kitでドラッグできるテーブルを作成する
はじめに
ここでは、私が個人開発をしていた時にちょっと苦労したDataGridをドラッグできるようにするテーブルを紹介します。
UIフレームワークであるMUIにある多機能テーブルDataGridと、ドラッグアンドドロップ機能を追加できるライブラリdnd-kitを使って、ドラッグで順番の入れ替えができるテーブルを作成します。
開発環境
開発環境は以下の通りです。
- react: 18.2.0
- @emotion/react: 11.10.6
- @emotion/styled: 11.10.6
- @mui/material: 5.11.15
- @mui/x-data-grid: 6.0.3
- @dnd-kit/core: 6.0.8
- @dnd-kit/modifiers: 6.0.1
- @dnd-kit/sortable: 7.0.2
- @dnd-kit/utilities: 3.2.1
作成するもの
作成するテーブルはこちら。
サンプルをCodeSandboxに作成済みです。
テーブルの行をドラッグしてみてください。
入れ替えができるはずです。
コーディング
それでは作っていきましょう。
DataGridでテーブルを作成する
まずは基本となるテーブルをDataGridで作っていきます。
DataGridでは様々な機能を持ったテーブルが作成できるため、ご自身の目的に合ったテーブルを作成してください。
ここではDataGridそのものの解説は行わない為、DataGridについて知りたい方は、公式ドキュメントや他記事を参考にしてください。
公式ドキュメント
Zennの他記事
テーブルを作成します。
import { DataGrid, GridColDef, GridValueGetterParams } from "@mui/x-data-grid";
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 90 },
{
field: "firstName",
headerName: "First name",
width: 150,
editable: true
},
{
field: "lastName",
headerName: "Last name",
width: 150,
editable: true
},
{
field: "age",
headerName: "Age",
type: "number",
width: 110,
editable: true
},
{
field: "fullName",
headerName: "Full name",
description: "This column has a value getter and is not sortable.",
sortable: false,
width: 160,
valueGetter: (params: GridValueGetterParams) =>
`${params.row.firstName || ""} ${params.row.lastName || ""}`
}
];
const initialRows = [
{ id: 1, lastName: "Snow", firstName: "Jon", age: 35 },
{ id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
{ id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
{ id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
{ id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null },
{ id: 6, lastName: "Melisandre", firstName: null, age: 150 },
{ id: 7, lastName: "Clifford", firstName: "Ferrara", age: 44 },
{ id: 8, lastName: "Frances", firstName: "Rossini", age: 36 },
{ id: 9, lastName: "Roxie", firstName: "Harvey", age: 65 }
];
const SampleTable = () => {
return (
<DataGrid
rows={rows}
columns={columns}
autoHeight
checkboxSelection
disableRowSelectionOnClick
/>
)
}
ドラッグアンドドロップの適用範囲を設定する
dnd-kitの<DndContext>
タグと<SortableContext>
タグでテーブルを囲み、ドラッグアンドドロップの適用範囲を設定します。
ここではdnd-kitそのものの解説は行わない為、dnd-kitについて知りたい方は、公式ドキュメントや他記事を参考にしてください。
公式ドキュメント
Zennの他記事
<DndContext>
タグと<SortableContext>
タグを追加します。
また、それぞれに必要なimport
と処理も追加します。
import { DataGrid, GridColDef, GridValueGetterParams } from "@mui/x-data-grid";
+ import {
+ DndContext,
+ closestCenter,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ DragEndEvent,
+ } from '@dnd-kit/core';
+ import {
+ arrayMove,
+ SortableContext,
+ verticalListSortingStrategy,
+ } from '@dnd-kit/sortable';
// データは省略
const SampleTable = () => {
+ // ドラッグ可能なセンサーを設定
+ const sensors = useSensors(useSensor(PointerSensor));
+
+ // ドラッグ終了時の処理
+ const handleDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+ if (over) {
+ const oldIndex = rows.findIndex((rows) => rows.id === active.id);
+ const newIndex = rows.findIndex((rows) => rows.id === over.id);
+ onDragChange(arrayMove(rows, oldIndex, newIndex));
+ }
+ };
return (
+ <DndContext
+ sensors={sensors}
+ collisionDetection={closestCenter}
+ onDragEnd={handleDragEnd}
+ autoScroll={false}
+ >
+ <SortableContext
+ items={rows.map((row) => row.id)}
+ strategy={verticalListSortingStrategy}
+ >
<DataGrid
rows={rows}
columns={columns}
autoHeight
checkboxSelection
disableRowSelectionOnClick
/>
+ </SortableContext>
+ </DndContext>
)
}
ドラッグできる要素を設定する
どの要素をドラッグできるようにするか、dnd-kitのuseSortable
を使って設定します。
テーブルのデータを入れ替えるためには、テーブルの行を入れ替え可能にする必要があるので、行に設定をしていきます。
DataGridでは、行コンポーネントである<GridRow>
をオーバーライドすることで、実現できます。
<DataGrid>
コンポーネントのcomponents
プロパティを使用することで、<DataGrid>
内部のコンポーネントをオーバーライドすることができます。
import {
DataGrid,
GridColDef,
GridValueGetterParams,
+ GridRow,
+ GridRowProps
} from "@mui/x-data-grid";
import {
DndContext,
closestCenter,
PointerSensor,
useSensor,
useSensors,
DragEndEvent,
} from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
verticalListSortingStrategy,
+ useSortable,
} from '@dnd-kit/sortable';
+ import { CSS } from '@dnd-kit/utilities';
// データは省略
+ // ドラッグできる行コンポーネント
+ const DraggableGridRow = memo((params: GridRowProps) => {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging
+ } = useSortable({ id: params.rowId });
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition
+ };
+
+ return (
+ <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
+ <GridRow {...params} />
+ </div>
+ );
+ });
const SampleTable = () => {
// ドラッグ可能なセンサーを設定
const sensors = useSensors(useSensor(PointerSensor));
// ドラッグ終了時の処理
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
if (over) {
const oldIndex = rows.findIndex((rows) => rows.id === active.id);
const newIndex = rows.findIndex((rows) => rows.id === over.id);
onDragChange(arrayMove(rows, oldIndex, newIndex));
}
};
return (
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
autoScroll={false}
>
<SortableContext
items={rows.map((row) => row.id)}
strategy={verticalListSortingStrategy}
>
<DataGrid
rows={rows}
columns={columns}
+ components={{ Row: DraggableGridRow }}
autoHeight
checkboxSelection
disableRowSelectionOnClick
/>
</SortableContext>
</DndContext>
)
}
これにて完成です。
最初にお見せしたサンプルは、コンポーネントを分割しているので全く同じコードではありませんが、機能では同じものになります。
オーバーライドするための行を独自に作成したい場合は、Githubの<DataGrid>
コンポーネントのコードを読みつつやってみましょう。
Discussion