Skip to content

Commit 2fbe8b4

Browse files
authored
feat: support scrollTo (#1030)
* chore: init interface * feat: scrollTo * test: add test case * test: add test case
1 parent 7f5fda9 commit 2fbe8b4

File tree

9 files changed

+226
-55
lines changed

9 files changed

+226
-55
lines changed

docs/examples/scrollY.tsx

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import Table, { type Reference } from 'rc-table';
12
import React from 'react';
2-
import Table from 'rc-table';
33
import '../../assets/index.less';
44

55
const data = [];
@@ -12,51 +12,63 @@ for (let i = 0; i < 10; i += 1) {
1212
});
1313
}
1414

15-
class Demo extends React.Component {
16-
state = {
17-
showBody: true,
18-
};
15+
const Test = () => {
16+
const tblRef = React.useRef<Reference>();
17+
const [showBody, setShowBody] = React.useState(true);
1918

20-
toggleBody = () => {
21-
this.setState(({ showBody }) => ({ showBody: !showBody }));
19+
const toggleBody = () => {
20+
setShowBody(!showBody);
2221
};
2322

24-
render() {
25-
const { showBody } = this.state;
26-
const columns = [
27-
{ title: 'title1', key: 'a', dataIndex: 'a', width: 100 },
28-
{ id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
29-
{ title: 'title3', key: 'c', dataIndex: 'c', width: 200 },
30-
{
31-
title: (
32-
<a onClick={this.toggleBody} href="#">
33-
{showBody ? '隐藏' : '显示'}
34-
</a>
35-
),
36-
key: 'x',
37-
width: 200,
38-
render() {
39-
return <a href="#">Operations</a>;
40-
},
23+
const columns = [
24+
{ title: 'title1', key: 'a', dataIndex: 'a', width: 100 },
25+
{ id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
26+
{ title: 'title3', key: 'c', dataIndex: 'c', width: 200 },
27+
{
28+
title: (
29+
<a onClick={toggleBody} href="#">
30+
{showBody ? '隐藏' : '显示'}
31+
</a>
32+
),
33+
key: 'x',
34+
width: 200,
35+
render() {
36+
return <a href="#">Operations</a>;
4137
},
42-
];
43-
return (
38+
},
39+
];
40+
41+
return (
42+
<div>
43+
<h2>scroll body table</h2>
44+
<button
45+
onClick={() => {
46+
tblRef.current?.scrollTo({
47+
top: 9999,
48+
});
49+
}}
50+
>
51+
Scroll To End
52+
</button>
53+
<button
54+
onClick={() => {
55+
tblRef.current?.scrollTo({
56+
key: 9,
57+
});
58+
}}
59+
>
60+
Scroll To key 9
61+
</button>
4462
<Table
63+
reference={tblRef}
4564
columns={columns}
4665
data={data}
4766
scroll={{ y: 300 }}
4867
rowKey={record => record.key}
49-
onRow={(record, index) => ({ style: { backgroundColor: "red" } })}
68+
onRow={(record, index) => ({ style: { backgroundColor: 'red' } })}
5069
/>
51-
);
52-
}
53-
}
54-
55-
const Test = () => (
56-
<div>
57-
<h2>scroll body table</h2>
58-
<Demo />
59-
</div>
60-
);
70+
</div>
71+
);
72+
};
6173

6274
export default Test;

docs/examples/virtual.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import '../../assets/index.less';
33
import { VirtualTable } from '../../src';
4-
import type { ColumnsType } from '../../src/interface';
4+
import type { ColumnsType, Reference } from '../../src/interface';
55

66
interface RecordType {
77
a: string;
@@ -189,18 +189,34 @@ const data: RecordType[] = new Array(4 * 10000).fill(null).map((_, index) => ({
189189
}));
190190

191191
const Demo = () => {
192-
const [scrollY, setScrollY] = React.useState(true);
192+
const tblRef = React.useRef<Reference>();
193193

194194
return (
195195
<div style={{ width: 800, padding: `0 64px` }}>
196-
<label>
197-
<input type="checkbox" checked={scrollY} onChange={() => setScrollY(!scrollY)} />
198-
Scroll Y
199-
</label>
196+
<button
197+
onClick={() => {
198+
tblRef.current?.scrollTo({
199+
top: 9999999999999,
200+
});
201+
}}
202+
>
203+
Scroll To End
204+
</button>
205+
206+
<button
207+
onClick={() => {
208+
tblRef.current?.scrollTo({
209+
index: data.length - 1,
210+
});
211+
}}
212+
>
213+
Scroll To Key
214+
</button>
215+
200216
<VirtualTable
201217
columns={columns}
202218
// expandedRowRender={({ b, c }) => b || c}
203-
scroll={{ x: 1300, y: scrollY ? 200 : null }}
219+
scroll={{ x: 1300, y: 200 }}
204220
data={data}
205221
// data={[]}
206222
rowKey="indexKey"
@@ -220,6 +236,7 @@ const Demo = () => {
220236

221237
return mergedWidth;
222238
}}
239+
reference={tblRef}
223240
/>
224241
</div>
225242
);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"@rc-component/context": "^1.4.0",
5858
"classnames": "^2.2.5",
5959
"rc-resize-observer": "^1.1.0",
60-
"rc-util": "^5.36.0",
60+
"rc-util": "^5.37.0",
6161
"rc-virtual-list": "^3.11.1"
6262
},
6363
"devDependencies": {

src/Table.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import type {
6464
GetRowKey,
6565
LegacyExpandableProps,
6666
PanelRender,
67+
Reference,
6768
RowClassName,
6869
TableComponents,
6970
TableLayout,
@@ -120,6 +121,8 @@ export interface TableProps<RecordType = unknown>
120121

121122
sticky?: boolean | TableSticky;
122123

124+
reference?: React.Ref<Reference>;
125+
123126
// =================================== Internal ===================================
124127
/**
125128
* @private Internal usage, may remove by refactor. Should always use `columns` instead.
@@ -185,6 +188,7 @@ function Table<RecordType extends DefaultRecordType>(tableProps: TableProps<Reco
185188
scroll,
186189
tableLayout,
187190
direction,
191+
reference,
188192

189193
// Additional Part
190194
title,
@@ -304,11 +308,37 @@ function Table<RecordType extends DefaultRecordType>(tableProps: TableProps<Reco
304308
[columns, flattenColumns],
305309
);
306310

307-
// ====================== Scroll ======================
311+
// ======================= Refs =======================
308312
const fullTableRef = React.useRef<HTMLDivElement>();
309313
const scrollHeaderRef = React.useRef<HTMLDivElement>();
310314
const scrollBodyRef = React.useRef<HTMLDivElement>();
311315
const scrollBodyContainerRef = React.useRef<HTMLDivElement>();
316+
317+
React.useImperativeHandle(reference, () => {
318+
return {
319+
nativeElement: fullTableRef.current,
320+
scrollTo: config => {
321+
if (scrollBodyRef.current instanceof HTMLElement) {
322+
// Native scroll
323+
const { index, top, key } = config;
324+
325+
if (top) {
326+
scrollBodyRef.current?.scrollTo({
327+
top,
328+
});
329+
} else {
330+
const mergedKey = key ?? getRowKey(mergedData[index]);
331+
scrollBodyRef.current.querySelector(`[data-row-key="${mergedKey}"]`)?.scrollIntoView();
332+
}
333+
} else if ((scrollBodyRef.current as any)?.scrollTo) {
334+
// Pass to proxy
335+
(scrollBodyRef.current as any).scrollTo(config);
336+
}
337+
},
338+
};
339+
});
340+
341+
// ====================== Scroll ======================
312342
const scrollSummaryRef = React.useRef<HTMLDivElement>();
313343
const [pingedLeft, setPingedLeft] = React.useState(false);
314344
const [pingedRight, setPingedRight] = React.useState(false);

src/VirtualTable/BodyGrid.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as React from 'react';
55
import Cell from '../Cell';
66
import TableContext, { responseImmutable } from '../context/TableContext';
77
import useFlattenRecords, { type FlattenData } from '../hooks/useFlattenRecords';
8-
import type { ColumnType, OnCustomizeScroll } from '../interface';
8+
import type { ColumnType, OnCustomizeScroll, ScrollConfig } from '../interface';
99
import BodyLine from './BodyLine';
1010
import { GridContext, StaticContext } from './context';
1111

@@ -16,6 +16,7 @@ export interface GridProps<RecordType = any> {
1616

1717
export interface GridRef {
1818
scrollLeft: number;
19+
scrollTo?: (scrollConfig: ScrollConfig) => void;
1920
}
2021

2122
const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
@@ -70,7 +71,12 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
7071

7172
// =========================== Ref ============================
7273
React.useImperativeHandle(ref, () => {
73-
const obj = {} as GridRef;
74+
const obj = {
75+
scrollTo: (config: ScrollConfig) => {
76+
console.log('!!!!', config);
77+
listRef.current?.scrollTo(config);
78+
},
79+
} as unknown as GridRef;
7480

7581
Object.defineProperty(obj, 'scrollLeft', {
7682
get: () => listRef.current?.getScrollInfo().x || 0,

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { EXPAND_COLUMN, INTERNAL_HOOKS } from './constant';
22
import { FooterComponents as Summary } from './Footer';
3-
import VirtualTable, { genVirtualTable } from './VirtualTable';
4-
import type { VirtualTableProps } from './VirtualTable';
3+
import type { Reference } from './interface';
54
import Column from './sugar/Column';
65
import ColumnGroup from './sugar/ColumnGroup';
76
import type { TableProps } from './Table';
87
import Table, { genTable } from './Table';
98
import { INTERNAL_COL_DEFINE } from './utils/legacyUtil';
9+
import type { VirtualTableProps } from './VirtualTable';
10+
import VirtualTable, { genVirtualTable } from './VirtualTable';
1011

1112
export {
1213
genTable,
@@ -20,6 +21,7 @@ export {
2021
VirtualTable,
2122
genVirtualTable,
2223
type VirtualTableProps,
24+
type Reference,
2325
};
2426

2527
export default Table;

src/interface.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ export type DefaultRecordType = Record<string, any>;
2525

2626
export type TableLayout = 'auto' | 'fixed';
2727

28+
export type ScrollConfig = {
29+
index?: number;
30+
key?: Key;
31+
top?: number;
32+
};
33+
34+
export type Reference = {
35+
nativeElement: HTMLDivElement;
36+
scrollTo: (config: ScrollConfig) => void;
37+
};
38+
2839
// ==================== Row =====================
2940
export type RowClassName<RecordType> = (
3041
record: RecordType,
@@ -135,7 +146,7 @@ export type CustomizeScrollBody<RecordType> = (
135146
data: readonly RecordType[],
136147
info: {
137148
scrollbarSize: number;
138-
ref: React.Ref<{ scrollLeft: number }>;
149+
ref: React.Ref<{ scrollLeft: number; scrollTo?: (scrollConfig: ScrollConfig) => void }>;
139150
onScroll: OnCustomizeScroll;
140151
},
141152
) => React.ReactNode;

tests/Virtual.spec.tsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,26 @@ import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil';
44
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
55
import { resetWarned } from 'rc-util/lib/warning';
66
import React from 'react';
7-
import { VirtualTable, type VirtualTableProps } from '../src';
7+
import { VirtualTable, type Reference, type VirtualTableProps } from '../src';
8+
9+
global.scrollToConfig = null;
810

911
vi.mock('rc-virtual-list', async () => {
1012
const RealVirtualList = ((await vi.importActual('rc-virtual-list')) as any).default;
1113

12-
const WrapperVirtualList = React.forwardRef((props: any, ref) => (
13-
<RealVirtualList ref={ref} {...props} data-scroll-width={props.scrollWidth} />
14-
));
14+
const WrapperVirtualList = React.forwardRef((props: any, ref) => {
15+
const myRef = React.useRef(null);
16+
17+
React.useImperativeHandle(ref, () => ({
18+
...myRef.current,
19+
scrollTo: (config: any) => {
20+
global.scrollToConfig = config;
21+
return myRef.current.scrollTo(config);
22+
},
23+
}));
24+
25+
return <RealVirtualList ref={myRef} {...props} data-scroll-width={props.scrollWidth} />;
26+
});
1527

1628
return {
1729
default: WrapperVirtualList,
@@ -38,6 +50,7 @@ describe('Table.Virtual', () => {
3850

3951
beforeEach(() => {
4052
scrollLeftCalled = false;
53+
global.scrollToConfig = null;
4154
vi.useFakeTimers();
4255
resetWarned();
4356
});
@@ -295,4 +308,19 @@ describe('Table.Virtual', () => {
295308
bottom: '10px',
296309
});
297310
});
311+
312+
it('scrollTo should pass', async () => {
313+
const tblRef = React.createRef<Reference>();
314+
getTable({ reference: tblRef });
315+
316+
tblRef.current.scrollTo({
317+
index: 99,
318+
});
319+
320+
await waitFakeTimer();
321+
322+
expect(global.scrollToConfig).toEqual({
323+
index: 99,
324+
});
325+
});
298326
});

0 commit comments

Comments
 (0)