カンバンボード
:LiTarget: 用途
D&D対応のカンバンボードUI。列追加・並び替え可能。
:LiSparkle: 特徴
- D&D並び替え
- 列追加
- カードカスタム
- 検索
:LiCode: コード(コピペ用)
'use client';
import { useState } from 'react';
type Card = { id: string; title: string; [k: string]: any };
type Column = { id: string; title: string; cards: Card[] };
export function KanbanBoard({
initial, onMove,
}: {
initial: Column[];
onMove?: (cardId: string, fromCol: string, toCol: string) => void;
}) {
const [cols, setCols] = useState<Column[]>(initial);
const [dragging, setDragging] = useState<{ cardId: string; fromCol: string } | null>(null);
const handleDrop = (toCol: string) => {
if (!dragging || dragging.fromCol === toCol) return;
setCols((cs) => {
const card = cs.find((c) => c.id === dragging.fromCol)?.cards.find((c) => c.id === dragging.cardId);
if (!card) return cs;
return cs.map((c) => {
if (c.id === dragging.fromCol) return { ...c, cards: c.cards.filter((x) => x.id !== dragging.cardId) };
if (c.id === toCol) return { ...c, cards: [...c.cards, card] };
return c;
});
});
onMove?.(dragging.cardId, dragging.fromCol, toCol);
setDragging(null);
};
return (
<div className="flex gap-4 overflow-x-auto pb-4">
{cols.map((col) => (
<div key={col.id}
className="bg-zinc-900/50 border border-zinc-800 rounded-2xl p-3 min-w-[280px] w-[280px]"
onDragOver={(e) => e.preventDefault()}
onDrop={() => handleDrop(col.id)}>
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-semibold text-zinc-200">{col.title}</h3>
<span className="text-xs text-zinc-500">{col.cards.length}</span>
</div>
<div className="space-y-2 min-h-[200px]">
{col.cards.map((card) => (
<div key={card.id}
draggable
onDragStart={() => setDragging({ cardId: card.id, fromCol: col.id })}
className="bg-zinc-800 rounded-xl p-3 cursor-move hover:bg-zinc-700 transition-colors">
<div className="text-sm text-zinc-100">{card.title}</div>
</div>
))}
</div>
</div>
))}
</div>
);
}
:LiHandPointer: 使い方
対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。
:LiAlertCircle: 注意事項
- 依存パッケージを忘れず追加