Skip to content

Commit 0444789

Browse files
committed
feat: ScrollTo support key directly
1 parent c564374 commit 0444789

File tree

5 files changed

+57
-19
lines changed

5 files changed

+57
-19
lines changed

examples/basic.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as React from 'react';
33
import List from '../src/List';
44

55
interface Item {
6-
id: number;
6+
id: string;
77
}
88

99
const MyItem: React.FC<Item> = ({ id }, ref) => (
@@ -24,7 +24,7 @@ const MyItem: React.FC<Item> = ({ id }, ref) => (
2424

2525
const ForwardMyItem = React.forwardRef(MyItem);
2626

27-
class TestItem extends React.Component<{ id: number }, {}> {
27+
class TestItem extends React.Component<Item, {}> {
2828
state = {};
2929

3030
render() {
@@ -35,7 +35,7 @@ class TestItem extends React.Component<{ id: number }, {}> {
3535
const data: Item[] = [];
3636
for (let i = 0; i < 100; i += 1) {
3737
data.push({
38-
id: i,
38+
id: String(i),
3939
});
4040
}
4141

@@ -127,6 +127,17 @@ const Demo = () => {
127127
>
128128
Scroll To 50 (auto)
129129
</button>
130+
<button
131+
type="button"
132+
onClick={() => {
133+
listRef.current.scrollTo({
134+
key: '50',
135+
align: 'auto',
136+
});
137+
}}
138+
>
139+
Scroll To key 50 (auto)
140+
</button>
130141
</div>
131142
</React.StrictMode>
132143
);

src/List.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getCompareItemRelativeTop,
1313
alignScrollTop,
1414
requireVirtual,
15+
Key,
1516
} from './utils/itemUtil';
1617
import { getIndexByStartLoc, findListDiffIndex } from './utils/algorithmUtil';
1718

@@ -20,6 +21,17 @@ const ScrollStyle = {
2021
overflowAnchor: 'none',
2122
};
2223

24+
type ScrollAlign = 'top' | 'bottom' | 'auto';
25+
type ScrollConfig =
26+
| {
27+
index: number;
28+
align?: ScrollAlign;
29+
}
30+
| {
31+
key: Key;
32+
align?: ScrollAlign;
33+
};
34+
2335
export type RenderFunc<T> = (
2436
item: T,
2537
index: number,
@@ -47,7 +59,7 @@ export interface ListProps<T> extends React.HTMLAttributes<any> {
4759
itemHeight?: number;
4860
/** If not match virtual scroll condition, Set List still use height of container. */
4961
fullHeight?: boolean;
50-
itemKey: string | ((item: T) => string);
62+
itemKey: Key | ((item: T) => Key);
5163
component?: string | React.FC<any> | React.ComponentClass<any>;
5264
disabled?: boolean;
5365

@@ -417,7 +429,7 @@ class List<T = any> extends React.Component<ListProps<T>, ListState<T>> {
417429
return this.getItemKey(item, mergedProps);
418430
};
419431

420-
public getItemKey = (item: T, props?: Partial<ListProps<T>>): string => {
432+
public getItemKey = (item: T, props?: Partial<ListProps<T>>): Key => {
421433
const { itemKey } = props || this.props;
422434

423435
return typeof itemKey === 'function' ? itemKey(item) : item[itemKey];
@@ -444,14 +456,23 @@ class List<T = any> extends React.Component<ListProps<T>, ListState<T>> {
444456

445457
public scrollTo(scrollTop: number): void;
446458

447-
public scrollTo(config: { index: number; align?: 'top' | 'bottom' | 'auto' }): void;
459+
public scrollTo(config: ScrollConfig): void;
448460

449461
public scrollTo(arg0: any) {
450462
// Number top
451463
if (typeof arg0 === 'object') {
452464
const { isVirtual } = this.state;
453465
const { height, itemHeight, data } = this.props;
454-
const { index, align = 'auto' } = arg0;
466+
const { align = 'auto' } = arg0;
467+
468+
let index = 0;
469+
if ('index' in arg0) {
470+
({ index } = arg0);
471+
} else if ('key' in arg0) {
472+
const { key } = arg0;
473+
index = data.findIndex(item => this.getItemKey(item) === key);
474+
}
475+
455476
const visibleCount = Math.ceil(height / itemHeight);
456477
const item = data[index];
457478
if (item) {

src/utils/algorithmUtil.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Key } from './itemUtil';
2+
13
/**
24
* Get index with specific start index one by one. e.g.
35
* min: 3, max: 9, start: 6
@@ -39,7 +41,7 @@ export function getIndexByStartLoc(min: number, max: number, start: number, inde
3941
export function findListDiffIndex<T>(
4042
originList: T[],
4143
targetList: T[],
42-
getKey: (item: T) => string,
44+
getKey: (item: T) => Key,
4345
): { index: number; multiple: boolean } | null {
4446
const originLen = originList.length;
4547
const targetLen = targetList.length;

src/utils/itemUtil.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import findDOMNode from 'rc-util/lib/Dom/findDOMNode';
66
*/
77
export const GHOST_ITEM_KEY = '__rc_ghost_item__';
88

9+
export type Key = string | number;
10+
911
interface LocationItemResult {
1012
/** Located item index */
1113
index: number;
@@ -108,7 +110,7 @@ interface ItemTopConfig {
108110
scrollPtg: number;
109111
clientHeight: number;
110112

111-
getItemKey: (index: number) => string;
113+
getItemKey: (index: number) => Key;
112114
}
113115

114116
/**
@@ -139,7 +141,7 @@ interface CompareItemConfig {
139141
locatedItemRelativeTop: number;
140142
locatedItemIndex: number;
141143
compareItemIndex: number;
142-
getItemKey: (index: number) => string;
144+
getItemKey: (index: number) => Key;
143145
startIndex: number;
144146
endIndex: number;
145147
itemElementHeights: { [key: string]: number };

tests/scroll.test.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import List from '../src';
44
import { spyElementPrototypes } from './utils/domHook';
55

66
function genData(count) {
7-
return new Array(count).fill(null).map((_, id) => ({ id }));
7+
return new Array(count).fill(null).map((_, index) => ({ id: String(index) }));
88
}
99

1010
describe('List.Scroll', () => {
@@ -60,7 +60,7 @@ describe('List.Scroll', () => {
6060
mockElement.mockRestore();
6161
});
6262

63-
function testPlots(type, props) {
63+
function testPlots(type, scrollConfig, props) {
6464
describe(`${type} list`, () => {
6565
let listRef;
6666
let wrapper;
@@ -81,11 +81,11 @@ describe('List.Scroll', () => {
8181
});
8282

8383
it('top', () => {
84-
listRef.current.scrollTo({ index: 10, align: 'top' });
84+
listRef.current.scrollTo({ ...scrollConfig, align: 'top' });
8585
expect(scrollTop).toEqual(200);
8686
});
8787
it('bottom', () => {
88-
listRef.current.scrollTo({ index: 10, align: 'bottom' });
88+
listRef.current.scrollTo({ ...scrollConfig, align: 'bottom' });
8989
expect(scrollTop).toEqual(120);
9090
});
9191
describe('auto', () => {
@@ -96,7 +96,7 @@ describe('List.Scroll', () => {
9696
.last()
9797
.simulate('scroll');
9898
expect(onScroll).toHaveBeenCalled();
99-
listRef.current.scrollTo({ index: 10, align: 'auto' });
99+
listRef.current.scrollTo({ ...scrollConfig, align: 'auto' });
100100
expect(scrollTop).toEqual(200);
101101
});
102102
it('lower of', () => {
@@ -106,7 +106,7 @@ describe('List.Scroll', () => {
106106
.last()
107107
.simulate('scroll');
108108
expect(onScroll).toHaveBeenCalled();
109-
listRef.current.scrollTo({ index: 10, align: 'auto' });
109+
listRef.current.scrollTo({ ...scrollConfig, align: 'auto' });
110110
expect(scrollTop).toEqual(120);
111111
});
112112
it('in range', () => {
@@ -116,14 +116,16 @@ describe('List.Scroll', () => {
116116
.last()
117117
.simulate('scroll');
118118
expect(onScroll).toHaveBeenCalled();
119-
listRef.current.scrollTo({ index: 10, align: 'auto' });
119+
listRef.current.scrollTo({ ...scrollConfig, align: 'auto' });
120120
expect(scrollTop).toEqual(150);
121121
});
122122
});
123123
});
124124
}
125125

126-
testPlots('virtual list');
127-
testPlots('raw list', { itemHeight: null });
126+
testPlots('virtual list', { index: 10 });
127+
testPlots('raw list', { index: 10 }, { itemHeight: null });
128+
testPlots('virtual list by key', { key: '10' });
129+
testPlots('raw list by key', { key: '10' }, { itemHeight: null });
128130
});
129131
});

0 commit comments

Comments
 (0)