|
@@ -1,10 +1,205 @@
|
|
-import type { TableProps } from 'antd';
|
|
|
|
-import React from 'react';
|
|
|
|
|
|
+import React, { useEffect, useRef, useState } from "react";
|
|
|
|
+import "antd/dist/antd.css";
|
|
|
|
+import "./index.less";
|
|
|
|
+import { Table } from "antd";
|
|
|
|
+import type { TableProps } from "antd";
|
|
|
|
+import classNames from "classnames";
|
|
|
|
+import ResizeObserver from "rc-resize-observer";
|
|
|
|
+import type { ColumnsType } from "antd/es/table";
|
|
|
|
+import { VariableSizeGrid as Grid } from "react-window";
|
|
|
|
|
|
-const VirtualTable = <RecordType extends object>(props: TableProps<RecordType>) => {
|
|
|
|
-
|
|
|
|
|
|
+const VirtualTable = <RecordType extends object>(
|
|
|
|
+ props: TableProps<RecordType>
|
|
|
|
+) => {
|
|
|
|
+ const { columns, scroll, className } = props;
|
|
|
|
+ const [tableWidth, setTableWidth] = useState(0);
|
|
|
|
|
|
- return <div>1111</div>
|
|
|
|
|
|
+ // const widthColumnCount = columns!.filter(({ width }) => !width).length;
|
|
|
|
+ const mergedColumns = columns!.map((column) => {
|
|
|
|
+ if (column.width) {
|
|
|
|
+ return column;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ ...column,
|
|
|
|
+ // width: Math.floor(tableWidth / widthColumnCount)
|
|
|
|
+ width: 100
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const gridRef = useRef<any>();
|
|
|
|
+ const [connectObject] = useState<any>(() => {
|
|
|
|
+ const obj = {};
|
|
|
|
+ Object.defineProperty(obj, "scrollLeft", {
|
|
|
|
+ get: () => {
|
|
|
|
+ if (gridRef.current) {
|
|
|
|
+ return gridRef.current?.state?.scrollLeft;
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ },
|
|
|
|
+ set: (scrollLeft: number) => {
|
|
|
|
+ if (gridRef.current) {
|
|
|
|
+ gridRef.current.scrollTo({ scrollLeft });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return obj;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const resetVirtualGrid = () => {
|
|
|
|
+ gridRef.current?.resetAfterIndices({
|
|
|
|
+ columnIndex: 0,
|
|
|
|
+ shouldForceUpdate: true
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ useEffect(() => resetVirtualGrid, [tableWidth]);
|
|
|
|
+
|
|
|
|
+ const renderVirtualList = (
|
|
|
|
+ rawData: object[],
|
|
|
|
+ { scrollbarSize, ref, onScroll }: any
|
|
|
|
+ ) => {
|
|
|
|
+ ref.current = connectObject;
|
|
|
|
+ const totalHeight = rawData.length * 54;
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <Grid
|
|
|
|
+ ref={gridRef}
|
|
|
|
+ className="virtual-grid"
|
|
|
|
+ columnCount={mergedColumns.length}
|
|
|
|
+ columnWidth={(index: number) => {
|
|
|
|
+ const { width } = mergedColumns[index];
|
|
|
|
+ return totalHeight > (scroll!.y! as any) && index === mergedColumns.length - 1
|
|
|
|
+ ? (width as number) - scrollbarSize - 1
|
|
|
|
+ : (width as number);
|
|
|
|
+ }}
|
|
|
|
+ height={scroll!.y as number}
|
|
|
|
+ rowCount={rawData.length}
|
|
|
|
+ rowHeight={() => 100}
|
|
|
|
+ width={tableWidth}
|
|
|
|
+ onScroll={({ scrollLeft }: { scrollLeft: number }) => {
|
|
|
|
+ onScroll({ scrollLeft });
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ {({ columnIndex, rowIndex, style }: { columnIndex: number; rowIndex: number; style: React.CSSProperties; }) => {
|
|
|
|
+ const { render, dataIndex, align } = (mergedColumns as any)[columnIndex];
|
|
|
|
+ const myStyle = {
|
|
|
|
+ justifyContent:
|
|
|
|
+ align === "left"
|
|
|
|
+ ? "flex-start"
|
|
|
|
+ : align === "right"
|
|
|
|
+ ? "flex-end"
|
|
|
|
+ : align,
|
|
|
|
+ ...style
|
|
|
|
+ }
|
|
|
|
+ return (
|
|
|
|
+ <div
|
|
|
|
+ className={classNames("virtual-table-cell", {
|
|
|
|
+ "virtual-table-cell-last": columnIndex === mergedColumns.length - 1
|
|
|
|
+ })}
|
|
|
|
+ style={myStyle}
|
|
|
|
+ >
|
|
|
|
+ {render
|
|
|
|
+ ? render(
|
|
|
|
+ (rawData[rowIndex] as any)[dataIndex],
|
|
|
|
+ rawData[rowIndex] as any
|
|
|
|
+ )
|
|
|
|
+ : (rawData[rowIndex] as any)[dataIndex]}
|
|
|
|
+ </div>
|
|
|
|
+ );
|
|
|
|
+ }}
|
|
|
|
+ </Grid>
|
|
|
|
+ );
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <ResizeObserver
|
|
|
|
+ onResize={({ width }) => {
|
|
|
|
+ setTableWidth(width);
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ <Table
|
|
|
|
+ {...props}
|
|
|
|
+ bordered
|
|
|
|
+ className={`virtual-table all_table ${className ? className : ''}`}
|
|
|
|
+ columns={mergedColumns}
|
|
|
|
+ pagination={false}
|
|
|
|
+ components={{
|
|
|
|
+ body: renderVirtualList as any
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ </ResizeObserver>
|
|
|
|
+ );
|
|
};
|
|
};
|
|
|
|
|
|
-export default React.memo(VirtualTable)
|
|
|
|
|
|
+// Usage
|
|
|
|
+const columns: ColumnsType<any> = [
|
|
|
|
+ { title: <span>YYY</span>, dataIndex: "key", width: 150, align: "center", sorter: true },
|
|
|
|
+ { title: "B", dataIndex: "key" },
|
|
|
|
+ { title: "C", dataIndex: "key" },
|
|
|
|
+ { title: "D", dataIndex: "key" },
|
|
|
|
+ { title: "E", dataIndex: "key", width: 200 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100, align: "center" },
|
|
|
|
+ {
|
|
|
|
+ title: "dd11",
|
|
|
|
+ dataIndex: "key",
|
|
|
|
+ width: 100,
|
|
|
|
+ align: "center",
|
|
|
|
+ render: (a) => {
|
|
|
|
+ return <span>{a * 20}</span>;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 200 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 200 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 200 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 200 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "key", width: 100 },
|
|
|
|
+ { title: "F", dataIndex: "dd", width: 100 }
|
|
|
|
+];
|
|
|
|
+
|
|
|
|
+const data = Array.from({ length: 100000 }, (_, key) => ({
|
|
|
|
+ key,
|
|
|
|
+ dd: "啊大大撒旦啊实打实大苏打阿斯顿撒打算阿三打撒"
|
|
|
|
+}));
|
|
|
|
+
|
|
|
|
+const App1: React.FC = () => (
|
|
|
|
+ <VirtualTable
|
|
|
|
+ columns={columns}
|
|
|
|
+ dataSource={data}
|
|
|
|
+ scroll={{ y: 600, x: "100vw" }}
|
|
|
|
+ />
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+export default App1;
|