AutoLoadingTable.tsx 29 KB

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