index.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { Table } from 'antd';
  2. import type { TableProps } from 'antd';
  3. import classNames from 'classnames';
  4. import ResizeObserver from 'rc-resize-observer';
  5. import React, { useEffect, useRef, useState } from 'react';
  6. import { VariableSizeGrid as Grid } from 'react-window';
  7. const VirtualTable = <RecordType extends object>(props: TableProps<RecordType>) => {
  8. const { columns, scroll } = props;
  9. const [tableWidth, setTableWidth] = useState(0);
  10. const widthColumnCount = columns!.filter(({ width }) => !width).length;
  11. const mergedColumns = columns!.map(column => {
  12. if (column.width) {
  13. return column;
  14. }
  15. return {
  16. ...column,
  17. width: Math.floor(tableWidth / widthColumnCount),
  18. };
  19. });
  20. const gridRef = useRef<any>();
  21. const [connectObject] = useState<any>(() => {
  22. const obj = {};
  23. Object.defineProperty(obj, 'scrollLeft', {
  24. get: () => {
  25. if (gridRef.current) {
  26. return gridRef.current?.state?.scrollLeft;
  27. }
  28. return null;
  29. },
  30. set: (scrollLeft: number) => {
  31. if (gridRef.current) {
  32. gridRef.current.scrollTo({ scrollLeft });
  33. }
  34. },
  35. });
  36. return obj;
  37. });
  38. const resetVirtualGrid = () => {
  39. gridRef.current?.resetAfterIndices({
  40. columnIndex: 0,
  41. shouldForceUpdate: true,
  42. });
  43. };
  44. useEffect(() => resetVirtualGrid, [tableWidth]);
  45. const renderVirtualList = (rawData: object[], { scrollbarSize, ref, onScroll }: any) => {
  46. ref.current = connectObject;
  47. const totalHeight = rawData.length * 54;
  48. return (
  49. <Grid
  50. ref={gridRef}
  51. className="virtual-grid"
  52. columnCount={mergedColumns.length}
  53. columnWidth={(index: number) => {
  54. const { width } = mergedColumns[index];
  55. return totalHeight > (scroll!.y! as any) && index === mergedColumns.length - 1
  56. ? (width as number) - scrollbarSize - 1
  57. : (width as number);
  58. }}
  59. height={scroll!.y as number}
  60. rowCount={rawData.length}
  61. rowHeight={() => 54}
  62. width={tableWidth}
  63. onScroll={({ scrollLeft }: { scrollLeft: number }) => {
  64. onScroll({ scrollLeft });
  65. }}
  66. >
  67. {({
  68. columnIndex,
  69. rowIndex,
  70. style,
  71. }: {
  72. columnIndex: number;
  73. rowIndex: number;
  74. style: React.CSSProperties;
  75. }) => (
  76. <div
  77. className={classNames('virtual-table-cell', {
  78. 'virtual-table-cell-last': columnIndex === mergedColumns.length - 1,
  79. })}
  80. style={style}
  81. >
  82. {(rawData[rowIndex] as any)[(mergedColumns as any)[columnIndex].dataIndex]}
  83. </div>
  84. )}
  85. </Grid>
  86. );
  87. };
  88. return (
  89. <ResizeObserver
  90. onResize={({ width }) => {
  91. setTableWidth(width);
  92. }}
  93. >
  94. <Table
  95. {...props}
  96. className="virtual-table"
  97. columns={mergedColumns}
  98. pagination={false}
  99. components={{
  100. body: renderVirtualList as any,
  101. }}
  102. />
  103. </ResizeObserver>
  104. );
  105. };
  106. export default React.memo(VirtualTable)