inertia.js + reactでdraggable (react-beautiful-dnd)
react-beautiful-dnd を使う例
事前準備
artisan make:model Draggable -mrsf
とりあえずtitleだけあればいいとしよう
Schema::create('draggables', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->integer('sort_order')->default(0);
$table->timestamps();
});
ただし↑のようにsort_orderで並び順を定義する
class DraggableFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'title' => fake()->company,
];
}
}
\App\Models\Draggable::factory(5)->create();
これで
= Illuminate\Database\Eloquent\Collection {#7618
all: [
App\Models\Draggable {#7620
id: 1,
title: "有限会社 工藤",
sort_order: 0,
created_at: "2023-12-31 02:07:56",
updated_at: "2023-12-31 02:07:56",
},
App\Models\Draggable {#7621
id: 2,
title: "株式会社 山本",
sort_order: 0,
created_at: "2023-12-31 02:07:56",
updated_at: "2023-12-31 02:07:56",
},
App\Models\Draggable {#7622
id: 3,
title: "有限会社 木村",
sort_order: 0,
created_at: "2023-12-31 02:07:56",
updated_at: "2023-12-31 02:07:56",
},
App\Models\Draggable {#7623
id: 4,
title: "株式会社 加藤",
sort_order: 0,
created_at: "2023-12-31 02:07:56",
updated_at: "2023-12-31 02:07:56",
},
App\Models\Draggable {#7624
id: 5,
title: "有限会社 山口",
sort_order: 0,
created_at: "2023-12-31 02:07:56",
updated_at: "2023-12-31 02:07:56",
},
]
まあこんな具合に5つエントリが出来た。
routes/web.phpには適当にresouceを振っとく
Route::resource('draggables', DraggableController::class);
draggable.index
public function index()
{
$draggables = Draggable::all();
return Inertia::render('Draggables/Index', [
'draggables' => $draggables,
]);
}
resources/js/Pages/Draggables/Index.jsx
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import { Head } from '@inertiajs/react'
export default function DraggableIndex({ auth, draggables }) {
return (
<AuthenticatedLayout
user={auth.user}
header={
<h2 className="font-semibold text-xl text-gray-800 leading-tight">
Draggable
</h2>
}
>
<Head title="Draggable" />
<div className="py-12">
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6 text-gray-900">
<ul className="space-y-2">
{draggables.map((draggable, index) => (
<li
key={index}
className="px-4 py-2 border rounded-md hover:bg-gray-100"
>
{draggable.title}
</li>
))}
</ul>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
)
}
これを動かしていく
install
npm install react-beautiful-dnd
したら
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import { Head } from '@inertiajs/react'
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
こんな感じでimportできる。
コード
詳しい説明は一気に割愛するが、とりあえず動くものを貼り付けておく。これは今後もう少しこれを使って何か作る事があった場合は逐次解説する
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head } from '@inertiajs/react';
import { router } from '@inertiajs/react';
export default function DraggableIndex({ auth, draggables }) {
// ドラッグアンドドロップの状態を管理するためのローカルステート
const [items, setItems] = useState(draggables);
// ドラッグが終了したときのハンドラー
const onDragEnd = (result) => {
if (!result.destination) return;
const newItems = Array.from(items);
const [reorderedItem] = newItems.splice(result.source.index, 1);
newItems.splice(result.destination.index, 0, reorderedItem);
setItems(newItems);
router.post(route('draggables.store'), { items: newItems })
};
return (
<AuthenticatedLayout
user={auth.user}
header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Draggable</h2>}
>
<Head title="Draggable" />
<div className="py-12">
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6 bg-white border-b border-gray-200">
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<ul className="space-y-2" {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id.toString()} index={index}>
{(provided) => (
<li
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
className="px-4 py-2 border rounded-md hover:bg-gray-100"
>
{item.title}
</li>
)}
</Draggable>
))}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}
backendの処理
そうすると
router.post(route('draggables.store'), { items: newItems })
により、storeに転送される。まあstoreっていうのは流石にちょっとアレな名前なので本来はreorderとかそういう名前にしておいた方がいいんだろうけど、これをdumpすると
public function store(Request $request)
{
dd($request->all());
}
このように番号が振り直されているものが来るので、あとはbackendでsort_orderをupdateしてあげればよい。
class Draggable extends Model
{
use HasFactory;
protected $fillable = [
'sort_order'
];
}
public function store(Request $request): RedirectResponse
{
$draggables = $request->items;
foreach ($draggables as $index => $item) {
Draggable::where('id', $item['id'])
->update(['sort_order' => $index]);
}
return redirect(route('draggables.index'));
}
とか
最終的に
indexもsort_orderの値を見る事
public function index()
{
// $draggables = Draggable::all();
$draggables = Draggable::orderBy('sort_order')->get();
return Inertia::render('Draggables/Index', [
'draggables' => $draggables,
]);
}
この記事が気に入ったらサポートをしてみませんか?