187 lines
4.7 KiB
JavaScript
187 lines
4.7 KiB
JavaScript
import { makeStyles } from '@mui/styles'
|
|
import classnames from 'classnames'
|
|
import * as R from 'ramda'
|
|
import React, { useState, useEffect } from 'react'
|
|
import {
|
|
AutoSizer,
|
|
List,
|
|
CellMeasurer,
|
|
CellMeasurerCache
|
|
} from 'react-virtualized'
|
|
import {
|
|
Table,
|
|
TBody,
|
|
THead,
|
|
Tr,
|
|
Td,
|
|
Th
|
|
} from 'src/components/fake-table/Table'
|
|
import { H4 } from 'src/components/typography'
|
|
import ExpandClosedIcon from 'src/styling/icons/action/expand/closed.svg?react'
|
|
import ExpandOpenIcon from 'src/styling/icons/action/expand/open.svg?react'
|
|
|
|
import styles from 'src/components/tables/DataTable.styles'
|
|
|
|
const useStyles = makeStyles(styles)
|
|
|
|
const Row = ({
|
|
id,
|
|
elements,
|
|
data,
|
|
width,
|
|
Details,
|
|
expanded,
|
|
expandRow,
|
|
expWidth,
|
|
expandable,
|
|
onClick
|
|
}) => {
|
|
const classes = useStyles()
|
|
|
|
const hasPointer = onClick || expandable
|
|
const trClasses = {
|
|
[classes.pointer]: hasPointer,
|
|
[classes.row]: true,
|
|
[classes.expanded]: expanded
|
|
}
|
|
|
|
return (
|
|
<div className={classes.rowWrapper}>
|
|
<div className={classnames({ [classes.before]: expanded && id !== 0 })}>
|
|
<Tr
|
|
className={classnames(trClasses)}
|
|
onClick={() => {
|
|
expandable && expandRow(id)
|
|
onClick && onClick(data)
|
|
}}
|
|
error={data.error}
|
|
errorMessage={data.errorMessage}>
|
|
{elements.map(({ view = it => it?.toString(), ...props }, idx) => (
|
|
<Td key={idx} {...props}>
|
|
{view(data)}
|
|
</Td>
|
|
))}
|
|
{expandable && (
|
|
<Td width={expWidth} textAlign="center">
|
|
<button
|
|
onClick={() => expandRow(id)}
|
|
className={classes.expandButton}>
|
|
{expanded && <ExpandOpenIcon />}
|
|
{!expanded && <ExpandClosedIcon />}
|
|
</button>
|
|
</Td>
|
|
)}
|
|
</Tr>
|
|
</div>
|
|
{expandable && expanded && (
|
|
<div className={classes.after}>
|
|
<Tr className={classnames({ [classes.expanded]: expanded })}>
|
|
<Td width={width}>
|
|
<Details it={data} />
|
|
</Td>
|
|
</Tr>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const DataTable = ({
|
|
elements = [],
|
|
data = [],
|
|
Details,
|
|
className,
|
|
expandable,
|
|
initialExpanded,
|
|
onClick,
|
|
loading,
|
|
emptyText,
|
|
extraHeight,
|
|
...props
|
|
}) => {
|
|
const [expanded, setExpanded] = useState(initialExpanded)
|
|
|
|
useEffect(() => setExpanded(initialExpanded), [initialExpanded])
|
|
|
|
const coreWidth = R.compose(R.sum, R.map(R.prop('width')))(elements)
|
|
const expWidth = 850 - coreWidth
|
|
const width = coreWidth + (expandable ? expWidth : 0)
|
|
|
|
const classes = useStyles({ width })
|
|
|
|
const expandRow = id => {
|
|
setExpanded(id === expanded ? null : id)
|
|
}
|
|
|
|
const cache = new CellMeasurerCache({
|
|
defaultHeight: 62,
|
|
fixedWidth: true
|
|
})
|
|
|
|
function rowRenderer({ index, key, parent, style }) {
|
|
return (
|
|
<CellMeasurer
|
|
cache={cache}
|
|
columnIndex={0}
|
|
key={key}
|
|
parent={parent}
|
|
rowIndex={index}>
|
|
<div style={style}>
|
|
<Row
|
|
width={width}
|
|
id={index}
|
|
expWidth={expWidth}
|
|
elements={elements}
|
|
data={data[index]}
|
|
Details={Details}
|
|
expanded={index === expanded}
|
|
expandRow={expandRow}
|
|
expandable={expandable}
|
|
onClick={onClick}
|
|
/>
|
|
</div>
|
|
</CellMeasurer>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-1 flex-col">
|
|
<Table className={classes.table}>
|
|
<THead>
|
|
{elements.map(({ width, className, textAlign, header }, idx) => (
|
|
<Th
|
|
key={idx}
|
|
width={width}
|
|
className={className}
|
|
textAlign={textAlign}>
|
|
{header}
|
|
</Th>
|
|
))}
|
|
{expandable && <Th width={expWidth}></Th>}
|
|
</THead>
|
|
<TBody className={classes.body}>
|
|
{loading && <H4>Loading...</H4>}
|
|
{!loading && R.isEmpty(data) && <H4>{emptyText}</H4>}
|
|
<AutoSizer disableWidth disableHeight>
|
|
{() => (
|
|
<List
|
|
// this has to be in a style because of how the component works
|
|
style={{ overflowX: 'inherit', outline: 'none' }}
|
|
{...props}
|
|
height={data.length * 62 + extraHeight}
|
|
width={width}
|
|
rowCount={data.length}
|
|
rowHeight={cache.rowHeight}
|
|
rowRenderer={rowRenderer}
|
|
overscanRowCount={50}
|
|
deferredMeasurementCache={cache}
|
|
/>
|
|
)}
|
|
</AutoSizer>
|
|
</TBody>
|
|
</Table>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default DataTable
|