Table.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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 EditableTableHead from './TableEditableHead';
  14. import ActionBar from './ActionBar';
  15. import LoadingTable from './LoadingTable';
  16. import CopyButton from '../advancedSearch/CopyButton';
  17. import { useTranslation } from 'react-i18next';
  18. const useStyles = makeStyles(theme => ({
  19. root: {
  20. width: '100%',
  21. flexGrow: 1,
  22. /* set flex basis to make child item height 100% work on Safari */
  23. flexBasis: 0,
  24. // change scrollbar style
  25. '&::-webkit-scrollbar': {
  26. width: '8px',
  27. },
  28. '&::-webkit-scrollbar-track': {
  29. backgroundColor: '#f9f9f9',
  30. },
  31. '&::-webkit-scrollbar-thumb': {
  32. borderRadius: '8px',
  33. backgroundColor: '#eee',
  34. },
  35. },
  36. box: {
  37. backgroundColor: '#fff',
  38. },
  39. paper: {
  40. width: '100%',
  41. marginBottom: theme.spacing(2),
  42. },
  43. table: {
  44. minWidth: 750,
  45. },
  46. tableCell: {
  47. background: theme.palette.common.white,
  48. paddingLeft: theme.spacing(2),
  49. },
  50. hoverActionCell: {
  51. transition: '0.2s all',
  52. padding: 0,
  53. width: '50px',
  54. backgroundColor: '#fff',
  55. '& span': {
  56. opacity: 0,
  57. },
  58. },
  59. checkbox: {
  60. background: theme.palette.common.white,
  61. },
  62. rowHover: {
  63. '&:hover': {
  64. backgroundColor: '#f3fcfe !important',
  65. '& td': {
  66. background: 'inherit',
  67. },
  68. '& $hoverActionCell': {
  69. backgroundColor: theme.palette.primary.main,
  70. '& span': {
  71. opacity: 1,
  72. },
  73. },
  74. },
  75. },
  76. cell: {
  77. borderBottom: '1px solid #e9e9ed',
  78. '& p': {
  79. display: 'inline-block',
  80. verticalAlign: 'middle',
  81. overflow: 'hidden',
  82. textOverflow: 'ellipsis',
  83. whiteSpace: 'nowrap',
  84. maxWidth: (props: { tableCellMaxWidth: string }) =>
  85. props.tableCellMaxWidth,
  86. fontSize: '14px',
  87. lineHeight: '20px',
  88. },
  89. },
  90. noData: {
  91. paddingTop: theme.spacing(6),
  92. textAlign: 'center',
  93. letterSpacing: '0.5px',
  94. color: 'rgba(0, 0, 0, 0.6)',
  95. },
  96. copyBtn: {
  97. marginLeft: theme.spacing(0.5),
  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. // whether show checkbox in the first column
  110. // set true as default
  111. openCheckBox = true,
  112. disableSelect,
  113. noData,
  114. // whether change table row background color when mouse hover
  115. // set true as default
  116. showHoverStyle = true,
  117. isLoading,
  118. setPageSize,
  119. headEditable = false,
  120. // editable heads required param, contains heads components and its value
  121. editHeads = [],
  122. // if table cell max width not be passed, table row will use 300px as default
  123. tableCellMaxWidth = '300px',
  124. handleSort,
  125. order,
  126. orderBy,
  127. } = props;
  128. const classes = useStyles({ tableCellMaxWidth });
  129. const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
  130. const containerRef = useRef(null);
  131. const { t: commonTrans } = useTranslation();
  132. const copyTrans = commonTrans('copy');
  133. useEffect(() => {
  134. const height: number = (containerRef.current as any)!.offsetHeight;
  135. // table header 57px, loading row 40px
  136. const count = Math.floor((height - 57) / 40);
  137. setLoadingRowCount(count);
  138. }, []);
  139. useEffect(() => {
  140. if (setPageSize) {
  141. const containerHeight: number = (containerRef.current as any)!
  142. .offsetHeight;
  143. // table default row height is 54
  144. // if pass component as row item, its max height should be 54 too
  145. const rowHeight = 54;
  146. // table header default height is 57
  147. const tableHeaderHeight: number = 57;
  148. if (rowHeight > 0) {
  149. const pageSize = Math.floor(
  150. (containerHeight - tableHeaderHeight) / rowHeight
  151. );
  152. setPageSize(pageSize);
  153. }
  154. }
  155. }, [setPageSize]);
  156. return (
  157. <TableContainer ref={containerRef} className={classes.root}>
  158. <Box height="100%" className={classes.box}>
  159. <Table
  160. stickyHeader
  161. className={classes.table}
  162. aria-labelledby="tableTitle"
  163. size="medium"
  164. aria-label="enhanced table"
  165. >
  166. {!headEditable ? (
  167. <EnhancedTableHead
  168. colDefinitions={colDefinitions}
  169. numSelected={selected.length}
  170. order={order}
  171. orderBy={orderBy}
  172. onSelectAllClick={onSelectedAll}
  173. handleSort={handleSort}
  174. rowCount={rows.length}
  175. openCheckBox={openCheckBox}
  176. />
  177. ) : (
  178. <EditableTableHead editHeads={editHeads} />
  179. )}
  180. {!isLoading && (
  181. <TableBody>
  182. {rows && rows.length ? (
  183. rows.map((row, index) => {
  184. const isItemSelected = isSelected(row);
  185. const labelId = `enhanced-table-checkbox-${index}`;
  186. return (
  187. <TableRow
  188. hover={showHoverStyle}
  189. key={'row' + row[primaryKey] + index}
  190. onClick={event => onSelected(event, row)}
  191. role="checkbox"
  192. aria-checked={isItemSelected}
  193. tabIndex={-1}
  194. selected={isItemSelected && !disableSelect}
  195. classes={{
  196. hover: classes.rowHover,
  197. }}
  198. >
  199. {openCheckBox && (
  200. <TableCell
  201. padding="checkbox"
  202. className={classes.checkbox}
  203. >
  204. <Checkbox
  205. checked={isItemSelected}
  206. color="primary"
  207. inputProps={{ 'aria-labelledby': labelId }}
  208. />
  209. </TableCell>
  210. )}
  211. {colDefinitions.map((colDef, i) => {
  212. const { actionBarConfigs = [], needCopy = false } =
  213. colDef;
  214. const cellStyle = colDef.getStyle
  215. ? colDef.getStyle(row[colDef.id])
  216. : {};
  217. return colDef.showActionCell ? (
  218. <TableCell
  219. className={`${classes.cell} ${classes.tableCell} ${
  220. colDef.isHoverAction
  221. ? classes.hoverActionCell
  222. : ''
  223. }`}
  224. key="manage"
  225. style={cellStyle}
  226. >
  227. <ActionBar
  228. configs={actionBarConfigs}
  229. isHoverType={colDef.isHoverAction}
  230. row={row}
  231. ></ActionBar>
  232. </TableCell>
  233. ) : (
  234. <TableCell
  235. key={'cell' + row[primaryKey] + i}
  236. // padding={i === 0 ? 'none' : 'default'}
  237. align={colDef.align || 'left'}
  238. className={`${classes.cell} ${classes.tableCell}`}
  239. style={cellStyle}
  240. >
  241. {row[colDef.id] &&
  242. typeof row[colDef.id] === 'string' ? (
  243. <Typography title={row[colDef.id]}>
  244. {colDef.onClick ? (
  245. <Button
  246. color="primary"
  247. data-data={row[colDef.id]}
  248. data-index={index}
  249. onClick={e => {
  250. colDef.onClick && colDef.onClick(e, row);
  251. }}
  252. >
  253. {row[colDef.id]}
  254. </Button>
  255. ) : (
  256. row[colDef.id]
  257. )}
  258. </Typography>
  259. ) : (
  260. <>
  261. {colDef.onClick ? (
  262. <Button
  263. color="primary"
  264. data-data={row[colDef.id]}
  265. data-index={index}
  266. onClick={e => {
  267. colDef.onClick && colDef.onClick(e, row);
  268. }}
  269. >
  270. {row[colDef.id]}
  271. </Button>
  272. ) : (
  273. row[colDef.id]
  274. )}
  275. </>
  276. )}
  277. {needCopy && row[colDef.id] && (
  278. <CopyButton
  279. label={copyTrans.label}
  280. value={row[colDef.id]}
  281. className={classes.copyBtn}
  282. />
  283. )}
  284. </TableCell>
  285. );
  286. })}
  287. </TableRow>
  288. );
  289. })
  290. ) : (
  291. <tr>
  292. <td
  293. className={classes.noData}
  294. colSpan={
  295. openCheckBox
  296. ? colDefinitions.length + 1
  297. : colDefinitions.length
  298. }
  299. >
  300. {noData}
  301. </td>
  302. </tr>
  303. )}
  304. </TableBody>
  305. )}
  306. </Table>
  307. {/* TODO(wenyi): loadingRowCount wrong here*/}
  308. {isLoading && <LoadingTable count={loadingRowCount} />}
  309. </Box>
  310. </TableContainer>
  311. );
  312. };
  313. export default EnhancedTable;