Inline cell editing
In our implementation, we want to be able to add records to the array inline from the datatable, think of a structure of the table and at the bottom an "empty" row that works as a button that adds a new entry to the records. The way we do this now is that we use an unstyled button and use a transparent styling to indicate "interactivity".
However, what we can't yet do or figure out is inline cell value editing. I understand we can handle cell clicks but I'd rather make it that we can focus on the cell and have an input in them and then "save" the value to the records array once we unfocus the input
Some pseudo code to kind of illustrate what I mean
const records = [{name: 'row 1', some_val: 100}, {name: 'row2', some_val: 101}];
const create = () => {
// push an "empty" record, or a default variant of a record
records.push({name: '', some_val: 0})
}
// cellEdit are pseudo props - does not exist
<DataTable columns={table_columns} records={records} cellEditable />
<Button color="rgba(173, 173, 173, 1)" variant="transparent" onClick={create}>New record...</Button>
What would be recommended way of doing this?
I apologize before hand if there is already an issue or fix for this but after searching I can't seem to find a relevant issue or article in docs.
I did find this: https://icflorescu.github.io/mantine-datatable/examples/expanding-rows/#using-collapse-function-in-row-expansion-content
However I think for our use-case this would be a bit too "verbose" as we want to edit directly in the cell.
Current workaround is to define the render prop in the column to render an unstyled input. Not sure if I'm a big fan but for now this "works". I'll keep this open for now still for discussion in case we may want to look into supporting cell editing.
@icflorescu its your call :)
Did you manage to do proper cell editing?
Not necessarily, I havent yet had the time to fork and implement something myself. The way I eventually solved it was by using cell click handlers to "replace" the cell with an input and then when listen to the leave focus event to then save the value in the dataset and re-render the row
Current workaround is to define the render prop in the column to render an unstyled input. Not sure if I'm a big fan but for now this "works". I'll keep this open for now still for discussion in case we may want to look into supporting cell editing.
@icflorescu its your call :)
Got some snippet for your implementation?
Current workaround is to define the render prop in the column to render an unstyled input. Not sure if I'm a big fan but for now this "works". I'll keep this open for now still for discussion in case we may want to look into supporting cell editing. @icflorescu its your call :)
Got some snippet for your implementation?
Sure, I put this together fast for you but this is by no means the best, but it is a start.
type DataPoint = {
id: string;
name: string;
};
const initialData: DataPoint[] = [
{
id: "user_1",
name: "mikkurogue",
},
];
const columns: DataTableColumn<DataPoint>[] = [
{
accessor: "id",
},
{
accessor: "name",
},
];
---
export default function Home() {
const [data, setData] = useState(initialData);
const [editingCell, setEditingCell] = useState<string | null>(null);
function handleCellEdit(id: string, field: string, value: any) {
setData((prevData) =>
prevData.map((row) => (row.id === id ? { ...row, [field]: value } : row)),
);
setEditingCell(null);
}
return (
<Table
columns={columns}
data={data}
editingCell={editingCell}
onCellClick={(id, field) => setEditingCell(`${id}-${field}`)}
onCellEdit={handleCellEdit}
/>
);
}
---
type TableProps<T extends { id: string }> = {
columns: DataTableColumn<T>[];
data: T[];
editingCell: string | null;
onCellClick: (id: string, field: string) => void;
onCellEdit: (id: string, field: string, value: any) => void;
};
function Table<T extends { id: string }>({
columns,
data,
editingCell,
onCellClick,
onCellEdit,
}: TableProps<T>) {
return (
<DataTable
withTableBorder
withColumnBorders
striped
records={data}
columns={columns.map((col) => ({
...col,
render: (record: T) => {
if (
editingCell === `${record.id}-${col.accessor}` &&
col.accessor !== "id"
) {
return (
<TextInput
defaultValue={record[col.accessor as keyof T] as string}
onBlur={(e) =>
onCellEdit(
record.id,
col.accessor as string,
e.currentTarget.value,
)
}
autoFocus
/>
);
}
return (
<div
onClick={() => onCellClick(record.id, col.accessor as string)}
style={{ width: "100%", height: "100%" }}
>
{record[col.accessor as keyof T]}
</div>
);
},
}))}
/>
);
}
export default Table;
In this example, I basically abstract the table to a separate component but you can just do it all in the same place where the implementation of DataTable happens too. Its just important to make sure you have then set proper types for the data/records you are trying to play with