Add editing of table fields, significant refactoring

This makes RowOpenAction obsolete with PrimaryTableCell. Dropdowns to be
implemented at a later point. Lots of typing and further styling needed.
This commit is contained in:
Andy Chan 2024-04-13 12:56:00 -04:00
parent b188c783ce
commit d689e22dd8
5 changed files with 73 additions and 26 deletions

View File

@ -12,12 +12,14 @@ import {
sortingFns,
useReactTable,
} from "@tanstack/react-table";
import { ChangeEvent, useState, useEffect, FunctionComponent } from "react";
import { ChangeEvent, useState, useEffect, FunctionComponent, useRef, ChangeEventHandler, Key } from "react";
import { RowOptionMenu } from "./RowOptionMenu";
import { RowOpenAction } from "./RowOpenAction";
import { TableAction } from "./TableAction";
import { Bars2Icon, AtSymbolIcon, HashtagIcon, ArrowDownCircleIcon } from "@heroicons/react/24/solid";
import { rankItem } from "@tanstack/match-sorter-utils";
import { TableCell } from "./TableCell";
import { PrimaryTableCell } from "./PrimaryTableCell";
const usersExample = usersImport as unknown as User[];
@ -80,19 +82,19 @@ export const Table = () => {
}),
columnHelper.accessor("username", {
header: () => <><Bars2Icon className="inline align-top h-4 mr-2" /> Username</>,
cell: (info) => <RowOpenAction title={info.getValue()} />,
cell: PrimaryTableCell,
}),
columnHelper.accessor("role", {
header: () => <><ArrowDownCircleIcon className="inline align-top h-4" /> Role</>,
cell: (info) => info.renderValue(),
cell: TableCell,
}),
columnHelper.accessor("email", {
header: () => <><AtSymbolIcon className="inline align-top h-4" /> Email</>,
cell: (info) => info.renderValue(),
cell: TableCell,
}),
columnHelper.accessor("program", {
header: () => <><ArrowDownCircleIcon className="inline align-top h-4" /> Program</>,
cell: (info) => info.renderValue(),
cell: TableCell,
}),
];
@ -105,6 +107,11 @@ export const Table = () => {
setQuery(String(target.value));
}
const handleCellChange = (e: ChangeEvent, key: Key) => {
const target = e.target as HTMLInputElement;
console.log(key);
}
// TODO: Filtering
// TODO: Sorting
@ -121,6 +128,21 @@ export const Table = () => {
globalFilterFn: fuzzyFilter,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
meta: {
updateData: (rowIndex: number, columnId: string, value: string) => {
setData(old =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
};
}
return row;
})
);
}
}
});
return (
@ -163,7 +185,11 @@ export const Table = () => {
key={row.id}
>
{row.getVisibleCells().map((cell, i) => (
<TableCell key={cell.id} cell={cell} />
<td key={cell.id}
className={"p-2 [&:nth-child(n+3)]:border-x relative first:text-left first:px-0 last:border-none"}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
);
@ -173,21 +199,3 @@ export const Table = () => {
</div>
)
}
type TableCellProps = {
cell: Cell<any, any>;
//i: number;
}
const TableCell: FunctionComponent<TableCellProps> = ({ cell }) => {
return (
<td
className={
"p-2 [&:nth-child(n+3)]:border-x "
+ "first:text-left first:px-0 last:border-none"
}
key={cell.id}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>)
}

View File

@ -0,0 +1,22 @@
/* An extension of TableCell.tsx that includes an "open" button and the drawer.
For cells in the "primary" (or first) column of the table. */
import Drawer from "@/components/page/Drawer";
import { TableCell } from "./TableCell";
import { SetStateAction, useState } from "react";
export const PrimaryTableCell = ({ getValue, row, column, table }) => {
const [pageContent, setPageContent] = useState("")
const handleDrawerContentChange = (newContent: SetStateAction<string>) => {
setPageContent(newContent);
};
return (
<div className="font-semibold group flex flex-row items-center justify-between pr-2">
<TableCell getValue={getValue} row={row} column={column} table={table} />
<span>
<Drawer title={getValue()} editableContent={pageContent} onSave={handleDrawerContentChange}>{pageContent}</Drawer>
</span>
</div>
);
};

View File

@ -16,7 +16,7 @@ export const RowOptionMenu = ( { onDelete, onHide } ) => {
<>
<button className="align-center" onClick={() => setMenuOpen(!menuOpen)}><EllipsisVerticalIcon className="h-4"/></button>
<div
className={"justify-start border border-gray-200 shadow-lg flex flex-col absolute bg-white w-auto p-2 rounded [&>*]:rounded" + (!menuOpen ? " invisible" : "")}
className={"justify-start border border-gray-200 shadow-lg flex flex-col absolute bg-white w-auto p-2 rounded [&>*]:rounded z-10" + (!menuOpen ? " invisible" : "")}
>
<RowOption icon={TrashIcon} label="Delete" onClick={onDelete} />
<RowOption icon={ArrowUpRightIcon} label="Open" onClick={() => { /* handle open */ }} />

View File

@ -0,0 +1,17 @@
/* A lone table cell. Passed in for "cell" for a TanStack Table. */
import { useState, useEffect } from "react";
export const TableCell = ({ getValue, row, column, table }) => {
const initialValue = getValue();
const [value, setValue] = useState(initialValue);
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
const onBlur = () => {
table.options.meta?.updateData(row.index, column.id, value);
};
return <input className="w-full py-2 bg-inherit" value={value} onChange={e => setValue(e.target.value)} onBlur={onBlur} />;
};

View File

@ -26,7 +26,7 @@ const Drawer: FunctionComponent<DrawerProps> = ({ title, children, onSave, edita
setEditContent(event.target.value);
};
const drawerClassName = `fixed top-0 right-0 w-1/2 h-full bg-white transform ease-in-out duration-300 ${
const drawerClassName = `fixed top-0 right-0 w-1/2 h-full bg-white transform ease-in-out duration-300 z-20 ${
isOpen ? "translate-x-0 shadow-xl" : "translate-x-full"
}`;