Untitled
unknown
plain_text
6 months ago
6.3 kB
3
Indexable
import React, { useState, ChangeEvent } from 'react'; import { useDrag, useDrop, DragSourceMonitor } from 'react-dnd'; const ItemType = 'TABLE_ITEM'; type DraggableTableProps = { columns: string[]; rows: string[][]; onRowChange: (updatedRows: string[][]) => void; onColumnChange: (updatedColumns: string[]) => void; onNewRow: (newRow: string[]) => void; enableDragAndDrop: boolean; }; type DraggableCellProps = { children: React.ReactNode; columnIndex: number; rowIndex: number; moveRow: (fromIndex: number, toIndex: number, columnIndex: number) => void; moveColumn: (fromRowIndex: number, fromColumnIndex: number, toRowIndex: number, toColumnIndex: number) => void; enableDragAndDrop: boolean; }; type DraggableHeaderProps = { children: React.ReactNode; columnIndex: number; moveColumn: (fromRowIndex: number, fromColumnIndex: number, toRowIndex: number, toColumnIndex: number) => void; enableDragAndDrop: boolean; }; type DragItem = { rowIndex: number; columnIndex: number; isRow: boolean; }; // Draggable Cell Component const DraggableCell: React.FC<DraggableCellProps> = ({ children, columnIndex, rowIndex, moveRow, moveColumn, enableDragAndDrop, }) => { if (!enableDragAndDrop) { return <td>{children}</td>; } const [{ isDragging }, drag] = useDrag({ type: ItemType, item: { rowIndex, columnIndex, isRow: false }, collect: (monitor: DragSourceMonitor) => ({ isDragging: monitor.isDragging(), }), }); const [, drop] = useDrop({ accept: ItemType, hover: (item: DragItem) => { if (item.isRow) { moveRow(item.rowIndex, rowIndex, columnIndex); } else { moveColumn(item.rowIndex, item.columnIndex, rowIndex, columnIndex); } }, }); return ( <td ref={(node) => drag(drop(node))} style={{ opacity: isDragging ? 0.5 : 1 }} className="draggable-cell"> {children} </td> ); }; // Draggable Header Component const DraggableHeader: React.FC<DraggableHeaderProps> = ({ children, columnIndex, moveColumn, enableDragAndDrop, }) => { if (!enableDragAndDrop) { return <th>{children}</th>; } const [, drag] = useDrag({ type: ItemType, item: { rowIndex: -1, columnIndex, isRow: true }, }); return ( <th ref={drag} className="draggable-header"> {children} </th> ); }; // Reusable Table Component const DraggableTable: React.FC<DraggableTableProps> = ({ columns, rows, onRowChange, onColumnChange, onNewRow, enableDragAndDrop, }) => { const [currentColumns, setColumns] = useState<string[]>(columns); const [currentRows, setRows] = useState<string[][]>(rows); const [newRow, setNewRow] = useState<string[]>(new Array(columns.length).fill('')); // Move a row to a new position const moveRow = (fromIndex: number, toIndex: number, columnIndex: number) => { const updatedRows = [...currentRows]; const [movedRow] = updatedRows.splice(fromIndex, 1); updatedRows.splice(toIndex, 0, movedRow); setRows(updatedRows); onRowChange(updatedRows); // Notify parent of row change }; // Move a column to a new position const moveColumn = ( fromRowIndex: number, fromColumnIndex: number, toRowIndex: number, toColumnIndex: number ) => { const updatedColumns = [...currentColumns]; const [movedColumn] = updatedColumns.splice(fromColumnIndex, 1); updatedColumns.splice(toColumnIndex, 0, movedColumn); setColumns(updatedColumns); // Reorder the rows based on the moved column const updatedRows = [...currentRows]; updatedRows.forEach((row) => { const movedCell = row.splice(fromColumnIndex, 1); row.splice(toColumnIndex, 0, movedCell[0]); }); setRows(updatedRows); onColumnChange(updatedColumns); // Notify parent of column change }; // Handle input change for new row const handleNewRowChange = (e: ChangeEvent<HTMLInputElement>, columnIndex: number) => { const value = e.target.value; const updatedNewRow = [...newRow]; updatedNewRow[columnIndex] = value; setNewRow(updatedNewRow); }; // Handle adding the new row const handleAddNewRow = () => { if (newRow.some((cell) => cell !== '')) { const updatedRows = [...currentRows, newRow]; setRows(updatedRows); onRowChange(updatedRows); // Notify parent of row change setNewRow(new Array(currentColumns.length).fill('')); // Clear the new row after adding onNewRow(newRow); // Notify parent about the new row added } }; return ( <div className="table-container"> <table> <thead> <tr> {currentColumns.map((column, columnIndex) => ( <DraggableHeader key={columnIndex} columnIndex={columnIndex} moveColumn={moveColumn} enableDragAndDrop={enableDragAndDrop} > {column} </DraggableHeader> ))} </tr> </thead> <tbody> {currentRows.map((row, rowIndex) => ( <tr key={rowIndex}> {row.map((cell, columnIndex) => ( <DraggableCell key={columnIndex} rowIndex={rowIndex} columnIndex={columnIndex} moveRow={moveRow} moveColumn={moveColumn} enableDragAndDrop={enableDragAndDrop} > {cell} </DraggableCell> ))} </tr> ))} {/* Empty row at the bottom */} <tr> {newRow.map((cell, columnIndex) => ( <td key={columnIndex}> <input type="text" value={cell} onChange={(e) => handleNewRowChange(e, columnIndex)} onBlur={handleAddNewRow} // Automatically add the row when focus leaves /> </td> ))} </tr> </tbody> </table> </div> ); }; export default DraggableTable;
Editor is loading...
Leave a Comment