import { ref } from 'vue' interface DragReorderHandlers { draggingIndex: Ref dropTargetIndex: Ref onDragStart: (index: number, event: DragEvent) => void onDragEnter: (index: number) => void onDragOver: (event: DragEvent) => void onDrop: (index: number) => void onDragEnd: () => void reorderClass: (index: number) => string reset: () => void } interface DragReorderOptions { draggingClass?: string dropTargetClass?: string onReorder?: () => void } function moveItemInPlace(list: T[], from: number, to: number): void { if (from === to) return if (from < 0 || to < 0 || from >= list.length || to >= list.length) return const updated = list.slice() const [item] = updated.splice(from, 1) if (item === undefined) return updated.splice(to, 0, item) list.splice(0, list.length, ...updated) } export function useDragReorder( getList: () => unknown[] | undefined, options: DragReorderOptions = {}, ): DragReorderHandlers { const { draggingClass = 'border-dashed border-primary', dropTargetClass = 'border-primary border-dashed bg-primary/5', onReorder, } = options const draggingIndex = ref(null) const dropTargetIndex = ref(null) const reset = () => { draggingIndex.value = null dropTargetIndex.value = null } const onDragStart = (index: number, event: DragEvent) => { draggingIndex.value = index dropTargetIndex.value = index if (event.dataTransfer) { event.dataTransfer.effectAllowed = 'move' } } const onDragEnter = (index: number) => { if (draggingIndex.value === null) return dropTargetIndex.value = index } const onDragOver = (event: DragEvent) => { event.preventDefault() } const onDrop = (index: number) => { const list = getList() if (!Array.isArray(list)) { reset() return } const from = draggingIndex.value if (from === null) { reset() return } moveItemInPlace(list, from, index) onReorder?.() reset() } const onDragEnd = () => { reset() } const reorderClass = (index: number): string => { if (draggingIndex.value === index) return draggingClass if ( draggingIndex.value !== null && dropTargetIndex.value === index && draggingIndex.value !== index ) { return dropTargetClass } return '' } return { draggingIndex, dropTargetIndex, onDragStart, onDragEnter, onDragOver, onDrop, onDragEnd, reorderClass, reset, } }