Skip to content

Commit 61897f1

Browse files
committed
feat: horizontal virtual
1 parent b43c2f2 commit 61897f1

File tree

9 files changed

+255
-40
lines changed

9 files changed

+255
-40
lines changed

docs/examples/virtual.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import '../../assets/index.less';
33
import { VirtualTable } from '../../src';
44
import type { ColumnsType, Reference } from '../../src/interface';
@@ -162,15 +162,6 @@ const columns: ColumnsType = [
162162
},
163163
];
164164

165-
export function cleanOnCell(cols: any = []) {
166-
cols.forEach(col => {
167-
delete (col as any).onCell;
168-
169-
cleanOnCell((col as any).children);
170-
});
171-
}
172-
cleanOnCell(columns);
173-
174165
const data: RecordType[] = new Array(4 * 10000).fill(null).map((_, index) => ({
175166
a: `a${index}`,
176167
b: `b${index}`,
@@ -190,9 +181,24 @@ const data: RecordType[] = new Array(4 * 10000).fill(null).map((_, index) => ({
190181

191182
const Demo = () => {
192183
const tblRef = React.useRef<Reference>();
184+
const [enableColRowSpan, setEnableColRowSpan] = useState(false);
185+
186+
function cleanOnCell(cols: any = []) {
187+
cols.forEach(col => {
188+
delete (col as any).onCell;
189+
190+
cleanOnCell((col as any).children);
191+
});
192+
}
193+
if (!enableColRowSpan) {
194+
cleanOnCell(columns);
195+
}
193196

194197
return (
195198
<div style={{ width: 800, padding: `0 64px` }}>
199+
<button onClick={() => setEnableColRowSpan(!enableColRowSpan)}>
200+
Toggle RowSpan and ColSpan
201+
</button>
196202
<button
197203
onClick={() => {
198204
tblRef.current?.scrollTo({

src/Table.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,6 @@ function Table<RecordType extends DefaultRecordType>(
427427
target(scrollLeft);
428428
} else if (target.scrollLeft !== scrollLeft) {
429429
target.scrollLeft = scrollLeft;
430-
431430
// Delay to force scroll position if not sync
432431
// ref: https://github.com/ant-design/ant-design/issues/37179
433432
if (target.scrollLeft !== scrollLeft) {
@@ -447,7 +446,6 @@ function Table<RecordType extends DefaultRecordType>(
447446
const compareTarget = currentTarget || EMPTY_SCROLL_TARGET;
448447
if (!getScrollTarget() || getScrollTarget() === compareTarget) {
449448
setScrollTarget(compareTarget);
450-
451449
forceScroll(mergedScrollLeft, scrollHeaderRef.current);
452450
forceScroll(mergedScrollLeft, scrollBodyRef.current);
453451
forceScroll(mergedScrollLeft, scrollSummaryRef.current);

src/VirtualTable/BodyGrid.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
3131
childrenColumnName,
3232
emptyNode,
3333
scrollX,
34+
componentWidth,
3435
} = useContext(TableContext, [
3536
'flattenColumns',
3637
'onColumnResize',
@@ -40,11 +41,13 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
4041
'childrenColumnName',
4142
'emptyNode',
4243
'scrollX',
44+
'componentWidth',
4345
]);
4446
const {
4547
sticky,
4648
scrollY,
4749
listItemHeight,
50+
horizontalVirtual,
4851
getComponent,
4952
onScroll: onTablePropScroll,
5053
} = useContext(StaticContext);
@@ -69,6 +72,8 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
6972
[columnsWidth],
7073
);
7174

75+
const [scrollLeft, setScrollLeft] = React.useState(0);
76+
7277
React.useEffect(() => {
7378
columnsWidth.forEach(([key, width]) => {
7479
onColumnResize(key, width);
@@ -87,6 +92,12 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
8792
get: () => listRef.current?.getScrollInfo().x || 0,
8893

8994
set: (value: number) => {
95+
if (horizontalVirtual) {
96+
const max = (scrollX as number) - componentWidth;
97+
let left = Math.max(value, 0);
98+
left = Math.min(left, max);
99+
setScrollLeft(left);
100+
}
90101
listRef.current?.scrollTo({
91102
left: value,
92103
});
@@ -163,7 +174,6 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
163174
spanLines.push(i);
164175
}
165176
}
166-
167177
// Patch extra line on the page
168178
const nodes: React.ReactElement[] = spanLines.map(index => {
169179
const item = flattenData[index];
@@ -191,6 +201,8 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
191201
}}
192202
extra
193203
getHeight={getHeight}
204+
scrollLeft={scrollLeft}
205+
columnsWidth={columnsWidth}
194206
/>
195207
);
196208
});
@@ -235,6 +247,9 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
235247
component={wrapperComponent}
236248
scrollWidth={scrollX as number}
237249
onVirtualScroll={({ x }) => {
250+
if (horizontalVirtual) {
251+
setScrollLeft(x);
252+
}
238253
onScroll({
239254
scrollLeft: x,
240255
});
@@ -244,7 +259,16 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
244259
>
245260
{(item, index, itemProps) => {
246261
const rowKey = getRowKey(item.record, index);
247-
return <BodyLine data={item} rowKey={rowKey} index={index} {...itemProps} />;
262+
return (
263+
<BodyLine
264+
data={item}
265+
rowKey={rowKey}
266+
index={index}
267+
scrollLeft={scrollLeft}
268+
columnsWidth={columnsWidth}
269+
{...itemProps}
270+
/>
271+
);
248272
}}
249273
</VirtualList>
250274
);

src/VirtualTable/BodyLine.tsx

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,53 @@ import type { FlattenData } from '../hooks/useFlattenRecords';
77
import useRowInfo from '../hooks/useRowInfo';
88
import VirtualCell from './VirtualCell';
99
import { StaticContext } from './context';
10+
import { getCellProps } from '../Body/BodyRow';
11+
import VirtualRow from './VirtualRow';
1012

1113
export interface BodyLineProps<RecordType = any> {
1214
data: FlattenData<RecordType>;
1315
index: number;
1416
className?: string;
1517
style?: React.CSSProperties;
1618
rowKey: React.Key;
19+
scrollLeft: number;
20+
columnsWidth: [key: React.Key, width: number, total: number][];
1721

1822
/** Render cell only when it has `rowSpan > 1` */
1923
extra?: boolean;
2024
getHeight?: (rowSpan: number) => number;
2125
}
2226

2327
const BodyLine = React.forwardRef<HTMLDivElement, BodyLineProps>((props, ref) => {
24-
const { data, index, className, rowKey, style, extra, getHeight, ...restProps } = props;
28+
const {
29+
data,
30+
index,
31+
className,
32+
rowKey,
33+
style,
34+
extra,
35+
getHeight,
36+
scrollLeft,
37+
columnsWidth,
38+
...restProps
39+
} = props;
2540
const { record, indent, index: renderIndex } = data;
2641

2742
const { scrollX, flattenColumns, prefixCls, fixColumn, componentWidth } = useContext(
2843
TableContext,
2944
['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX'],
3045
);
31-
const { getComponent } = useContext(StaticContext, ['getComponent']);
46+
const { getComponent, horizontalVirtual } = useContext(StaticContext, [
47+
'getComponent',
48+
'horizontalVirtual',
49+
]);
3250

3351
const rowInfo = useRowInfo(record, rowKey, index, indent);
3452

53+
const cellPropsCollections = flattenColumns.map((column, colIndex) =>
54+
getCellProps(rowInfo, column, colIndex, indent, index),
55+
);
56+
3557
const RowComponent = getComponent(['body', 'row'], 'div');
3658
const cellComponent = getComponent(['body', 'cell'], 'div');
3759

@@ -87,6 +109,16 @@ const BodyLine = React.forwardRef<HTMLDivElement, BodyLineProps>((props, ref) =>
87109
rowStyle.pointerEvents = 'none';
88110
}
89111

112+
const shareCellProps = {
113+
index,
114+
renderIndex,
115+
inverse: extra,
116+
record,
117+
rowInfo,
118+
component: cellComponent,
119+
getHeight,
120+
};
121+
90122
const rowNode = (
91123
<RowComponent
92124
{...rowProps}
@@ -98,23 +130,23 @@ const BodyLine = React.forwardRef<HTMLDivElement, BodyLineProps>((props, ref) =>
98130
})}
99131
style={{ ...rowStyle, ...rowProps?.style }}
100132
>
101-
{flattenColumns.map((column, colIndex) => {
102-
return (
133+
{horizontalVirtual ? (
134+
<VirtualRow
135+
cellPropsCollections={cellPropsCollections}
136+
scrollLeft={scrollLeft}
137+
{...shareCellProps}
138+
/>
139+
) : (
140+
flattenColumns.map((column, colIndex) => (
103141
<VirtualCell
104142
key={colIndex}
105-
component={cellComponent}
106-
rowInfo={rowInfo}
107143
column={column}
108144
colIndex={colIndex}
109-
indent={indent}
110-
index={index}
111-
renderIndex={renderIndex}
112-
record={record}
113-
inverse={extra}
114-
getHeight={getHeight}
145+
cellProps={cellPropsCollections[colIndex]}
146+
{...shareCellProps}
115147
/>
116-
);
117-
})}
148+
))
149+
)}
118150
</RowComponent>
119151
);
120152

src/VirtualTable/VirtualCell.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useContext } from '@rc-component/context';
22
import classNames from 'classnames';
33
import * as React from 'react';
4-
import { getCellProps } from '../Body/BodyRow';
4+
import type { getCellProps } from '../Body/BodyRow';
55
import Cell from '../Cell';
66
import type useRowInfo from '../hooks/useRowInfo';
77
import type { ColumnType, CustomizeComponent } from '../interface';
@@ -11,12 +11,12 @@ export interface VirtualCellProps<RecordType> {
1111
rowInfo: ReturnType<typeof useRowInfo<RecordType>>;
1212
column: ColumnType<RecordType>;
1313
colIndex: number;
14-
indent: number;
1514
index: number;
1615
component?: CustomizeComponent;
1716
/** Used for `column.render` */
1817
renderIndex: number;
1918
record: RecordType;
19+
cellProps: ReturnType<typeof getCellProps>;
2020

2121
// Follow props is used for RowSpanVirtualCell only
2222
style?: React.CSSProperties;
@@ -41,28 +41,22 @@ function VirtualCell<RecordType = any>(props: VirtualCellProps<RecordType>) {
4141
rowInfo,
4242
column,
4343
colIndex,
44-
indent,
4544
index,
4645
component,
4746
renderIndex,
4847
record,
4948
style,
5049
className,
5150
inverse,
51+
cellProps,
5252
getHeight,
5353
} = props;
5454

5555
const { render, dataIndex, className: columnClassName, width: colWidth } = column;
5656

5757
const { columnsOffset } = useContext(GridContext, ['columnsOffset']);
5858

59-
const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps(
60-
rowInfo,
61-
column,
62-
colIndex,
63-
indent,
64-
index,
65-
);
59+
const { key, fixedInfo, appendCellNode, additionalCellProps } = cellProps;
6660

6761
const { style: cellStyle, colSpan = 1, rowSpan = 1 } = additionalCellProps;
6862

src/VirtualTable/VirtualRow.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as React from 'react';
2+
import { useContext } from '@rc-component/context';
3+
import TableContext from '../context/TableContext';
4+
import useHorizontalVirtual from './useHorizontalVirtual';
5+
import type { getCellProps } from '../Body/BodyRow';
6+
import VirtualCell from './VirtualCell';
7+
import type useRowInfo from '../hooks/useRowInfo';
8+
import type { CustomizeComponent } from '../interface';
9+
10+
export interface VirtualRowProps {
11+
cellPropsCollections: ReturnType<typeof getCellProps>[];
12+
scrollLeft: number;
13+
index: number;
14+
renderIndex: number;
15+
inverse: boolean;
16+
record: any;
17+
rowInfo: ReturnType<typeof useRowInfo>;
18+
component: CustomizeComponent;
19+
getHeight: (rowSpan: number) => number;
20+
}
21+
22+
export default function VirtualRow({
23+
cellPropsCollections,
24+
scrollLeft,
25+
...restProps
26+
}: VirtualRowProps) {
27+
const { flattenColumns } = useContext(TableContext, ['flattenColumns']);
28+
29+
const [startIndex, virtualOffset, showColumnIndexes] = useHorizontalVirtual(
30+
cellPropsCollections,
31+
scrollLeft,
32+
);
33+
34+
return showColumnIndexes
35+
.map(idx => flattenColumns[idx])
36+
.map((column, idx) => {
37+
const colIndex = showColumnIndexes[idx];
38+
return (
39+
<VirtualCell
40+
key={colIndex}
41+
column={column}
42+
colIndex={colIndex}
43+
style={startIndex === colIndex ? { marginLeft: virtualOffset } : {}}
44+
cellProps={cellPropsCollections[colIndex]}
45+
{...restProps}
46+
/>
47+
);
48+
});
49+
}

src/VirtualTable/context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface StaticContextProps {
55
scrollY: number;
66
listItemHeight: number;
77
sticky: boolean | TableSticky;
8+
horizontalVirtual: boolean;
89
getComponent: GetComponent;
910
onScroll?: React.UIEventHandler<HTMLDivElement>;
1011
}

0 commit comments

Comments
 (0)