Untitled
unknown
plain_text
a year ago
6.3 kB
4
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