Grid.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import { FC, MouseEvent, useRef, useEffect, useState } from 'react';
  2. import Typography from '@mui/material/Typography';
  3. import { Theme } from '@mui/material/styles/createTheme';
  4. import Grid from '@mui/material/Grid';
  5. import Breadcrumbs from '@mui/material/Breadcrumbs';
  6. import TablePagination from '@mui/material/TablePagination';
  7. import { makeStyles } from '@mui/styles';
  8. import CustomToolbar from './ToolBar';
  9. import Table from './Table';
  10. import { AttuGridType } from './Types';
  11. import { useTranslation } from 'react-i18next';
  12. import TablePaginationActions from './TablePaginationActions';
  13. const userStyle = makeStyles((theme: Theme) => ({
  14. loading: {
  15. height: '100%',
  16. display: 'flex',
  17. alignItems: 'center',
  18. justifyContent: 'center',
  19. padding: theme.spacing(20),
  20. width: '100%',
  21. },
  22. tableTitle: {
  23. '& .last': {
  24. color: 'rgba(0, 0, 0, 0.54)',
  25. },
  26. },
  27. noData: {
  28. pointerEvents: 'none',
  29. color: '#999',
  30. textAlign: 'center',
  31. height: '50vh',
  32. display: 'grid',
  33. justifyContent: 'center',
  34. alignContent: 'center',
  35. fontSize: '32px',
  36. },
  37. pagenation: {
  38. '& .MuiTablePagination-spacer': {
  39. display: 'none',
  40. },
  41. '& .MuiTablePagination-toolbar': {
  42. display: 'flex',
  43. justifyContent: 'space-between',
  44. paddingLeft: 8,
  45. },
  46. '& .MuiTablePagination-caption': {
  47. position: 'absolute',
  48. left: 0,
  49. bottom: 0,
  50. top: 0,
  51. display: 'flex',
  52. alignItems: 'center',
  53. '& .rows': {
  54. color: 'rgba(0,0,0,0.33)',
  55. marginLeft: theme.spacing(1),
  56. },
  57. },
  58. },
  59. noBottomPadding: {
  60. paddingBottom: '0 !important',
  61. display: 'flex',
  62. flexDirection: 'column',
  63. },
  64. wrapper: {
  65. height: '100%',
  66. },
  67. container: {
  68. flexWrap: 'nowrap',
  69. flexDirection: 'column',
  70. },
  71. }));
  72. /**
  73. *
  74. * @param rowCount required. totoal data count for pagination
  75. * @param rowsPerPage per page for pagination, default is 10
  76. * @param primaryKey required. The unique column for your data. use for checkbox and render key.
  77. * @param onPageChange handle page change
  78. * @param labelDisplayedRows Custom pagination label function, return string;
  79. * @param page current page for pagination
  80. * @param showToolbar control toolbar display. default is false
  81. * @param rows table data you want to render
  82. * @param colDefinitions Define how to render table heder.
  83. * @param isLoading table loading status
  84. * @param title Render breadcrumbs
  85. * @param openCheckBox control checkbox display. default is true
  86. * @param disableSelect disable table row select. default false
  87. * @param noData when table is empty, what tip we need to show.
  88. * @param showHoverStyle control table row hover style display
  89. * @param headEditable if true, user can edit header.
  90. * @param editHeads Only headEditable is true will render editHeads
  91. * @param tableCellMaxWidth Define table cell max width, default is 300
  92. * @param handlesort how to sort table, if it's undefined, then you can not sort table
  93. * @param order 'desc' | 'asc'. sort direction
  94. * @param order order by which table field
  95. * @returns
  96. */
  97. const AttuGrid: FC<AttuGridType> = props => {
  98. const classes = userStyle();
  99. const tableRef = useRef<HTMLDivElement | null>(null);
  100. const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
  101. // i18n
  102. const { t: commonTrans } = useTranslation();
  103. const gridTrans = commonTrans('grid');
  104. const {
  105. rowCount = 20,
  106. rowsPerPage = 10,
  107. tableHeaderHeight = 46,
  108. rowHeight = 43,
  109. pagerHeight = 52,
  110. primaryKey = 'id',
  111. showToolbar = false,
  112. toolbarConfigs = [],
  113. onPageChange = (
  114. e: MouseEvent<HTMLButtonElement> | null,
  115. nextPageNum: number
  116. ) => {
  117. console.log('nextPageNum', nextPageNum);
  118. },
  119. labelDisplayedRows,
  120. page = 0,
  121. rows = [],
  122. colDefinitions = [],
  123. isLoading = false,
  124. title,
  125. openCheckBox = true,
  126. disableSelect = false,
  127. noData = gridTrans.noData,
  128. showHoverStyle = true,
  129. headEditable = false,
  130. editHeads = [],
  131. selected = [],
  132. setSelected = () => {},
  133. setRowsPerPage = () => {},
  134. tableCellMaxWidth,
  135. handleSort,
  136. order,
  137. orderBy,
  138. showPagination = true,
  139. hideOnDisable,
  140. } = props;
  141. const _isSelected = (row: { [x: string]: any }) => {
  142. // console.log("row selected test", row[primaryKey]);
  143. return selected.some((s: any) => s[primaryKey] === row[primaryKey]);
  144. };
  145. const _onSelected = (event: React.MouseEvent, row: { [x: string]: any }) => {
  146. if (disableSelect) {
  147. return;
  148. }
  149. let newSelected: any[] = ([] as any[]).concat(selected);
  150. if (_isSelected(row)) {
  151. newSelected = newSelected.filter(s => s[primaryKey] !== row[primaryKey]);
  152. } else {
  153. newSelected.push(row);
  154. }
  155. setSelected(newSelected);
  156. };
  157. const _onSelectedAll = (event: React.ChangeEvent) => {
  158. if ((event.target as HTMLInputElement).checked) {
  159. const newSelecteds = rows;
  160. setSelected(newSelecteds);
  161. return;
  162. }
  163. setSelected([]);
  164. };
  165. const defaultLabelRows = ({ from = 0, to = 0, count = 0 }) => {
  166. return (
  167. <>
  168. <Typography variant="body2" component="span">
  169. {from} - {to}
  170. </Typography>
  171. <Typography variant="body2" className="rows" component="span">
  172. {gridTrans.of} {count} {gridTrans.rows}
  173. </Typography>
  174. </>
  175. );
  176. };
  177. const calculateRowCountAndPageSize = () => {
  178. if (tableRef.current && rowHeight > 0) {
  179. const containerHeight: number = tableRef.current.offsetHeight;
  180. const hasToolbar = toolbarConfigs.length > 0;
  181. const totalHeight =
  182. containerHeight -
  183. tableHeaderHeight -
  184. (showPagination ? pagerHeight : 0) -
  185. (hasToolbar ? 47 : 0);
  186. const rowCount = Math.floor(totalHeight / rowHeight);
  187. // fix loading mask
  188. setLoadingRowCount(rowCount);
  189. if (setRowsPerPage) {
  190. setRowsPerPage(rowCount);
  191. }
  192. }
  193. };
  194. useEffect(() => {
  195. // const timer = setTimeout(() => {
  196. calculateRowCountAndPageSize();
  197. // }, 16);
  198. // Add event listener for window resize
  199. window.addEventListener('resize', calculateRowCountAndPageSize);
  200. // Clean up event listener on unmount
  201. return () => {
  202. window.removeEventListener('resize', calculateRowCountAndPageSize);
  203. // clearTimeout(timer);
  204. };
  205. }, [tableHeaderHeight, rowHeight]);
  206. return (
  207. <Grid
  208. container
  209. classes={{ root: classes.wrapper, container: classes.container }}
  210. ref={tableRef}
  211. >
  212. {title && (
  213. <Grid item xs={12} className={classes.tableTitle}>
  214. <Breadcrumbs separator="›" aria-label="breadcrumb" role="breadcrumb">
  215. {title.map(
  216. (v: any, i: number) =>
  217. v && (
  218. <Typography
  219. key={v}
  220. className={i === title.length - 1 ? 'last' : ''}
  221. variant="h6"
  222. color="textPrimary"
  223. >
  224. {v}
  225. </Typography>
  226. )
  227. )}
  228. </Breadcrumbs>
  229. </Grid>
  230. )}
  231. {(showToolbar || toolbarConfigs.length > 0) && (
  232. <Grid item>
  233. <CustomToolbar
  234. toolbarConfigs={toolbarConfigs}
  235. selected={selected}
  236. hideOnDisable={hideOnDisable}
  237. ></CustomToolbar>
  238. </Grid>
  239. )}
  240. <Grid item xs={12} className={classes.noBottomPadding}>
  241. <Table
  242. loadingRowCount={loadingRowCount}
  243. openCheckBox={openCheckBox}
  244. primaryKey={primaryKey}
  245. rows={rows}
  246. selected={selected}
  247. colDefinitions={colDefinitions}
  248. onSelectedAll={_onSelectedAll}
  249. onSelected={_onSelected}
  250. isSelected={_isSelected}
  251. disableSelect={disableSelect}
  252. noData={noData}
  253. showHoverStyle={showHoverStyle}
  254. isLoading={isLoading}
  255. headEditable={headEditable}
  256. editHeads={editHeads}
  257. tableCellMaxWidth={tableCellMaxWidth}
  258. handleSort={handleSort}
  259. order={order}
  260. orderBy={orderBy}
  261. ></Table>
  262. {rowCount && showPagination ? (
  263. <TablePagination
  264. component="div"
  265. colSpan={3}
  266. count={rowCount}
  267. page={page}
  268. labelDisplayedRows={labelDisplayedRows || defaultLabelRows}
  269. rowsPerPage={rowsPerPage}
  270. rowsPerPageOptions={[]}
  271. onPageChange={onPageChange}
  272. className={classes.pagenation}
  273. ActionsComponent={TablePaginationActions}
  274. />
  275. ) : null}
  276. </Grid>
  277. </Grid>
  278. );
  279. };
  280. export default AttuGrid;