AutoLoadingTable.tsx 28 KB

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