diff --git a/packages/analyst/src/components/AnalystTable.js b/packages/analyst/src/components/AnalystTable.js index c71ae17..030e71b 100644 --- a/packages/analyst/src/components/AnalystTable.js +++ b/packages/analyst/src/components/AnalystTable.js @@ -3,21 +3,26 @@ import Badge from '@material-ui/core/Badge'; import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; import { get as getPandasCsv } from '@wq/pandas'; -import { useComponents } from '@wq/react'; +import { useComponents, useNav, useApp } from '@wq/react'; import PropTypes from 'prop-types'; export default function AnalystTable({ + data: initialData, url, initial_rows, initial_order, id_column, id_url_prefix, + compact, }) { - const [data, setData] = useState(), + const [data, setData] = useState(initialData), [columns, setColumns] = useState(), [filters, setFilters] = useState({}), [orders, setOrders] = useState(initial_order || {}), - [rowsPerPage, setRowsPerPage] = useState(initial_rows || 50), + pagination = initial_rows !== 'all', + [rowsPerPage, setRowsPerPage] = useState( + pagination ? initial_rows || 50 : null + ), [page, setPage] = useState(0), { Typography, @@ -30,9 +35,29 @@ export default function AnalystTable({ TableContainer, TablePagination, IconButton, - } = useComponents(); + } = useComponents(), + app = useApp(), + appNav = useNav(), + prefixIsAppUrl = + id_url_prefix && + new URL(id_url_prefix, window.location.origin) + .toString() + .startsWith( + new URL(app.base_url, window.location.origin).toString() + ), + nav = (id) => { + const url = `${id_url_prefix}${id}`; + if (prefixIsAppUrl) { + appNav(url); + } else { + window.location.href = url; + } + }; useEffect(() => { + if (!url) { + return; + } async function loadData() { const data = await getPandasCsv(url, { flatten: true }); setData(data); @@ -65,9 +90,7 @@ export default function AnalystTable({ }); const colInfo = { name: key }; - if (key === id_column) { - colInfo.url_prefix = id_url_prefix; - } else if (metaKeys[key]) { + if (metaKeys[key]) { colInfo.values = Object.entries(metaKeys[key]) .sort((kval1, kval2) => sort(kval1[0], kval2[0])) .map(([key, value]) => ({ @@ -109,11 +132,14 @@ export default function AnalystTable({ if (!sortedData) { return null; } + if (!pagination) { + return sortedData; + } return sortedData.slice( page * rowsPerPage, page * rowsPerPage + rowsPerPage ); - }, [sortedData, page, rowsPerPage]); + }, [sortedData, pagination, page, rowsPerPage]); const toggleOrder = (name) => { let nextOrders = { ...orders }; @@ -161,126 +187,202 @@ export default function AnalystTable({ if (!sortedData.length) { return No data matching the current filter(s).; } + + const FullHeader = (column) => { + if (column.name == id_column) { + return ; + } + + return ( + + + + {column.name} + + + toggleOrder(column.name)} + /> + + {column.values && ( + + toggleFilter(column.name, value) + } + resetFilter={() => resetFilter(column.name)} + /> + )} + + + ); + }; + + const CompactHeader = (column) => { + if (column.name === id_column) { + return null; + } else if (column.values) { + return ( + + + toggleFilter(column.name, value) + } + resetFilter={() => resetFilter(column.name)} + /> + + ); + } else { + return ( + toggleOrder(column.name)} + > + {column.name} + {orders[column.name] === 'desc' + ? ' ↓' + : orders[column.name] + ? ' ↑' + : ''} + + ); + } + }; + + const ColumnHeader = compact ? CompactHeader : FullHeader, + Cell = (cell) => { + if (cell.column.name === id_column) { + if (compact) { + return null; + } else { + return ( + + + + ); + } + } else if (compact && id_column) { + const id = cell.row[id_column]; + return ( + nav(id)} + > + + + ); + } else { + return ( + + + + ); + } + }; + return ( - {columns.map((column) => - column.url_prefix ? ( - - ) : ( - - - - {column.name} - - - - toggleOrder(column.name) - } - /> - - {column.values && ( - - toggleFilter( - column.name, - value - ) - } - resetFilter={() => - resetFilter(column.name) - } - /> - )} - - - ) - )} + {columns.map((column) => ( + + ))} {slicedData.map((row, i) => ( {columns.map((column) => ( - - - + ))} ))} - setPage(page)} - onChangeRowsPerPage={(evt) => setRowsPerPage(evt.target.value)} - /> + {pagination && ( + setPage(page)} + onChangeRowsPerPage={(evt) => + setRowsPerPage(evt.target.value) + } + /> + )} ); } AnalystTable.propTypes = { + data: PropTypes.arrayOf(PropTypes.object), url: PropTypes.string, - initial_rows: PropTypes.number, + initial_rows: PropTypes.oneOfType(PropTypes.number, PropTypes.string), + compact: PropTypes.bool, initial_order: PropTypes.object, id_column: PropTypes.string, id_url_prefix: PropTypes.string, }; -function ColumnFilter({ name, values, filter, toggleFilter, resetFilter }) { +function ColumnFilter({ + name, + values, + filter, + toggleFilter, + resetFilter, + textButton, +}) { const [anchorEl, setAnchorEl] = useState(null), { IconButton, CheckboxButton } = useComponents(), menuId = `${name}-menu`; + const Component = textButton ? TextButton : IconButton; return ( <> - setAnchorEl(evt.target)} size="small" icon="filter" color={filter && 'secondary'} + title={name} /> + {props.title} + {props.color && ' *'} + + ); +} +TextButton.propTypes = { + title: PropTypes.string, + color: PropTypes.string, }; function CellValue({ row, column }) { - const { name, url_prefix } = column, + const { name } = column, value = row[name]; - if (url_prefix) { - return ; - } else { - return <>{value}>; - } + return <>{value}>; } CellValue.propTypes = { @@ -328,8 +440,8 @@ CellValue.propTypes = { column: PropTypes.object, }; -function CellLink({ row, column }) { - const { name, url_prefix } = column, +function CellLink({ row, column, nav }) { + const { name } = column, value = row[name], { IconButton } = useComponents(); return ( @@ -337,8 +449,7 @@ function CellLink({ row, column }) { icon="view" size="small" color="primary" - component="a" - href={`${url_prefix}${value}`} + onClick={() => nav(value)} title={`View ${name} ${value}`} /> ); @@ -347,6 +458,7 @@ function CellLink({ row, column }) { CellLink.propTypes = { row: PropTypes.object, column: PropTypes.object, + nav: PropTypes.func, }; function matchFilters(obj, filters) {