93 lines
2.4 KiB
TypeScript
93 lines
2.4 KiB
TypeScript
import { useState } from "react";
|
|
import {
|
|
DndContext,
|
|
closestCenter,
|
|
KeyboardSensor,
|
|
PointerSensor,
|
|
useSensor,
|
|
useSensors,
|
|
type DragStartEvent,
|
|
type DragEndEvent,
|
|
} from "@dnd-kit/core";
|
|
import { arrayMove, sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
|
|
|
export interface DragAndDropItem {
|
|
[key: string]: any;
|
|
}
|
|
|
|
export interface UseDragAndDropProps<T extends DragAndDropItem> {
|
|
items: T[];
|
|
onReorder: (reorderedItems: Array<{ id: string; sortOrder: number }>) => Promise<void>;
|
|
getItemId: (item: T) => string;
|
|
}
|
|
|
|
export function useDragAndDrop<T extends DragAndDropItem>({ items, onReorder, getItemId }: UseDragAndDropProps<T>) {
|
|
const [activeId, setActiveId] = useState<string | null>(null);
|
|
|
|
// 드래그 센서 설정
|
|
const sensors = useSensors(
|
|
useSensor(PointerSensor, {
|
|
activationConstraint: {
|
|
distance: 8, // 8px 이동 후 드래그 시작
|
|
},
|
|
}),
|
|
useSensor(KeyboardSensor, {
|
|
coordinateGetter: sortableKeyboardCoordinates,
|
|
}),
|
|
);
|
|
|
|
// 드래그 시작 핸들러
|
|
const handleDragStart = (event: DragStartEvent) => {
|
|
setActiveId(event.active.id as string);
|
|
};
|
|
|
|
// 드래그 종료 핸들러
|
|
const handleDragEnd = async (event: DragEndEvent) => {
|
|
setActiveId(null);
|
|
const { active, over } = event;
|
|
|
|
if (over && active.id !== over.id) {
|
|
const oldIndex = items.findIndex((item) => getItemId(item) === active.id);
|
|
const newIndex = items.findIndex((item) => getItemId(item) === over.id);
|
|
|
|
if (oldIndex !== -1 && newIndex !== -1) {
|
|
const newOrder = arrayMove(items, oldIndex, newIndex);
|
|
|
|
// 순서 업데이트를 위한 데이터 준비
|
|
const reorderedItems = newOrder.map((item, index) => ({
|
|
id: getItemId(item),
|
|
sortOrder: index + 1,
|
|
}));
|
|
|
|
try {
|
|
await onReorder(reorderedItems);
|
|
} catch (error) {
|
|
console.error("순서 변경 실패:", error);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// 현재 드래그 중인 아이템 찾기
|
|
const activeItem = activeId ? items.find((item) => getItemId(item) === activeId) : null;
|
|
|
|
return {
|
|
// 상태
|
|
activeId,
|
|
activeItem,
|
|
|
|
// 센서 및 핸들러
|
|
sensors,
|
|
handleDragStart,
|
|
handleDragEnd,
|
|
|
|
// DndContext props
|
|
dndContextProps: {
|
|
sensors,
|
|
collisionDetection: closestCenter,
|
|
onDragStart: handleDragStart,
|
|
onDragEnd: handleDragEnd,
|
|
},
|
|
};
|
|
}
|