import { Checkbox, IconButton, Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core'; import { Delete, Edit } from "@material-ui/icons"; import React, { FunctionComponent, useCallback, useMemo, useState } from 'react'; import './NiceTable.css'; export type NiceTableRow = { key: string columnValues: {[key: string]: any} } export type NiceTableColumn = { key: string label: string element?: any } interface Props { rows: NiceTableRow[], columns: NiceTableColumn[], onDeleteRow?: (key: string) => void, deleteRowLabel?: string, onEditRow?: (key: string) => void, editRowLabel?: string, selectionMode?: 'none' | 'single' | 'multiple', selectedRowKeys?: string[], onSelectedRowKeysChanged?: ((keys: string[]) => void) selectionDisabled?: boolean } const NiceTable: FunctionComponent = ({ rows, columns, onDeleteRow=undefined, deleteRowLabel=undefined, onEditRow=undefined, editRowLabel=undefined, selectionMode='none', // none, single, multiple selectedRowKeys=[], onSelectedRowKeysChanged=undefined, selectionDisabled }) => { const selectedRowKeysObj = useMemo(() => { const x: {[key: string]: boolean} = {}; selectedRowKeys.forEach((key) => {x[key] = true}); return x }, [selectedRowKeys]) const [confirmDeleteRowKey, setConfirmDeleteRowKey] = useState(null) const handleClickRow = useCallback((key: string) => { if (!onSelectedRowKeysChanged || false) return; if (selectionMode === 'single') { if (!(key in selectedRowKeysObj) || !selectedRowKeysObj[key]) { onSelectedRowKeysChanged([key + '']); } else { onSelectedRowKeysChanged([]); } } else if (selectionMode === 'multiple') { // todo: write this logic. Note, we'll need to also pass in the event to get the ctrl/shift modifiers onSelectedRowKeysChanged( Object.keys(selectedRowKeysObj) // eslint-disable-next-line eqeqeq .filter(k => k != key && selectedRowKeysObj[k]) .concat(selectedRowKeysObj[key] ? [] : [key.toString()]) ); } }, [onSelectedRowKeysChanged, selectionMode, selectedRowKeysObj]) const handleDeleteRow = useCallback((rowKey: string) => { setConfirmDeleteRowKey(rowKey) }, []) const handleConfirmDeleteRow = useCallback((rowKey: string, confirmed: boolean) => { if (confirmed) { onDeleteRow && onDeleteRow(rowKey) } setConfirmDeleteRowKey(null) }, [onDeleteRow]) const handleEditRow = useCallback((rowKey: string) => { onEditRow && onEditRow(rowKey) }, [onEditRow]) return ( { columns.map(col => ( {col.element ? col.element : ( {col.label} )} )) } { rows.map(row => ( { onDeleteRow && ((confirmDeleteRowKey === row.key) ? ( ) : ( ( ) )) } { onEditRow && ( ) } { selectionMode !== 'none' && ( handleClickRow(row.key)} disabled={selectionDisabled} /> ) } { columns.map(col => ( {makeCell(row.columnValues[col.key])} )) } )) }
); }; const DeleteRowButton: FunctionComponent<{title: string, rowKey: string, onDeleteRow?: (key: string) => void}> = ({ title, rowKey, onDeleteRow }) => { const handleClick = useCallback(() => { onDeleteRow && onDeleteRow(rowKey) }, [onDeleteRow, rowKey]) return ( ) } const ConfirmDeleteRowButton: FunctionComponent<{title: string, rowKey: string, onConfirmDeleteRow?: (key: string, confirmed: boolean) => void}> = ({ title, rowKey, onConfirmDeleteRow }) => { const handleClick = useCallback(() => { onConfirmDeleteRow && onConfirmDeleteRow(rowKey, true) }, [onConfirmDeleteRow, rowKey]) const handleCancel = useCallback(() => { onConfirmDeleteRow && onConfirmDeleteRow(rowKey, false) }, [onConfirmDeleteRow, rowKey]) return ( Confirm delete? ✖ ) } const EditRowButton: FunctionComponent<{title: string, rowKey: string, onEditRow?: (key: string) => void}> = ({title, rowKey, onEditRow}) => { return ( onEditRow && onEditRow(rowKey)} > ) } const makeCell = (x: any) => { // eslint-disable-next-line eqeqeq if (x == 0) return x; // !'0' is true, but we shouldn't null out actual 0s if (!x) return ''; if (typeof(x) == "object") { if (x.element) return x.element; else return x.text || ''; } else { return x; } } export default NiceTable;