PaginatedTable.tsx 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, {
  14. ChangeEvent,
  15. CSSProperties,
  16. MouseEvent,
  17. useCallback,
  18. useEffect,
  19. useMemo,
  20. useRef,
  21. useState,
  22. } from "react";
  23. import AddIcon from "@mui/icons-material/Add";
  24. import DataSaverOff from "@mui/icons-material/DataSaverOff";
  25. import DataSaverOn from "@mui/icons-material/DataSaverOn";
  26. import Download from "@mui/icons-material/Download";
  27. import Box from "@mui/material/Box";
  28. import IconButton from "@mui/material/IconButton";
  29. import Paper from "@mui/material/Paper";
  30. import Skeleton from "@mui/material/Skeleton";
  31. import Table from "@mui/material/Table";
  32. import TableBody from "@mui/material/TableBody";
  33. import TableCell from "@mui/material/TableCell";
  34. import TableContainer from "@mui/material/TableContainer";
  35. import TableHead from "@mui/material/TableHead";
  36. import TablePagination from "@mui/material/TablePagination";
  37. import TableRow from "@mui/material/TableRow";
  38. import TableSortLabel from "@mui/material/TableSortLabel";
  39. import Tooltip from "@mui/material/Tooltip";
  40. import Typography from "@mui/material/Typography";
  41. import { visuallyHidden } from "@mui/utils";
  42. import { generateHeaderClassName } from "./tableUtils";
  43. import { createRequestTableUpdateAction, createSendActionNameAction } from "../../context/taipyReducers";
  44. import { emptyArray } from "../../utils";
  45. import {
  46. useClassNames,
  47. useDispatch,
  48. useDispatchRequestUpdateOnFirstRender,
  49. useDynamicJsonProperty,
  50. useDynamicProperty,
  51. useFormatConfig,
  52. useModule,
  53. } from "../../utils/hooks";
  54. import TableFilter from "./TableFilter";
  55. import {
  56. addActionColumn,
  57. baseBoxSx,
  58. ColumnDesc,
  59. DEFAULT_SIZE,
  60. defaultColumns,
  61. DownloadAction,
  62. EDIT_COL,
  63. EditableCell,
  64. FilterDesc,
  65. getClassName,
  66. getFormatFn,
  67. getPageKey,
  68. getRowIndex,
  69. getSortByIndex,
  70. getTooltip,
  71. headBoxSx,
  72. iconInRowSx,
  73. OnCellValidation,
  74. OnRowClick,
  75. OnRowDeletion,
  76. OnRowSelection,
  77. Order,
  78. PageSizeOptionsType,
  79. paperSx,
  80. ROW_CLASS_NAME,
  81. RowType,
  82. RowValue,
  83. tableSx,
  84. TaipyPaginatedTableProps,
  85. } from "./tableUtils";
  86. import { getComponentClassName } from "./TaipyStyle";
  87. import { getSuffixedClassNames, getUpdateVar } from "./utils";
  88. const loadingStyle: CSSProperties = { width: "100%", height: "3em", textAlign: "right", verticalAlign: "center" };
  89. const skeletonSx = { width: "100%", height: "3em" };
  90. const rowsPerPageOptions: PageSizeOptionsType = [10, 50, 100, 500];
  91. const PaginatedTable = (props: TaipyPaginatedTableProps) => {
  92. const {
  93. id,
  94. updateVarName,
  95. pageSizeOptions,
  96. allowAllRows = false,
  97. showAll = false,
  98. height,
  99. selected = emptyArray,
  100. updateVars,
  101. onEdit = "",
  102. onDelete = "",
  103. onAdd = "",
  104. onAction = "",
  105. width = "100%",
  106. size = DEFAULT_SIZE,
  107. userData,
  108. downloadable = false,
  109. compare = false,
  110. onCompare = "",
  111. useCheckbox = false,
  112. } = props;
  113. const pageSize = props.pageSize === undefined || props.pageSize < 1 ? 100 : Math.round(props.pageSize);
  114. const [value, setValue] = useState<Record<string, unknown>>({});
  115. const [startIndex, setStartIndex] = useState(0);
  116. const [rowsPerPage, setRowsPerPage] = useState(pageSize);
  117. const [order, setOrder] = useState<Order>("asc");
  118. const [orderBy, setOrderBy] = useState("");
  119. const [loading, setLoading] = useState(true);
  120. const [aggregates, setAggregates] = useState<string[]>([]);
  121. const [appliedFilters, setAppliedFilters] = useState<FilterDesc[]>([]);
  122. const dispatch = useDispatch();
  123. const pageKey = useRef("no-page");
  124. const selectedRowRef = useRef<HTMLTableRowElement | null>(null);
  125. const formatConfig = useFormatConfig();
  126. const module = useModule();
  127. const refresh = props.data?.__taipy_refresh !== undefined;
  128. const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
  129. const active = useDynamicProperty(props.active, props.defaultActive, true);
  130. const editable = useDynamicProperty(props.editable, props.defaultEditable, false);
  131. const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
  132. const baseColumns = useDynamicJsonProperty(props.columns, props.defaultColumns, defaultColumns);
  133. const [colsOrder, columns, cellClassNames, tooltips, formats, handleNan, filter, partialEditable, nbWidth] =
  134. useMemo(() => {
  135. let hNan = !!props.nanValue;
  136. if (baseColumns) {
  137. try {
  138. let filter = false;
  139. let partialEditable = editable;
  140. const newCols: Record<string, ColumnDesc> = {};
  141. Object.entries(baseColumns).forEach(([cId, cDesc]) => {
  142. const nDesc = (newCols[cId] = { ...cDesc });
  143. if (typeof nDesc.filter != "boolean") {
  144. nDesc.filter = !!props.filter;
  145. }
  146. filter = filter || nDesc.filter;
  147. if (typeof nDesc.notEditable == "boolean") {
  148. partialEditable = partialEditable || !nDesc.notEditable;
  149. } else {
  150. nDesc.notEditable = !editable;
  151. }
  152. if (nDesc.tooltip === undefined) {
  153. nDesc.tooltip = props.tooltip;
  154. }
  155. });
  156. addActionColumn(
  157. (active && partialEditable && (onAdd || onDelete) ? 1 : 0) +
  158. (active && filter ? 1 : 0) +
  159. (active && downloadable ? 1 : 0),
  160. newCols
  161. );
  162. const colsOrder = Object.keys(newCols).sort(getSortByIndex(newCols));
  163. let nbWidth = 0;
  164. const functions = colsOrder.reduce<Record<string, Record<string, string>>>((pv, col) => {
  165. if (newCols[col].className) {
  166. pv.classNames = pv.classNames || {};
  167. pv.classNames[newCols[col].dfid] = newCols[col].className;
  168. }
  169. hNan = hNan || !!newCols[col].nanValue;
  170. if (newCols[col].tooltip) {
  171. pv.tooltips = pv.tooltips || {};
  172. pv.tooltips[newCols[col].dfid] = newCols[col].tooltip;
  173. }
  174. if (newCols[col].formatFn) {
  175. pv.formats = pv.formats || {};
  176. pv.formats[newCols[col].dfid] = newCols[col].formatFn;
  177. }
  178. if (newCols[col].width !== undefined) {
  179. nbWidth++;
  180. }
  181. return pv;
  182. }, {});
  183. nbWidth = nbWidth ? colsOrder.length - nbWidth : 0;
  184. if (props.rowClassName) {
  185. functions.classNames = functions.classNames || {};
  186. functions.classNames[ROW_CLASS_NAME] = props.rowClassName;
  187. }
  188. return [
  189. colsOrder,
  190. newCols,
  191. functions.classNames,
  192. functions.tooltips,
  193. functions.formats,
  194. hNan,
  195. filter,
  196. partialEditable,
  197. nbWidth,
  198. ];
  199. } catch (e) {
  200. console.info("PaginatedTable.columns: ", (e as Error).message || e);
  201. }
  202. }
  203. return [
  204. [] as string[],
  205. {} as Record<string, ColumnDesc>,
  206. {} as Record<string, string>,
  207. {} as Record<string, string>,
  208. {} as Record<string, string>,
  209. hNan,
  210. false,
  211. false,
  212. 0,
  213. ];
  214. }, [
  215. active,
  216. editable,
  217. onAdd,
  218. onDelete,
  219. baseColumns,
  220. props.rowClassName,
  221. props.tooltip,
  222. props.nanValue,
  223. props.filter,
  224. downloadable,
  225. ]);
  226. useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars);
  227. /*
  228. TODO: If the 'selected' value is a negative number, it will lead to unexpected pagination behavior.
  229. For instance, if 'selected' is -1, the pagination will display from -99 to 0 and no data will be selected.
  230. Need to fix this issue.
  231. */
  232. useEffect(() => {
  233. if (selected.length) {
  234. if (selected[0] < startIndex || selected[0] > startIndex + rowsPerPage) {
  235. setLoading(true);
  236. setStartIndex(rowsPerPage * Math.floor(selected[0] / rowsPerPage));
  237. }
  238. }
  239. }, [selected, startIndex, rowsPerPage]);
  240. useEffect(() => {
  241. if (!refresh && props.data && props.data[pageKey.current] !== undefined) {
  242. setValue(props.data[pageKey.current]);
  243. setLoading(false);
  244. }
  245. }, [refresh, props.data]);
  246. useEffect(() => {
  247. const endIndex = showAll ? -1 : startIndex + rowsPerPage - 1;
  248. const cols = colsOrder.map((col) => columns[col].dfid).filter((c) => c != EDIT_COL);
  249. const afs = appliedFilters.filter((fd) => Object.values(columns).some((cd) => cd.dfid === fd.col));
  250. pageKey.current = getPageKey(
  251. columns,
  252. `${startIndex}-${endIndex}`,
  253. cols,
  254. orderBy,
  255. order,
  256. afs,
  257. aggregates,
  258. cellClassNames,
  259. tooltips,
  260. formats
  261. );
  262. if (refresh || !props.data || props.data[pageKey.current] === undefined) {
  263. setLoading(true);
  264. const applies = aggregates.length
  265. ? colsOrder.reduce<Record<string, unknown>>((pv, col) => {
  266. if (columns[col].apply) {
  267. pv[columns[col].dfid] = columns[col].apply;
  268. }
  269. return pv;
  270. }, {})
  271. : undefined;
  272. dispatch(
  273. createRequestTableUpdateAction(
  274. updateVarName,
  275. id,
  276. module,
  277. cols,
  278. pageKey.current,
  279. startIndex,
  280. endIndex,
  281. orderBy,
  282. order,
  283. aggregates,
  284. applies,
  285. cellClassNames,
  286. tooltips,
  287. formats,
  288. handleNan,
  289. afs,
  290. compare ? onCompare : undefined,
  291. updateVars && getUpdateVar(updateVars, "comparedatas"),
  292. typeof userData == "object"
  293. ? (userData as Record<string, Record<string, unknown>>).context
  294. : undefined
  295. )
  296. );
  297. } else {
  298. setValue(props.data[pageKey.current]);
  299. setLoading(false);
  300. }
  301. // eslint-disable-next-line react-hooks/exhaustive-deps
  302. }, [
  303. refresh,
  304. startIndex,
  305. aggregates,
  306. colsOrder,
  307. columns,
  308. showAll,
  309. cellClassNames,
  310. tooltips,
  311. formats,
  312. rowsPerPage,
  313. order,
  314. orderBy,
  315. updateVarName,
  316. updateVars,
  317. id,
  318. handleNan,
  319. appliedFilters,
  320. dispatch,
  321. module,
  322. compare,
  323. onCompare,
  324. userData,
  325. ]);
  326. const onSort = useCallback(
  327. (e: MouseEvent<HTMLElement>) => {
  328. const col = e.currentTarget.getAttribute("data-dfid");
  329. if (col) {
  330. const isAsc = orderBy === col && order === "asc";
  331. setOrder(isAsc ? "desc" : "asc");
  332. setOrderBy(col);
  333. }
  334. },
  335. [orderBy, order]
  336. );
  337. const handleChangePage = useCallback(
  338. (event: unknown, newPage: number) => {
  339. setStartIndex(newPage * rowsPerPage);
  340. },
  341. [rowsPerPage]
  342. );
  343. const handleChangeRowsPerPage = useCallback((event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
  344. setLoading(true);
  345. setRowsPerPage(parseInt(event.target.value, 10));
  346. setStartIndex(0);
  347. }, []);
  348. const onAggregate = useCallback((e: MouseEvent<HTMLElement>) => {
  349. const groupBy = e.currentTarget.getAttribute("data-dfid");
  350. if (groupBy) {
  351. setAggregates((ags) => {
  352. const nags = ags.filter((ag) => ag !== groupBy);
  353. if (ags.length == nags.length) {
  354. nags.push(groupBy);
  355. }
  356. return nags;
  357. });
  358. }
  359. e.stopPropagation();
  360. }, []);
  361. const onAddRowClick = useCallback(
  362. () =>
  363. dispatch(
  364. createSendActionNameAction(updateVarName, module, {
  365. action: onAdd,
  366. index: startIndex,
  367. user_data: userData,
  368. })
  369. ),
  370. [startIndex, dispatch, updateVarName, onAdd, module, userData]
  371. );
  372. const onDownload = useCallback(
  373. () =>
  374. dispatch(
  375. createSendActionNameAction(updateVarName, module, {
  376. action: DownloadAction,
  377. user_data: userData,
  378. })
  379. ),
  380. [dispatch, updateVarName, module, userData]
  381. );
  382. const tableContainerSx = useMemo(() => ({ maxHeight: height }), [height]);
  383. const pso = useMemo(() => {
  384. let psOptions = rowsPerPageOptions;
  385. if (pageSizeOptions) {
  386. try {
  387. psOptions = JSON.parse(pageSizeOptions);
  388. } catch (e) {
  389. console.log("PaginatedTable pageSizeOptions is wrong ", pageSizeOptions, e);
  390. }
  391. }
  392. if (
  393. pageSize > 0 &&
  394. !psOptions.some((ps) =>
  395. typeof ps === "number" ? ps === pageSize : typeof ps.value === "number" ? ps.value === pageSize : false
  396. )
  397. ) {
  398. psOptions.push({ value: pageSize, label: "" + pageSize });
  399. }
  400. if (allowAllRows) {
  401. psOptions.push({ value: -1, label: "All" });
  402. }
  403. psOptions.sort((a, b) => (typeof a === "number" ? a : a.value) - (typeof b === "number" ? b : b.value));
  404. return psOptions;
  405. }, [pageSizeOptions, allowAllRows, pageSize]);
  406. const { rows, rowCount, filteredCount, compRows } = useMemo(() => {
  407. const ret = { rows: undefined, rowCount: 0, filteredCount: 0, compRows: [] } as {
  408. rows?: RowType[];
  409. rowCount: number;
  410. filteredCount: number;
  411. compRows: RowType[];
  412. };
  413. if (value) {
  414. if (value.data) {
  415. ret.rows = value.data as RowType[];
  416. }
  417. if (value.rowcount) {
  418. ret.rowCount = value.rowcount as unknown as number;
  419. if (value.fullrowcount && value.rowcount != value.fullrowcount) {
  420. ret.filteredCount = (value.fullrowcount as unknown as number) - ret.rowCount;
  421. }
  422. }
  423. if (value.comp) {
  424. ret.compRows = value.comp as RowType[];
  425. }
  426. }
  427. return ret;
  428. }, [value]);
  429. const onCellValidation: OnCellValidation = useCallback(
  430. (value: RowValue, rowIndex: number, colName: string, userValue: string, tz?: string) =>
  431. dispatch(
  432. createSendActionNameAction(updateVarName, module, {
  433. action: onEdit,
  434. value: value,
  435. index: rows ? getRowIndex(rows[rowIndex], rowIndex, startIndex) : startIndex,
  436. col: colName,
  437. user_value: userValue,
  438. tz: tz,
  439. user_data: userData,
  440. })
  441. ),
  442. [dispatch, updateVarName, onEdit, rows, startIndex, module, userData]
  443. );
  444. const onRowDeletion: OnRowDeletion = useCallback(
  445. (rowIndex: number) =>
  446. dispatch(
  447. createSendActionNameAction(updateVarName, module, {
  448. action: onDelete,
  449. index: rows ? getRowIndex(rows[rowIndex], rowIndex, startIndex): startIndex,
  450. user_data: userData,
  451. })
  452. ),
  453. [dispatch, updateVarName, onDelete, rows, startIndex, module, userData]
  454. );
  455. const onRowSelection: OnRowSelection = useCallback(
  456. (rowIndex: number, colName?: string, value?: string) =>
  457. dispatch(
  458. createSendActionNameAction(updateVarName, module, {
  459. action: onAction,
  460. index: rows ? getRowIndex(rows[rowIndex], rowIndex, startIndex): startIndex,
  461. col: colName === undefined ? null : colName,
  462. value,
  463. reason: value === undefined ? "click" : "button",
  464. user_data: userData,
  465. })
  466. ),
  467. [dispatch, updateVarName, onAction, rows, startIndex, module, userData]
  468. );
  469. const onRowClick: OnRowClick = useCallback(
  470. (e: MouseEvent<HTMLTableRowElement>) => {
  471. const { index } = e.currentTarget.dataset || {};
  472. const rowIndex = index === undefined ? NaN : Number(index);
  473. if (!isNaN(rowIndex)) {
  474. onRowSelection(rowIndex);
  475. }
  476. },
  477. [onRowSelection]
  478. );
  479. const boxSx = useMemo(() => ({ ...baseBoxSx, width: width }), [width]);
  480. return (
  481. <Box
  482. id={id}
  483. sx={boxSx}
  484. className={`${className} ${getSuffixedClassNames(className, "-paginated")} ${getComponentClassName(
  485. props.children
  486. )}`}
  487. >
  488. <Paper sx={paperSx}>
  489. <Tooltip title={hover || ""}>
  490. <TableContainer sx={tableContainerSx}>
  491. <Table sx={tableSx} aria-labelledby="tableTitle" size={size} stickyHeader={true}>
  492. <TableHead>
  493. <TableRow>
  494. {colsOrder.map((col) => (
  495. <TableCell
  496. key={`head${columns[col].dfid}`}
  497. sortDirection={orderBy === columns[col].dfid && order}
  498. sx={
  499. columns[col].width
  500. ? { width: columns[col].width }
  501. : nbWidth
  502. ? { minWidth: `${100 / nbWidth}%` }
  503. : undefined
  504. }
  505. className={col === "EDIT_COL"
  506. ? getSuffixedClassNames(className, "-action")
  507. : getSuffixedClassNames(className, generateHeaderClassName(columns[col].dfid))
  508. }
  509. >
  510. {columns[col].dfid === EDIT_COL ? (
  511. [
  512. active && (editable || partialEditable) && onAdd ? (
  513. <Tooltip title="Add a row" key="addARow">
  514. <IconButton
  515. onClick={onAddRowClick}
  516. size="small"
  517. sx={iconInRowSx}
  518. >
  519. <AddIcon fontSize="inherit" />
  520. </IconButton>
  521. </Tooltip>
  522. ) : null,
  523. active && filter ? (
  524. <TableFilter
  525. key="filter"
  526. columns={columns}
  527. colsOrder={colsOrder}
  528. onValidate={setAppliedFilters}
  529. appliedFilters={appliedFilters}
  530. className={className}
  531. filteredCount={filteredCount}
  532. />
  533. ) : null,
  534. active && downloadable ? (
  535. <Tooltip title="Download as CSV" key="downloadCsv">
  536. <IconButton
  537. onClick={onDownload}
  538. size="small"
  539. sx={iconInRowSx}
  540. >
  541. <Download fontSize="inherit" />
  542. </IconButton>
  543. </Tooltip>
  544. ) : null,
  545. ]
  546. ) : (
  547. <TableSortLabel
  548. active={orderBy === columns[col].dfid}
  549. direction={orderBy === columns[col].dfid ? order : "asc"}
  550. data-dfid={columns[col].dfid}
  551. onClick={onSort}
  552. disabled={!active}
  553. hideSortIcon={!active}
  554. >
  555. <Box sx={headBoxSx}>
  556. {columns[col].groupBy ? (
  557. <IconButton
  558. onClick={onAggregate}
  559. size="small"
  560. title="aggregate"
  561. data-dfid={columns[col].dfid}
  562. disabled={!active}
  563. sx={iconInRowSx}
  564. >
  565. {aggregates.includes(columns[col].dfid) ? (
  566. <DataSaverOff fontSize="inherit" />
  567. ) : (
  568. <DataSaverOn fontSize="inherit" />
  569. )}
  570. </IconButton>
  571. ) : null}
  572. {columns[col].title === undefined
  573. ? columns[col].dfid
  574. : columns[col].title}
  575. </Box>
  576. {orderBy === columns[col].dfid ? (
  577. <Box component="span" sx={visuallyHidden}>
  578. {order === "desc"
  579. ? "sorted descending"
  580. : "sorted ascending"}
  581. </Box>
  582. ) : null}
  583. </TableSortLabel>
  584. )}
  585. </TableCell>
  586. ))}
  587. </TableRow>
  588. </TableHead>
  589. <TableBody>
  590. {rows?.map((row, index) => {
  591. const sel = selected.indexOf(index + startIndex);
  592. if (sel == 0) {
  593. Promise.resolve().then(
  594. () =>
  595. selectedRowRef.current?.scrollIntoView &&
  596. selectedRowRef.current.scrollIntoView({ block: "center" })
  597. );
  598. }
  599. return (
  600. <TableRow
  601. hover
  602. tabIndex={-1}
  603. key={`row${index}`}
  604. selected={sel > -1}
  605. ref={sel == 0 ? selectedRowRef : undefined}
  606. className={getClassName(row, props.rowClassName)}
  607. data-index={index}
  608. onClick={active && onAction ? onRowClick : undefined}
  609. >
  610. {colsOrder.map((col) => (
  611. <EditableCell
  612. key={`cell${index}${columns[col].dfid}`}
  613. className={getClassName(row, columns[col].className, col)}
  614. tableClassName={className}
  615. colDesc={columns[col]}
  616. value={row[col]}
  617. formattedVal={getFormatFn(row, columns[col].formatFn, col)}
  618. formatConfig={formatConfig}
  619. rowIndex={index}
  620. onValidation={
  621. active && !columns[col].notEditable && onEdit
  622. ? onCellValidation
  623. : undefined
  624. }
  625. onDeletion={
  626. active && (editable || partialEditable) && onDelete
  627. ? onRowDeletion
  628. : undefined
  629. }
  630. onSelection={active && onAction ? onRowSelection : undefined}
  631. nanValue={columns[col].nanValue || props.nanValue}
  632. tooltip={getTooltip(row, columns[col].tooltip, col)}
  633. comp={compRows && compRows[index] && compRows[index][col]}
  634. useCheckbox={useCheckbox}
  635. />
  636. ))}
  637. </TableRow>
  638. );
  639. })}
  640. {!rows &&
  641. loading &&
  642. Array.from(Array(30).keys(), (v, idx) => (
  643. <TableRow hover key={"rowSkel" + idx}>
  644. {colsOrder.map((col, cIdx) => (
  645. <TableCell key={"skel" + cIdx}>
  646. <Skeleton sx={skeletonSx} />
  647. </TableCell>
  648. ))}
  649. </TableRow>
  650. ))}
  651. </TableBody>
  652. </Table>
  653. </TableContainer>
  654. </Tooltip>
  655. {!showAll &&
  656. (loading ? (
  657. <Skeleton sx={loadingStyle}>
  658. <Typography>Loading...</Typography>
  659. </Skeleton>
  660. ) : (
  661. <TablePagination
  662. component="div"
  663. count={rowCount}
  664. page={startIndex / rowsPerPage}
  665. rowsPerPage={rowsPerPage}
  666. showFirstButton={true}
  667. showLastButton={true}
  668. rowsPerPageOptions={pso}
  669. onPageChange={handleChangePage}
  670. onRowsPerPageChange={handleChangeRowsPerPage}
  671. />
  672. ))}
  673. </Paper>
  674. {props.children}
  675. </Box>
  676. );
  677. };
  678. export default PaginatedTable;