Skip to content

Commit cefe7d8

Browse files
committed
feat: horizontal virtual
1 parent b43c2f2 commit cefe7d8

File tree

9 files changed

+343
-39
lines changed

9 files changed

+343
-39
lines changed

Diff for: docs/examples/virtual.tsx

+15-11
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';
@@ -11,7 +11,7 @@ interface RecordType {
1111
indexKey: string;
1212
}
1313

14-
const columns: ColumnsType = [
14+
const defaultColumns: ColumnsType = [
1515
// { title: 'title1', dataIndex: 'a', key: 'a', width: 100,},
1616
// { title: 'title1', dataIndex: 'a', key: 'a', width: 100, },
1717
{ title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' },
@@ -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,21 @@ 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: ColumnsType) {
187+
return cols?.map(({ onCell, ...col }: any) => {
188+
return { ...col, children: cleanOnCell(col.children) };
189+
});
190+
}
191+
192+
const columns = enableColRowSpan ? defaultColumns : cleanOnCell(defaultColumns);
193193

194194
return (
195195
<div style={{ width: 800, padding: `0 64px` }}>
196+
<button onClick={() => setEnableColRowSpan(!enableColRowSpan)}>
197+
Toggle RowSpan and ColSpan
198+
</button>
196199
<button
197200
onClick={() => {
198201
tblRef.current?.scrollTo({
@@ -214,6 +217,7 @@ const Demo = () => {
214217
</button>
215218

216219
<VirtualTable
220+
virtual={{ x: true }}
217221
columns={columns}
218222
// expandedRowRender={({ b, c }) => b || c}
219223
scroll={{ x: 1300, y: 200 }}

Diff for: src/VirtualTable/BodyGrid.tsx

+24-1
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
});
@@ -191,6 +202,7 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
191202
}}
192203
extra
193204
getHeight={getHeight}
205+
scrollLeft={scrollLeft}
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,15 @@ 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+
{...itemProps}
269+
/>
270+
);
248271
}}
249272
</VirtualList>
250273
);

Diff for: src/VirtualTable/BodyLine.tsx

+35-14
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,42 @@ 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;
1720

1821
/** Render cell only when it has `rowSpan > 1` */
1922
extra?: boolean;
2023
getHeight?: (rowSpan: number) => number;
2124
}
2225

2326
const BodyLine = React.forwardRef<HTMLDivElement, BodyLineProps>((props, ref) => {
24-
const { data, index, className, rowKey, style, extra, getHeight, ...restProps } = props;
27+
const { data, index, className, rowKey, style, extra, getHeight, scrollLeft, ...restProps } =
28+
props;
2529
const { record, indent, index: renderIndex } = data;
2630

2731
const { scrollX, flattenColumns, prefixCls, fixColumn, componentWidth } = useContext(
2832
TableContext,
2933
['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX'],
3034
);
31-
const { getComponent } = useContext(StaticContext, ['getComponent']);
35+
const { getComponent, horizontalVirtual } = useContext(StaticContext, [
36+
'getComponent',
37+
'horizontalVirtual',
38+
]);
3239

3340
const rowInfo = useRowInfo(record, rowKey, index, indent);
3441

42+
const cellPropsCollections = flattenColumns.map((column, colIndex) =>
43+
getCellProps(rowInfo, column, colIndex, indent, index),
44+
);
45+
3546
const RowComponent = getComponent(['body', 'row'], 'div');
3647
const cellComponent = getComponent(['body', 'cell'], 'div');
3748

@@ -87,6 +98,16 @@ const BodyLine = React.forwardRef<HTMLDivElement, BodyLineProps>((props, ref) =>
8798
rowStyle.pointerEvents = 'none';
8899
}
89100

101+
const shareCellProps = {
102+
index,
103+
renderIndex,
104+
inverse: extra,
105+
record,
106+
rowInfo,
107+
component: cellComponent,
108+
getHeight,
109+
};
110+
90111
const rowNode = (
91112
<RowComponent
92113
{...rowProps}
@@ -98,23 +119,23 @@ const BodyLine = React.forwardRef<HTMLDivElement, BodyLineProps>((props, ref) =>
98119
})}
99120
style={{ ...rowStyle, ...rowProps?.style }}
100121
>
101-
{flattenColumns.map((column, colIndex) => {
102-
return (
122+
{horizontalVirtual ? (
123+
<VirtualRow
124+
cellPropsCollections={cellPropsCollections}
125+
scrollLeft={scrollLeft}
126+
{...shareCellProps}
127+
/>
128+
) : (
129+
flattenColumns.map((column, colIndex) => (
103130
<VirtualCell
104131
key={colIndex}
105-
component={cellComponent}
106-
rowInfo={rowInfo}
107132
column={column}
108133
colIndex={colIndex}
109-
indent={indent}
110-
index={index}
111-
renderIndex={renderIndex}
112-
record={record}
113-
inverse={extra}
114-
getHeight={getHeight}
134+
cellProps={cellPropsCollections[colIndex]}
135+
{...shareCellProps}
115136
/>
116-
);
117-
})}
137+
))
138+
)}
118139
</RowComponent>
119140
);
120141

Diff for: src/VirtualTable/VirtualCell.tsx

+4-10
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

Diff for: src/VirtualTable/VirtualRow.tsx

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.map(colIndex => (
35+
<VirtualCell
36+
key={colIndex}
37+
column={flattenColumns[colIndex]}
38+
colIndex={colIndex}
39+
style={startIndex === colIndex ? { marginLeft: virtualOffset } : {}}
40+
cellProps={cellPropsCollections[colIndex]}
41+
{...restProps}
42+
/>
43+
));
44+
}

Diff for: src/VirtualTable/context.ts

+1
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
}

Diff for: src/VirtualTable/index.tsx

+14-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface VirtualTableProps<RecordType> extends Omit<TableProps<RecordTyp
2121
x?: number;
2222
y: number;
2323
};
24+
virtual?: { x?: boolean };
2425
listItemHeight?: number;
2526
}
2627

@@ -33,6 +34,7 @@ function VirtualTable<RecordType>(props: VirtualTableProps<RecordType>, ref: Rea
3334
className,
3435
listItemHeight,
3536
components,
37+
virtual,
3638
onScroll,
3739
} = props;
3840

@@ -63,10 +65,19 @@ function VirtualTable<RecordType>(props: VirtualTableProps<RecordType>, ref: Rea
6365
// Memo this
6466
const onInternalScroll = useEvent(onScroll);
6567

68+
const horizontalVirtual = !!virtual?.x;
69+
6670
// ========================= Context ==========================
6771
const context = React.useMemo(
68-
() => ({ sticky, scrollY, listItemHeight, getComponent, onScroll: onInternalScroll }),
69-
[sticky, scrollY, listItemHeight, getComponent, onInternalScroll],
72+
() => ({
73+
sticky,
74+
scrollY,
75+
listItemHeight,
76+
horizontalVirtual,
77+
getComponent,
78+
onScroll: onInternalScroll,
79+
}),
80+
[sticky, scrollY, listItemHeight, horizontalVirtual, getComponent, onInternalScroll],
7081
);
7182

7283
// ========================== Render ==========================
@@ -93,7 +104,7 @@ function VirtualTable<RecordType>(props: VirtualTableProps<RecordType>, ref: Rea
93104
}
94105

95106
export type ForwardGenericVirtualTable = (<RecordType>(
96-
props: TableProps<RecordType> & React.RefAttributes<Reference>,
107+
props: VirtualTableProps<RecordType> & React.RefAttributes<Reference>,
97108
) => React.ReactElement) & { displayName?: string };
98109

99110
const RefVirtualTable = React.forwardRef(VirtualTable) as ForwardGenericVirtualTable;

0 commit comments

Comments
 (0)