|
|
@@ -0,0 +1,105 @@
|
|
|
+import React, { useState, useEffect, useRef } from 'react'
|
|
|
+import { VariableSizeGrid as Grid } from 'react-window'
|
|
|
+import ResizeObserver from 'rc-resize-observer'
|
|
|
+import classNames from 'classnames'
|
|
|
+import { Table } from 'antd'
|
|
|
+import type { TableProps } from 'antd'
|
|
|
+
|
|
|
+function VirtualTable<RecordType extends Record<string, unknown>>(props: TableProps<RecordType>) {
|
|
|
+ const { columns, scroll } = props
|
|
|
+ const [state, setState] = useState({
|
|
|
+ tableWidth: 0,
|
|
|
+ current: 1
|
|
|
+ })
|
|
|
+ const widthColumnCount = columns.filter(({ width }) => !width).length
|
|
|
+ const mergedColumns = columns.map(column => {
|
|
|
+ if (column.width) {
|
|
|
+ return column
|
|
|
+ }
|
|
|
+
|
|
|
+ return { ...column, width: Math.floor(state.tableWidth / widthColumnCount) }
|
|
|
+ })
|
|
|
+ const gridRef = useRef()
|
|
|
+ const [connectObject] = useState(() => {
|
|
|
+ const obj = {}
|
|
|
+ Object.defineProperty(obj, 'scrollLeft', {
|
|
|
+ get: () => null,
|
|
|
+ set: scrollLeft => {
|
|
|
+ if (gridRef.current) {
|
|
|
+ gridRef.current.scrollTo({
|
|
|
+ scrollLeft
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return obj
|
|
|
+ })
|
|
|
+
|
|
|
+ const resetVirtualGrid = () => {
|
|
|
+ gridRef.current?.resetAfterIndices({
|
|
|
+ columnIndex: 0,
|
|
|
+ shouldForceUpdate: true
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ useEffect(() => resetVirtualGrid, [state.tableWidth])
|
|
|
+
|
|
|
+ const renderVirtualList = (rawData, { scrollbarSize, ref, onScroll }) => {
|
|
|
+ // eslint-disable-next-line no-param-reassign
|
|
|
+ ref.current = connectObject
|
|
|
+ const totalHeight = rawData.length * 54
|
|
|
+ return (
|
|
|
+ <Grid
|
|
|
+ ref={gridRef}
|
|
|
+ className="virtual-grid"
|
|
|
+ columnCount={mergedColumns.length}
|
|
|
+ columnWidth={index => {
|
|
|
+ const { width } = mergedColumns[index]
|
|
|
+ return totalHeight > scroll.y && index === mergedColumns.length - 1
|
|
|
+ ? width - scrollbarSize - 1
|
|
|
+ : width
|
|
|
+ }}
|
|
|
+ height={scroll.y}
|
|
|
+ rowCount={rawData.length}
|
|
|
+ rowHeight={() => 54}
|
|
|
+ width={state.tableWidth}
|
|
|
+ onScroll={({ scrollLeft }) => {
|
|
|
+ onScroll({
|
|
|
+ scrollLeft
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {({ columnIndex, rowIndex, style }) => (
|
|
|
+ <div
|
|
|
+ className={classNames('virtual-table-cell', {
|
|
|
+ 'virtual-table-cell-last': columnIndex === mergedColumns.length - 1
|
|
|
+ })}
|
|
|
+ style={style}
|
|
|
+ >
|
|
|
+ {rawData[rowIndex][mergedColumns[columnIndex].dataIndex]}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </Grid>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <ResizeObserver
|
|
|
+ onResize={({ width }) => {
|
|
|
+ setState({ ...state, tableWidth: width })
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Table
|
|
|
+ {...props}
|
|
|
+ className="virtual-table"
|
|
|
+ columns={mergedColumns}
|
|
|
+ pagination={false}
|
|
|
+ components={{
|
|
|
+ body: renderVirtualList
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </ResizeObserver>
|
|
|
+ )
|
|
|
+} // Usage
|
|
|
+
|
|
|
+export default VirtualTable
|