index.tsx 2.9 KB

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