import {useEffect, useMemo, useState} from 'react'; import type {DragEndEvent} from '@dnd-kit/core'; import {arrayMove} from '@dnd-kit/sortable'; export type Column = { column: string | undefined; id: number; }; interface UseDragAndDropColumnsProps { columns: string[]; setColumns: (columns: string[]) => void; } const extractColumns = (editableColumns: Column[]) => { return editableColumns.map(({column}) => column ?? ''); }; export function useDragNDropColumns({columns, setColumns}: UseDragAndDropColumnsProps) { const mappedColumns = useMemo(() => { return columns.map((column, i) => ({id: i + 1, column})); }, [columns]); const [editableColumns, setEditableColumns] = useState(mappedColumns); useEffect(() => { setEditableColumns(prevEditableColumns => { // Only update if there's a change between columns and editableColumns if ( JSON.stringify(prevEditableColumns.map(c => c.column)) !== JSON.stringify(columns) ) { return mappedColumns; } return prevEditableColumns; }); }, [columns, mappedColumns]); const [nextId, setNextId] = useState(editableColumns.length + 1); function insertColumn() { setEditableColumns(oldEditableColumns => { const newEditableColumns = oldEditableColumns.slice(); newEditableColumns.push({id: nextId, column: undefined}); setNextId(nextId + 1); // make sure to increment the id for the next time setColumns(extractColumns(newEditableColumns)); return newEditableColumns; }); } function updateColumnAtIndex(i: number, column: string) { setEditableColumns(oldEditableColumns => { const newEditableColumns = [...oldEditableColumns]; newEditableColumns[i].column = column; setColumns(extractColumns(newEditableColumns)); return newEditableColumns; }); } function deleteColumnAtIndex(i: number) { setEditableColumns(oldEditableColumns => { if (oldEditableColumns.length === 1) { setColumns(['']); return [{id: 1, column: undefined}]; } const newEditableColumns = [ ...oldEditableColumns.slice(0, i), ...oldEditableColumns.slice(i + 1), ]; setColumns(extractColumns(newEditableColumns)); return newEditableColumns; }); } function onDragEnd(event: DragEndEvent) { const {active, over} = event; if (active.id !== over?.id) { const oldIndex = editableColumns.findIndex(({id}) => id === active.id); const newIndex = editableColumns.findIndex(({id}) => id === over?.id); setEditableColumns(oldEditableColumns => { const newEditableColumns = arrayMove(oldEditableColumns, oldIndex, newIndex); setColumns(extractColumns(newEditableColumns)); return newEditableColumns; }); } } return { editableColumns, insertColumn, updateColumnAtIndex, deleteColumnAtIndex, onDragEnd, }; }