Table.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. import { FC } from 'react';
  2. import Table from '@mui/material/Table';
  3. import TableBody from '@mui/material/TableBody';
  4. import TableCell from '@mui/material/TableCell';
  5. import TableContainer from '@mui/material/TableContainer';
  6. import TableRow from '@mui/material/TableRow';
  7. import Checkbox from '@mui/material/Checkbox';
  8. import Box from '@mui/material/Box';
  9. import Button from '@mui/material/Button';
  10. import Typography from '@mui/material/Typography';
  11. import EnhancedTableHead from './TableHead';
  12. import EditableTableHead from './TableEditableHead';
  13. import ActionBar from './ActionBar';
  14. import LoadingTable from './LoadingTable';
  15. import CopyButton from '../advancedSearch/CopyButton';
  16. import type { Theme } from '@mui/material/styles';
  17. import type { TableType } from './Types';
  18. const EnhancedTable: FC<TableType> = props => {
  19. let {
  20. selected,
  21. onSelected,
  22. isSelected,
  23. onSelectedAll,
  24. rows = [],
  25. colDefinitions,
  26. primaryKey,
  27. // whether show checkbox in the first column
  28. // set true as default
  29. openCheckBox = true,
  30. disableSelect,
  31. noData,
  32. // whether change table row background color when mouse hover
  33. // set true as default
  34. showHoverStyle = true,
  35. isLoading,
  36. headEditable = false,
  37. // editable heads required param, contains heads components and its value
  38. editHeads = [],
  39. // if table cell max width not be passed, table row will use 300px as default
  40. tableCellMaxWidth = '300px',
  41. handleSort,
  42. order,
  43. orderBy,
  44. rowDecorator = () => ({}),
  45. rowHeight,
  46. } = props;
  47. const hasData = rows && rows.length > 0;
  48. return (
  49. <TableContainer
  50. sx={theme => ({
  51. width: '100%',
  52. flexGrow: 1,
  53. color: theme.palette.text.primary,
  54. backgroundColor: theme.palette.background.paper,
  55. })}
  56. >
  57. <Box height="100%">
  58. <Table
  59. stickyHeader
  60. sx={{
  61. minWidth: '100%',
  62. height: hasData ? 'auto' : '100%',
  63. }}
  64. aria-labelledby="tableTitle"
  65. size="medium"
  66. aria-label="enhanced table"
  67. >
  68. {!headEditable ? (
  69. <EnhancedTableHead
  70. colDefinitions={colDefinitions}
  71. numSelected={selected.length}
  72. order={order}
  73. orderBy={orderBy}
  74. onSelectAllClick={onSelectedAll}
  75. handleSort={handleSort}
  76. rowCount={rows.length}
  77. openCheckBox={openCheckBox}
  78. disableSelect={disableSelect}
  79. />
  80. ) : (
  81. <EditableTableHead editHeads={editHeads} />
  82. )}
  83. {!isLoading ? (
  84. <TableBody>
  85. {hasData ? (
  86. rows.map((row, index) => {
  87. const isItemSelected = isSelected(row);
  88. const labelId = `enhanced-table-checkbox-${index}`;
  89. return (
  90. <TableRow
  91. hover={showHoverStyle}
  92. key={'row' + row[primaryKey] + index}
  93. onClick={event => onSelected(event, row)}
  94. aria-checked={isItemSelected}
  95. tabIndex={-1}
  96. selected={isItemSelected && !disableSelect}
  97. sx={
  98. [
  99. showHoverStyle && {
  100. '&:hover': {
  101. '& td': {
  102. background: 'inherit',
  103. },
  104. '& .hoverActionCell': {
  105. '& span': {
  106. opacity: 1,
  107. },
  108. },
  109. },
  110. },
  111. isItemSelected &&
  112. !disableSelect && {
  113. '& td': {
  114. background: 'inherit',
  115. },
  116. },
  117. rowDecorator(row),
  118. ].filter(Boolean) as any
  119. }
  120. >
  121. {openCheckBox && (
  122. <TableCell
  123. padding="checkbox"
  124. sx={theme => ({
  125. width: '38px',
  126. borderBottom: `1px solid ${theme.palette.divider}`,
  127. })}
  128. >
  129. <Checkbox
  130. checked={isItemSelected}
  131. color="primary"
  132. size="small"
  133. disabled={disableSelect}
  134. inputProps={{
  135. 'aria-labelledby': labelId,
  136. role: 'checkbox',
  137. }}
  138. />
  139. </TableCell>
  140. )}
  141. {colDefinitions.map((colDef, i) => {
  142. const { actionBarConfigs = [], needCopy = false } =
  143. colDef;
  144. const cellStyleFromDef = colDef.getStyle
  145. ? colDef.getStyle(row[colDef.id])
  146. : {};
  147. return colDef.showActionCell ? (
  148. <TableCell
  149. sx={
  150. [
  151. (theme: Theme) => ({
  152. borderBottom: `1px solid ${theme.palette.divider}`,
  153. '& p': {
  154. display: 'inline-block',
  155. verticalAlign: 'middle',
  156. overflow: 'hidden',
  157. textOverflow: 'ellipsis',
  158. whiteSpace: 'nowrap',
  159. maxWidth: tableCellMaxWidth,
  160. },
  161. }),
  162. (theme: Theme) => ({
  163. padding: theme.spacing(1, 1.5),
  164. }),
  165. colDef.isHoverAction && {
  166. transition: '0.2s all',
  167. padding: 0,
  168. width: '50px',
  169. '& span': {
  170. opacity: 0,
  171. },
  172. },
  173. cellStyleFromDef,
  174. ].filter(Boolean) as any
  175. }
  176. className={
  177. colDef.isHoverAction ? 'hoverActionCell' : ''
  178. } // Keep class for targeting in rowHoverStyles
  179. key={colDef.id}
  180. >
  181. <ActionBar
  182. configs={actionBarConfigs}
  183. isHoverType={colDef.isHoverAction}
  184. row={row}
  185. ></ActionBar>
  186. </TableCell>
  187. ) : (
  188. <TableCell
  189. key={'cell' + row[primaryKey] + i}
  190. align={colDef.align || 'left'}
  191. sx={
  192. [
  193. (theme: Theme) => ({
  194. overflow: 'hidden',
  195. borderBottom: `1px solid ${theme.palette.divider}`,
  196. '& p': {
  197. display: 'inline-block',
  198. verticalAlign: 'middle',
  199. overflow: 'hidden',
  200. textOverflow: 'ellipsis',
  201. whiteSpace: 'nowrap',
  202. maxWidth: tableCellMaxWidth,
  203. },
  204. }),
  205. (theme: Theme) => ({
  206. padding: theme.spacing(1),
  207. }),
  208. cellStyleFromDef,
  209. ].filter(Boolean) as any
  210. }
  211. >
  212. <Box
  213. sx={{
  214. display: 'flex',
  215. whiteSpace: 'nowrap',
  216. alignItems: 'center',
  217. minHeight: rowHeight - 16, // 16 is the padding of the table cell
  218. }}
  219. >
  220. {typeof row[colDef.id] !== 'undefined' && (
  221. <>
  222. {colDef.formatter ? (
  223. colDef.onClick ? (
  224. <Button
  225. color="primary"
  226. data-data={row[colDef.id]}
  227. data-index={index}
  228. size="small"
  229. onClick={e => {
  230. colDef.onClick &&
  231. colDef.onClick(e, row);
  232. }}
  233. >
  234. <Typography
  235. component="div"
  236. variant="body2"
  237. title={String(row[colDef.id])}
  238. >
  239. {colDef.formatter(
  240. row,
  241. row[colDef.id],
  242. i
  243. )}
  244. </Typography>
  245. </Button>
  246. ) : (
  247. <Typography
  248. component="div"
  249. title={String(row[colDef.id])}
  250. variant="body2"
  251. >
  252. {colDef.formatter(
  253. row,
  254. row[colDef.id],
  255. i
  256. )}
  257. </Typography>
  258. )
  259. ) : colDef.onClick ? (
  260. <Button
  261. color="primary"
  262. data-data={row[colDef.id]}
  263. data-index={index}
  264. size="small"
  265. onClick={e => {
  266. colDef.onClick &&
  267. colDef.onClick(e, row);
  268. }}
  269. >
  270. <Typography
  271. component="div"
  272. title={String(row[colDef.id])}
  273. variant="body2"
  274. >
  275. {row[colDef.id]}
  276. </Typography>
  277. </Button>
  278. ) : (
  279. <Typography
  280. component="div"
  281. title={String(row[colDef.id])}
  282. variant="body2"
  283. >
  284. {row[colDef.id]}
  285. </Typography>
  286. )}
  287. {needCopy && (
  288. <CopyButton
  289. copyValue={row[colDef.id]}
  290. size="small"
  291. sx={theme => ({
  292. '& svg': {
  293. fontSize: '13px',
  294. },
  295. marginLeft: theme.spacing(0.5),
  296. })}
  297. />
  298. )}
  299. </>
  300. )}
  301. </Box>
  302. </TableCell>
  303. );
  304. })}
  305. </TableRow>
  306. );
  307. })
  308. ) : (
  309. <TableRow>
  310. <TableCell
  311. sx={theme => ({
  312. paddingTop: theme.spacing(6),
  313. textAlign: 'center',
  314. letterSpacing: '0.5px',
  315. color: theme.palette.text.secondary,
  316. })}
  317. colSpan={
  318. openCheckBox
  319. ? colDefinitions.length + 1
  320. : colDefinitions.length
  321. }
  322. >
  323. {noData}
  324. </TableCell>
  325. </TableRow>
  326. )}
  327. </TableBody>
  328. ) : (
  329. <TableBody>
  330. <TableRow>
  331. <TableCell
  332. sx={theme => ({
  333. paddingTop: theme.spacing(6),
  334. textAlign: 'center',
  335. letterSpacing: '0.5px',
  336. color: theme.palette.text.secondary,
  337. })}
  338. colSpan={
  339. openCheckBox
  340. ? colDefinitions.length + 1
  341. : colDefinitions.length
  342. }
  343. >
  344. <LoadingTable />
  345. </TableCell>
  346. </TableRow>
  347. </TableBody>
  348. )}
  349. </Table>
  350. </Box>
  351. </TableContainer>
  352. );
  353. };
  354. export default EnhancedTable;