Table.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import { FC, useEffect, useRef, useState } from 'react';
  2. import React from 'react';
  3. import { makeStyles } from '@material-ui/core/styles';
  4. import Table from '@material-ui/core/Table';
  5. import TableBody from '@material-ui/core/TableBody';
  6. import TableCell from '@material-ui/core/TableCell';
  7. import TableContainer from '@material-ui/core/TableContainer';
  8. import TableRow from '@material-ui/core/TableRow';
  9. import Checkbox from '@material-ui/core/Checkbox';
  10. import { TableType } from './Types';
  11. import { Box, Button, Typography } from '@material-ui/core';
  12. import EnhancedTableHead from './TableHead';
  13. import { stableSort, getComparator } from './Utils';
  14. import Copy from '../../components/copy/Copy';
  15. import ActionBar from './ActionBar';
  16. import LoadingTable from './LoadingTable';
  17. const useStyles = makeStyles(theme => ({
  18. root: {
  19. // minHeight: '29vh',
  20. width: '100%',
  21. flexGrow: 1,
  22. // flexBasis: 0,
  23. // change scrollbar style
  24. '&::-webkit-scrollbar': {
  25. width: '8px',
  26. },
  27. '&::-webkit-scrollbar-track': {
  28. backgroundColor: '#f9f9f9',
  29. },
  30. '&::-webkit-scrollbar-thumb': {
  31. borderRadius: '8px',
  32. backgroundColor: '#eee',
  33. },
  34. },
  35. box: {
  36. backgroundColor: '#fff',
  37. },
  38. paper: {
  39. width: '100%',
  40. marginBottom: theme.spacing(2),
  41. },
  42. table: {
  43. minWidth: 750,
  44. },
  45. tableCell: {
  46. background: theme.palette.common.white,
  47. paddingLeft: theme.spacing(2),
  48. },
  49. hoverActionCell: {
  50. transition: '0.2s all',
  51. padding: 0,
  52. width: '50px',
  53. backgroundColor: '#fff',
  54. '& span': {
  55. opacity: 0,
  56. },
  57. },
  58. checkbox: {
  59. background: theme.palette.common.white,
  60. },
  61. rowHover: {
  62. '&:hover': {
  63. backgroundColor: '#f3fcfe !important',
  64. '& td': {
  65. background: 'inherit',
  66. },
  67. '& $hoverActionCell': {
  68. backgroundColor: theme.palette.primary.main,
  69. '& span': {
  70. opacity: 1,
  71. },
  72. },
  73. },
  74. },
  75. cell: {
  76. '& p': {
  77. display: 'inline-block',
  78. verticalAlign: 'middle',
  79. overflow: 'hidden',
  80. textOverflow: 'ellipsis',
  81. whiteSpace: 'nowrap',
  82. maxWidth: '300px',
  83. fontSize: '12.8px',
  84. },
  85. '& button': {
  86. textTransform: 'inherit',
  87. justifyContent: 'flex-start',
  88. },
  89. // '& svg': {
  90. // color: 'rgba(0, 0, 0, 0.33)',
  91. // },
  92. },
  93. noData: {
  94. paddingTop: theme.spacing(6),
  95. textAlign: 'center',
  96. letterSpacing: '0.5px',
  97. color: 'rgba(0, 0, 0, 0.6)',
  98. },
  99. }));
  100. const EnhancedTable: FC<TableType> = props => {
  101. const {
  102. selected,
  103. onSelected,
  104. isSelected,
  105. onSelectedAll,
  106. rows = [],
  107. colDefinitions,
  108. primaryKey,
  109. openCheckBox = true,
  110. disableSelect,
  111. noData,
  112. showHoverStyle,
  113. isLoading,
  114. setPageSize,
  115. } = props;
  116. const classes = useStyles();
  117. const [order, setOrder] = React.useState('asc');
  118. const [orderBy, setOrderBy] = React.useState<string>('');
  119. const [tableMouseStatus, setTableMouseStatus] = React.useState<boolean[]>([]);
  120. const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
  121. const containerRef = useRef(null);
  122. const rowRef = useRef(null);
  123. const handleRequestSort = (event: any, property: string) => {
  124. const isAsc = orderBy === property && order === 'asc';
  125. setOrder(isAsc ? 'desc' : 'asc');
  126. setOrderBy(property);
  127. };
  128. useEffect(() => {
  129. const height: number = (containerRef.current as any)!.offsetHeight;
  130. // table header 57px, loading row 40px
  131. const count = Math.floor((height - 57) / 40);
  132. setLoadingRowCount(count);
  133. }, []);
  134. useEffect(() => {
  135. if (setPageSize) {
  136. const containerHeight: number = (containerRef.current as any)!
  137. .offsetHeight;
  138. const rowHeight: number = (rowRef.current as any)?.offsetHeight || 0;
  139. const tableHeaderHeight: number = 57;
  140. if (rowHeight > 0) {
  141. const pageSize = Math.floor(
  142. (containerHeight - tableHeaderHeight) / rowHeight
  143. );
  144. setPageSize(pageSize);
  145. }
  146. }
  147. }, [setPageSize]);
  148. return (
  149. <TableContainer ref={containerRef} className={classes.root}>
  150. <Box height="100%" className={classes.box}>
  151. <Table
  152. stickyHeader
  153. className={classes.table}
  154. aria-labelledby="tableTitle"
  155. size="medium"
  156. aria-label="enhanced table"
  157. >
  158. <EnhancedTableHead
  159. colDefinitions={colDefinitions}
  160. numSelected={selected.length}
  161. order={order}
  162. orderBy={orderBy}
  163. onSelectAllClick={onSelectedAll}
  164. onRequestSort={handleRequestSort}
  165. rowCount={rows.length}
  166. openCheckBox={openCheckBox}
  167. />
  168. {!isLoading && (
  169. <TableBody>
  170. {rows && rows.length ? (
  171. stableSort(rows, getComparator(order, orderBy)).map(
  172. (row, index) => {
  173. const isItemSelected = isSelected(row);
  174. const labelId = `enhanced-table-checkbox-${index}`;
  175. const handleMouseEnter = () => {
  176. setTableMouseStatus(v => {
  177. const copy = [...v];
  178. copy[index] = true;
  179. return copy;
  180. });
  181. };
  182. const handleMouseLeave = () =>
  183. setTableMouseStatus(v => {
  184. const copy = [...v];
  185. copy[index] = false;
  186. return copy;
  187. });
  188. return (
  189. <TableRow
  190. ref={rowRef}
  191. hover={showHoverStyle}
  192. key={'row' + row[primaryKey] + index}
  193. onClick={event => onSelected(event, row)}
  194. role="checkbox"
  195. aria-checked={isItemSelected}
  196. tabIndex={-1}
  197. selected={isItemSelected && !disableSelect}
  198. classes={{
  199. hover: classes.rowHover,
  200. }}
  201. onMouseEnter={handleMouseEnter}
  202. onMouseLeave={handleMouseLeave}
  203. >
  204. {openCheckBox && (
  205. <TableCell
  206. padding="checkbox"
  207. className={classes.checkbox}
  208. >
  209. <Checkbox
  210. checked={isItemSelected}
  211. color="primary"
  212. inputProps={{ 'aria-labelledby': labelId }}
  213. />
  214. </TableCell>
  215. )}
  216. {colDefinitions.map((colDef, i) => {
  217. const { actionBarConfigs = [], needCopy = false } =
  218. colDef;
  219. const cellStyle = colDef.getStyle
  220. ? colDef.getStyle(row[colDef.id])
  221. : {};
  222. return colDef.showActionCell ? (
  223. <TableCell
  224. className={`${classes.cell} ${
  225. classes.tableCell
  226. } ${
  227. colDef.isHoverAction
  228. ? classes.hoverActionCell
  229. : ''
  230. }`}
  231. key="manage"
  232. style={cellStyle}
  233. >
  234. <ActionBar
  235. showLabel={tableMouseStatus[index]}
  236. configs={actionBarConfigs}
  237. isHoverType={colDef.isHoverAction}
  238. row={row}
  239. ></ActionBar>
  240. </TableCell>
  241. ) : (
  242. <TableCell
  243. key={'cell' + row[primaryKey] + i}
  244. padding={i === 0 ? 'none' : 'default'}
  245. align={colDef.align || 'left'}
  246. className={`${classes.cell} ${classes.tableCell}`}
  247. style={cellStyle}
  248. >
  249. {row[colDef.id] &&
  250. typeof row[colDef.id] === 'string' ? (
  251. <Typography title={row[colDef.id]}>
  252. {colDef.onClick ? (
  253. <Button
  254. color="primary"
  255. data-data={row[colDef.id]}
  256. data-index={index}
  257. onClick={e => {
  258. colDef.onClick &&
  259. colDef.onClick(e, row);
  260. }}
  261. >
  262. {row[colDef.id]}
  263. </Button>
  264. ) : (
  265. row[colDef.id]
  266. )}
  267. </Typography>
  268. ) : (
  269. <>
  270. {colDef.onClick ? (
  271. <Button
  272. color="primary"
  273. data-data={row[colDef.id]}
  274. data-index={index}
  275. onClick={e => {
  276. colDef.onClick &&
  277. colDef.onClick(e, row);
  278. }}
  279. >
  280. {row[colDef.id]}
  281. </Button>
  282. ) : (
  283. row[colDef.id]
  284. )}
  285. </>
  286. )}
  287. {needCopy && row[colDef.id] && (
  288. <Copy data={row[colDef.id]} />
  289. )}
  290. </TableCell>
  291. );
  292. })}
  293. </TableRow>
  294. );
  295. }
  296. )
  297. ) : (
  298. <tr>
  299. <td
  300. className={classes.noData}
  301. colSpan={colDefinitions.length}
  302. >
  303. {noData}
  304. </td>
  305. </tr>
  306. )}
  307. </TableBody>
  308. )}
  309. </Table>
  310. {isLoading && <LoadingTable count={loadingRowCount} />}
  311. </Box>
  312. </TableContainer>
  313. );
  314. };
  315. export default EnhancedTable;