AutoLoadingTable.tsx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  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, { useState, useEffect, useCallback, useRef, useMemo, CSSProperties, MouseEvent } from "react";
  14. import Box from "@mui/material/Box";
  15. import MuiTable from "@mui/material/Table";
  16. import TableCell, { TableCellProps } from "@mui/material/TableCell";
  17. import TableContainer from "@mui/material/TableContainer";
  18. import TableHead from "@mui/material/TableHead";
  19. import TableRow from "@mui/material/TableRow";
  20. import TableSortLabel from "@mui/material/TableSortLabel";
  21. import Paper from "@mui/material/Paper";
  22. import { visuallyHidden } from "@mui/utils";
  23. import AutoSizer from "react-virtualized-auto-sizer";
  24. import { FixedSizeList, ListOnItemsRenderedProps } from "react-window";
  25. import InfiniteLoader from "react-window-infinite-loader";
  26. import Skeleton from "@mui/material/Skeleton";
  27. import IconButton from "@mui/material/IconButton";
  28. import Tooltip from "@mui/material/Tooltip";
  29. import AddIcon from "@mui/icons-material/Add";
  30. import DataSaverOn from "@mui/icons-material/DataSaverOn";
  31. import DataSaverOff from "@mui/icons-material/DataSaverOff";
  32. import Download from "@mui/icons-material/Download";
  33. import {
  34. createRequestInfiniteTableUpdateAction,
  35. createSendActionNameAction,
  36. FormatConfig,
  37. } from "../../context/taipyReducers";
  38. import {
  39. ColumnDesc,
  40. getsortByIndex,
  41. Order,
  42. TaipyTableProps,
  43. baseBoxSx,
  44. paperSx,
  45. tableSx,
  46. RowType,
  47. EditableCell,
  48. OnCellValidation,
  49. RowValue,
  50. EDIT_COL,
  51. OnRowDeletion,
  52. addDeleteColumn,
  53. headBoxSx,
  54. getClassName,
  55. LINE_STYLE,
  56. iconInRowSx,
  57. DEFAULT_SIZE,
  58. OnRowSelection,
  59. getRowIndex,
  60. getTooltip,
  61. defaultColumns,
  62. OnRowClick,
  63. DownloadAction,
  64. } from "./tableUtils";
  65. import {
  66. useClassNames,
  67. useDispatch,
  68. useDispatchRequestUpdateOnFirstRender,
  69. useDynamicJsonProperty,
  70. useDynamicProperty,
  71. useFormatConfig,
  72. useModule,
  73. } from "../../utils/hooks";
  74. import TableFilter, { FilterDesc } from "./TableFilter";
  75. import { getSuffixedClassNames, getUpdateVar } from "./utils";
  76. interface RowData {
  77. colsOrder: string[];
  78. columns: Record<string, ColumnDesc>;
  79. rows: RowType[];
  80. classes: Record<string, string>;
  81. cellProps: Partial<TableCellProps>[];
  82. isItemLoaded: (index: number) => boolean;
  83. selection: number[];
  84. formatConfig: FormatConfig;
  85. onValidation?: OnCellValidation;
  86. onDeletion?: OnRowDeletion;
  87. onRowSelection?: OnRowSelection;
  88. onRowClick?: OnRowClick;
  89. lineStyle?: string;
  90. nanValue?: string;
  91. compRows?: RowType[];
  92. }
  93. const Row = ({
  94. index,
  95. style,
  96. data: {
  97. colsOrder,
  98. columns,
  99. rows,
  100. classes,
  101. cellProps,
  102. isItemLoaded,
  103. selection,
  104. formatConfig,
  105. onValidation,
  106. onDeletion,
  107. onRowSelection,
  108. onRowClick,
  109. lineStyle,
  110. nanValue,
  111. compRows,
  112. },
  113. }: {
  114. index: number;
  115. style: CSSProperties;
  116. data: RowData;
  117. }) =>
  118. isItemLoaded(index) ? (
  119. <TableRow
  120. hover
  121. tabIndex={-1}
  122. key={"row" + index}
  123. component="div"
  124. sx={style}
  125. className={(classes && classes.row) + " " + getClassName(rows[index], lineStyle)}
  126. data-index={index}
  127. selected={selection.indexOf(index) > -1}
  128. onClick={onRowClick}
  129. >
  130. {colsOrder.map((col, cidx) => (
  131. <EditableCell
  132. key={"val" + index + "-" + cidx}
  133. className={getClassName(rows[index], columns[col].style, col)}
  134. colDesc={columns[col]}
  135. value={rows[index][col]}
  136. formatConfig={formatConfig}
  137. rowIndex={index}
  138. onValidation={!columns[col].notEditable ? onValidation : undefined}
  139. onDeletion={onDeletion}
  140. onSelection={onRowSelection}
  141. nanValue={columns[col].nanValue || nanValue}
  142. tableCellProps={cellProps[cidx]}
  143. tooltip={getTooltip(rows[index], columns[col].tooltip, col)}
  144. comp={compRows && compRows[index] && compRows[index][col]}
  145. />
  146. ))}
  147. </TableRow>
  148. ) : (
  149. <Skeleton sx={style} key={"Skeleton" + index} />
  150. );
  151. interface PromiseProps {
  152. resolve: () => void;
  153. reject: () => void;
  154. }
  155. interface key2Rows {
  156. key: string;
  157. promises: Record<number, PromiseProps>;
  158. }
  159. const getRowHeight = (size = DEFAULT_SIZE) => (size == DEFAULT_SIZE ? 37 : 54);
  160. const getCellSx = (width: string | number | undefined, size = DEFAULT_SIZE) => ({
  161. width: width,
  162. height: 22,
  163. padding: size == DEFAULT_SIZE ? "7px" : undefined,
  164. });
  165. const AutoLoadingTable = (props: TaipyTableProps) => {
  166. const {
  167. id,
  168. updateVarName,
  169. height = "80vh",
  170. width = "100%",
  171. updateVars,
  172. selected = [],
  173. pageSize = 100,
  174. defaultKey = "",
  175. onEdit = "",
  176. onDelete = "",
  177. onAdd = "",
  178. onAction = "",
  179. size = DEFAULT_SIZE,
  180. userData,
  181. downloadable = false,
  182. compare = false,
  183. onCompare = "",
  184. } = props;
  185. const [rows, setRows] = useState<RowType[]>([]);
  186. const [compRows, setCompRows] = useState<RowType[]>([]);
  187. const [rowCount, setRowCount] = useState(1000); // need something > 0 to bootstrap the infinite loader
  188. const [filteredCount, setFilteredCount] = useState(0);
  189. const dispatch = useDispatch();
  190. const page = useRef<key2Rows>({ key: defaultKey, promises: {} });
  191. const [orderBy, setOrderBy] = useState("");
  192. const [order, setOrder] = useState<Order>("asc");
  193. const [appliedFilters, setAppliedFilters] = useState<FilterDesc[]>([]);
  194. const [visibleStartIndex, setVisibleStartIndex] = useState(0);
  195. const [aggregates, setAggregates] = useState<string[]>([]);
  196. const infiniteLoaderRef = useRef<InfiniteLoader>(null);
  197. const headerRow = useRef<HTMLTableRowElement>(null);
  198. const formatConfig = useFormatConfig();
  199. const module = useModule();
  200. const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
  201. const active = useDynamicProperty(props.active, props.defaultActive, true);
  202. const editable = useDynamicProperty(props.editable, props.defaultEditable, true);
  203. const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
  204. const baseColumns = useDynamicJsonProperty(props.columns, props.defaultColumns, defaultColumns);
  205. const refresh = typeof props.data === "number";
  206. useEffect(() => {
  207. if (!refresh && props.data && page.current.key && props.data[page.current.key] !== undefined) {
  208. const newValue = props.data[page.current.key];
  209. const promise = page.current.promises[newValue.start];
  210. setRowCount(newValue.rowcount);
  211. setFilteredCount(
  212. newValue.fullrowcount && newValue.rowcount != newValue.fullrowcount
  213. ? newValue.fullrowcount - newValue.rowcount
  214. : 0
  215. );
  216. const nr = newValue.data as RowType[];
  217. if (Array.isArray(nr) && nr.length > newValue.start) {
  218. setRows(nr);
  219. newValue.comp && setCompRows(newValue.comp as RowType[])
  220. promise && promise.resolve();
  221. } else {
  222. promise && promise.reject();
  223. }
  224. delete page.current.promises[newValue.start];
  225. }
  226. }, [refresh, props.data]);
  227. useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars);
  228. const onSort = useCallback(
  229. (e: React.MouseEvent<HTMLElement>) => {
  230. const col = e.currentTarget.getAttribute("data-dfid");
  231. if (col) {
  232. const isAsc = orderBy === col && order === "asc";
  233. setOrder(isAsc ? "desc" : "asc");
  234. setOrderBy(col);
  235. setRows([]);
  236. setTimeout(() => infiniteLoaderRef.current?.resetloadMoreItemsCache(true), 1); // So that the state can be changed
  237. }
  238. },
  239. [orderBy, order]
  240. );
  241. useEffect(() => {
  242. if (refresh) {
  243. setRows([]);
  244. setTimeout(() => infiniteLoaderRef.current?.resetloadMoreItemsCache(true), 1); // So that the state can be changed
  245. }
  246. }, [refresh]);
  247. const onAggregate = useCallback((e: MouseEvent<HTMLElement>) => {
  248. const groupBy = e.currentTarget.getAttribute("data-dfid");
  249. if (groupBy) {
  250. setAggregates((ags) => {
  251. const nags = ags.filter((ag) => ag !== groupBy);
  252. if (ags.length == nags.length) {
  253. nags.push(groupBy);
  254. }
  255. return nags;
  256. });
  257. }
  258. e.stopPropagation();
  259. }, []);
  260. const [colsOrder, columns, styles, tooltips, handleNan, filter] = useMemo(() => {
  261. let hNan = !!props.nanValue;
  262. if (baseColumns) {
  263. try {
  264. let filter = false;
  265. Object.values(baseColumns).forEach((col) => {
  266. if (typeof col.filter != "boolean") {
  267. col.filter = !!props.filter;
  268. }
  269. filter = filter || col.filter;
  270. if (typeof col.notEditable != "boolean") {
  271. col.notEditable = !editable;
  272. }
  273. if (col.tooltip === undefined) {
  274. col.tooltip = props.tooltip;
  275. }
  276. });
  277. addDeleteColumn(
  278. (active && (onAdd || onDelete) ? 1 : 0) +
  279. (active && filter ? 1 : 0) +
  280. (active && downloadable ? 1 : 0),
  281. baseColumns
  282. );
  283. const colsOrder = Object.keys(baseColumns).sort(getsortByIndex(baseColumns));
  284. const styTt = colsOrder.reduce<Record<string, Record<string, string>>>((pv, col) => {
  285. if (baseColumns[col].style) {
  286. pv.styles = pv.styles || {};
  287. pv.styles[baseColumns[col].dfid] = baseColumns[col].style as string;
  288. }
  289. hNan = hNan || !!baseColumns[col].nanValue;
  290. if (baseColumns[col].tooltip) {
  291. pv.tooltips = pv.tooltips || {};
  292. pv.tooltips[baseColumns[col].dfid] = baseColumns[col].tooltip as string;
  293. }
  294. return pv;
  295. }, {});
  296. if (props.lineStyle) {
  297. styTt.styles = styTt.styles || {};
  298. styTt.styles[LINE_STYLE] = props.lineStyle;
  299. }
  300. return [colsOrder, baseColumns, styTt.styles, styTt.tooltips, hNan, filter];
  301. } catch (e) {
  302. console.info("ATable.columns: " + ((e as Error).message || e));
  303. }
  304. }
  305. return [
  306. [],
  307. {} as Record<string, ColumnDesc>,
  308. {} as Record<string, string>,
  309. {} as Record<string, string>,
  310. hNan,
  311. false,
  312. ];
  313. }, [
  314. active,
  315. editable,
  316. onAdd,
  317. onDelete,
  318. baseColumns,
  319. props.lineStyle,
  320. props.tooltip,
  321. props.nanValue,
  322. props.filter,
  323. downloadable,
  324. ]);
  325. const boxBodySx = useMemo(() => ({ height: height }), [height]);
  326. useEffect(() => {
  327. selected.length &&
  328. infiniteLoaderRef.current &&
  329. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  330. (infiniteLoaderRef.current as any)._listRef.scrollToItem(selected[0]);
  331. }, [selected]);
  332. useEffect(() => {
  333. if (headerRow.current) {
  334. Array.from(headerRow.current.cells).forEach((cell, idx) => {
  335. columns[colsOrder[idx]].widthHint = cell.offsetWidth;
  336. });
  337. }
  338. }, [columns, colsOrder]);
  339. const loadMoreItems = useCallback(
  340. (startIndex: number, stopIndex: number) => {
  341. if (page.current.promises[startIndex]) {
  342. page.current.promises[startIndex].reject();
  343. }
  344. return new Promise<void>((resolve, reject) => {
  345. const agg = aggregates.length
  346. ? colsOrder.reduce((pv, col, idx) => {
  347. if (aggregates.includes(columns[col].dfid)) {
  348. return pv + "-" + idx;
  349. }
  350. return pv;
  351. }, "-agg")
  352. : "";
  353. const cols = colsOrder.map((col) => columns[col].dfid).filter((c) => c != EDIT_COL);
  354. const afs = appliedFilters.filter((fd) => Object.values(columns).some((cd) => cd.dfid === fd.col));
  355. const key = `Infinite-${cols.join()}-${orderBy}-${order}${agg}${afs.map(
  356. (af) => `${af.col}${af.action}${af.value}`
  357. )}`;
  358. page.current = {
  359. key: key,
  360. promises: { ...page.current.promises, [startIndex]: { resolve: resolve, reject: reject } },
  361. };
  362. const applies = aggregates.length
  363. ? colsOrder.reduce<Record<string, unknown>>((pv, col) => {
  364. if (columns[col].apply) {
  365. pv[columns[col].dfid] = columns[col].apply;
  366. }
  367. return pv;
  368. }, {})
  369. : undefined;
  370. dispatch(
  371. createRequestInfiniteTableUpdateAction(
  372. updateVarName,
  373. id,
  374. module,
  375. cols,
  376. key,
  377. startIndex,
  378. stopIndex,
  379. orderBy,
  380. order,
  381. aggregates,
  382. applies,
  383. styles,
  384. tooltips,
  385. handleNan,
  386. afs,
  387. compare ? onCompare : undefined,
  388. updateVars && getUpdateVar(updateVars, "comparedatas")
  389. )
  390. );
  391. });
  392. },
  393. [
  394. aggregates,
  395. styles,
  396. tooltips,
  397. updateVarName,
  398. updateVars,
  399. orderBy,
  400. order,
  401. id,
  402. colsOrder,
  403. columns,
  404. handleNan,
  405. appliedFilters,
  406. compare,
  407. onCompare,
  408. dispatch,
  409. module,
  410. ]
  411. );
  412. const onAddRowClick = useCallback(
  413. () =>
  414. dispatch(
  415. createSendActionNameAction(updateVarName, module, {
  416. action: onAdd,
  417. index: visibleStartIndex,
  418. user_data: userData,
  419. })
  420. ),
  421. [visibleStartIndex, dispatch, updateVarName, onAdd, module, userData]
  422. );
  423. const onDownload = useCallback(
  424. () =>
  425. dispatch(
  426. createSendActionNameAction(updateVarName, module, {
  427. action: DownloadAction,
  428. user_data: userData,
  429. })
  430. ),
  431. [dispatch, updateVarName, module, userData]
  432. );
  433. const isItemLoaded = useCallback((index: number) => index < rows.length && !!rows[index], [rows]);
  434. const onCellValidation: OnCellValidation = useCallback(
  435. (value: RowValue, rowIndex: number, colName: string, userValue: string, tz?: string) =>
  436. dispatch(
  437. createSendActionNameAction(updateVarName, module, {
  438. action: onEdit,
  439. value: value,
  440. index: getRowIndex(rows[rowIndex], rowIndex),
  441. col: colName,
  442. user_value: userValue,
  443. tz: tz,
  444. user_data: userData,
  445. })
  446. ),
  447. [dispatch, updateVarName, onEdit, rows, module, userData]
  448. );
  449. const onRowDeletion: OnRowDeletion = useCallback(
  450. (rowIndex: number) =>
  451. dispatch(
  452. createSendActionNameAction(updateVarName, module, {
  453. action: onDelete,
  454. index: getRowIndex(rows[rowIndex], rowIndex),
  455. user_data: userData,
  456. })
  457. ),
  458. [dispatch, updateVarName, onDelete, rows, module, userData]
  459. );
  460. const onRowSelection: OnRowSelection = useCallback(
  461. (rowIndex: number, colName?: string, value?: string) =>
  462. dispatch(
  463. createSendActionNameAction(updateVarName, module, {
  464. action: onAction,
  465. index: getRowIndex(rows[rowIndex], rowIndex),
  466. col: colName === undefined ? null : colName,
  467. value,
  468. reason: value === undefined ? "click" : "button",
  469. user_data: userData,
  470. })
  471. ),
  472. [dispatch, updateVarName, onAction, rows, module, userData]
  473. );
  474. const onRowClick = useCallback(
  475. (e: MouseEvent<HTMLTableRowElement>) => {
  476. const { index } = e.currentTarget.dataset || {};
  477. const rowIndex = index === undefined ? NaN : Number(index);
  478. if (!isNaN(rowIndex)) {
  479. onRowSelection(rowIndex);
  480. }
  481. },
  482. [onRowSelection]
  483. );
  484. const onTaipyItemsRendered = useCallback(
  485. (onItemsR: (props: ListOnItemsRenderedProps) => undefined) =>
  486. ({ visibleStartIndex, visibleStopIndex }: { visibleStartIndex: number; visibleStopIndex: number }) => {
  487. setVisibleStartIndex(visibleStartIndex);
  488. onItemsR({ visibleStartIndex, visibleStopIndex } as ListOnItemsRenderedProps);
  489. },
  490. []
  491. );
  492. const rowData: RowData = useMemo(
  493. () => ({
  494. colsOrder: colsOrder,
  495. columns: columns,
  496. rows: rows,
  497. classes: {},
  498. cellProps: colsOrder.map((col) => ({
  499. sx: getCellSx(columns[col].width || columns[col].widthHint, size),
  500. component: "div",
  501. variant: "body",
  502. })),
  503. isItemLoaded: isItemLoaded,
  504. selection: selected,
  505. formatConfig: formatConfig,
  506. onValidation: active && onEdit ? onCellValidation : undefined,
  507. onDeletion: active && onDelete ? onRowDeletion : undefined,
  508. onRowSelection: active && onAction ? onRowSelection : undefined,
  509. onRowClick: active && onAction ? onRowClick : undefined,
  510. lineStyle: props.lineStyle,
  511. nanValue: props.nanValue,
  512. compRows: compRows,
  513. }),
  514. [
  515. rows,
  516. compRows,
  517. isItemLoaded,
  518. active,
  519. colsOrder,
  520. columns,
  521. selected,
  522. formatConfig,
  523. onEdit,
  524. onCellValidation,
  525. onDelete,
  526. onRowDeletion,
  527. onAction,
  528. onRowSelection,
  529. onRowClick,
  530. props.lineStyle,
  531. props.nanValue,
  532. size,
  533. ]
  534. );
  535. const boxSx = useMemo(() => ({ ...baseBoxSx, width: width }), [width]);
  536. return (
  537. <Box id={id} sx={boxSx} className={`${className} ${getSuffixedClassNames(className, "-autoloading")}`}>
  538. <Paper sx={paperSx}>
  539. <Tooltip title={hover || ""}>
  540. <TableContainer>
  541. <MuiTable sx={tableSx} aria-labelledby="tableTitle" size={size} stickyHeader={true}>
  542. <TableHead>
  543. <TableRow ref={headerRow}>
  544. {colsOrder.map((col, idx) => (
  545. <TableCell
  546. key={col + idx}
  547. sortDirection={orderBy === columns[col].dfid && order}
  548. sx={columns[col].width ? { width: columns[col].width } : {}}
  549. >
  550. {columns[col].dfid === EDIT_COL ? (
  551. [
  552. active && onAdd ? (
  553. <Tooltip title="Add a row" key="addARow">
  554. <IconButton
  555. onClick={onAddRowClick}
  556. size="small"
  557. sx={iconInRowSx}
  558. >
  559. <AddIcon fontSize="inherit" />
  560. </IconButton>
  561. </Tooltip>
  562. ) : null,
  563. active && filter ? (
  564. <TableFilter
  565. key="filter"
  566. columns={columns}
  567. colsOrder={colsOrder}
  568. onValidate={setAppliedFilters}
  569. appliedFilters={appliedFilters}
  570. className={className}
  571. filteredCount={filteredCount}
  572. />
  573. ) : null,
  574. active && downloadable ? (
  575. <Tooltip title="Download as CSV" key="downloadCsv">
  576. <IconButton
  577. onClick={onDownload}
  578. size="small"
  579. sx={iconInRowSx}
  580. >
  581. <Download fontSize="inherit" />
  582. </IconButton>
  583. </Tooltip>
  584. ) : null,
  585. ]
  586. ) : (
  587. <TableSortLabel
  588. active={orderBy === columns[col].dfid}
  589. direction={orderBy === columns[col].dfid ? order : "asc"}
  590. data-dfid={columns[col].dfid}
  591. onClick={onSort}
  592. disabled={!active}
  593. hideSortIcon={!active}
  594. >
  595. <Box sx={headBoxSx}>
  596. {columns[col].groupBy ? (
  597. <IconButton
  598. onClick={onAggregate}
  599. size="small"
  600. title="aggregate"
  601. data-dfid={columns[col].dfid}
  602. disabled={!active}
  603. sx={iconInRowSx}
  604. >
  605. {aggregates.includes(columns[col].dfid) ? (
  606. <DataSaverOff fontSize="inherit" />
  607. ) : (
  608. <DataSaverOn fontSize="inherit" />
  609. )}
  610. </IconButton>
  611. ) : null}
  612. {columns[col].title === undefined
  613. ? columns[col].dfid
  614. : columns[col].title}
  615. </Box>
  616. {orderBy === columns[col].dfid ? (
  617. <Box component="span" sx={visuallyHidden}>
  618. {order === "desc"
  619. ? "sorted descending"
  620. : "sorted ascending"}
  621. </Box>
  622. ) : null}
  623. </TableSortLabel>
  624. )}
  625. </TableCell>
  626. ))}
  627. </TableRow>
  628. </TableHead>
  629. </MuiTable>
  630. <Box sx={boxBodySx}>
  631. <AutoSizer>
  632. {({ height, width }) => (
  633. <InfiniteLoader
  634. ref={infiniteLoaderRef}
  635. isItemLoaded={isItemLoaded}
  636. itemCount={rowCount}
  637. loadMoreItems={loadMoreItems}
  638. minimumBatchSize={pageSize}
  639. >
  640. {({ onItemsRendered, ref }) => (
  641. <FixedSizeList
  642. height={height || 100}
  643. width={width || 100}
  644. itemCount={rowCount}
  645. itemSize={getRowHeight(size)}
  646. onItemsRendered={onTaipyItemsRendered(onItemsRendered)}
  647. ref={ref}
  648. itemData={rowData}
  649. >
  650. {Row}
  651. </FixedSizeList>
  652. )}
  653. </InfiniteLoader>
  654. )}
  655. </AutoSizer>
  656. </Box>
  657. </TableContainer>
  658. </Tooltip>
  659. </Paper>
  660. </Box>
  661. );
  662. };
  663. export default AutoLoadingTable;