From 04447895145bc72f3ef6e6e54806dd2fa099658b Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 18 Sep 2019 15:29:45 +0800 Subject: [PATCH] feat: ScrollTo support key directly --- examples/basic.tsx | 17 ++++++++++++++--- src/List.tsx | 29 +++++++++++++++++++++++++---- src/utils/algorithmUtil.ts | 4 +++- src/utils/itemUtil.ts | 6 ++++-- tests/scroll.test.js | 20 +++++++++++--------- 5 files changed, 57 insertions(+), 19 deletions(-) diff --git a/examples/basic.tsx b/examples/basic.tsx index 9d3b651d..9ec0280c 100644 --- a/examples/basic.tsx +++ b/examples/basic.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import List from '../src/List'; interface Item { - id: number; + id: string; } const MyItem: React.FC = ({ id }, ref) => ( @@ -24,7 +24,7 @@ const MyItem: React.FC = ({ id }, ref) => ( const ForwardMyItem = React.forwardRef(MyItem); -class TestItem extends React.Component<{ id: number }, {}> { +class TestItem extends React.Component { state = {}; render() { @@ -35,7 +35,7 @@ class TestItem extends React.Component<{ id: number }, {}> { const data: Item[] = []; for (let i = 0; i < 100; i += 1) { data.push({ - id: i, + id: String(i), }); } @@ -127,6 +127,17 @@ const Demo = () => { > Scroll To 50 (auto) + ); diff --git a/src/List.tsx b/src/List.tsx index e07fa654..9fb2442f 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -12,6 +12,7 @@ import { getCompareItemRelativeTop, alignScrollTop, requireVirtual, + Key, } from './utils/itemUtil'; import { getIndexByStartLoc, findListDiffIndex } from './utils/algorithmUtil'; @@ -20,6 +21,17 @@ const ScrollStyle = { overflowAnchor: 'none', }; +type ScrollAlign = 'top' | 'bottom' | 'auto'; +type ScrollConfig = + | { + index: number; + align?: ScrollAlign; + } + | { + key: Key; + align?: ScrollAlign; + }; + export type RenderFunc = ( item: T, index: number, @@ -47,7 +59,7 @@ export interface ListProps extends React.HTMLAttributes { itemHeight?: number; /** If not match virtual scroll condition, Set List still use height of container. */ fullHeight?: boolean; - itemKey: string | ((item: T) => string); + itemKey: Key | ((item: T) => Key); component?: string | React.FC | React.ComponentClass; disabled?: boolean; @@ -417,7 +429,7 @@ class List extends React.Component, ListState> { return this.getItemKey(item, mergedProps); }; - public getItemKey = (item: T, props?: Partial>): string => { + public getItemKey = (item: T, props?: Partial>): Key => { const { itemKey } = props || this.props; return typeof itemKey === 'function' ? itemKey(item) : item[itemKey]; @@ -444,14 +456,23 @@ class List extends React.Component, ListState> { public scrollTo(scrollTop: number): void; - public scrollTo(config: { index: number; align?: 'top' | 'bottom' | 'auto' }): void; + public scrollTo(config: ScrollConfig): void; public scrollTo(arg0: any) { // Number top if (typeof arg0 === 'object') { const { isVirtual } = this.state; const { height, itemHeight, data } = this.props; - const { index, align = 'auto' } = arg0; + const { align = 'auto' } = arg0; + + let index = 0; + if ('index' in arg0) { + ({ index } = arg0); + } else if ('key' in arg0) { + const { key } = arg0; + index = data.findIndex(item => this.getItemKey(item) === key); + } + const visibleCount = Math.ceil(height / itemHeight); const item = data[index]; if (item) { diff --git a/src/utils/algorithmUtil.ts b/src/utils/algorithmUtil.ts index 4d655390..1ce79296 100644 --- a/src/utils/algorithmUtil.ts +++ b/src/utils/algorithmUtil.ts @@ -1,3 +1,5 @@ +import { Key } from './itemUtil'; + /** * Get index with specific start index one by one. e.g. * min: 3, max: 9, start: 6 @@ -39,7 +41,7 @@ export function getIndexByStartLoc(min: number, max: number, start: number, inde export function findListDiffIndex( originList: T[], targetList: T[], - getKey: (item: T) => string, + getKey: (item: T) => Key, ): { index: number; multiple: boolean } | null { const originLen = originList.length; const targetLen = targetList.length; diff --git a/src/utils/itemUtil.ts b/src/utils/itemUtil.ts index 7a92a80e..afad88c9 100644 --- a/src/utils/itemUtil.ts +++ b/src/utils/itemUtil.ts @@ -6,6 +6,8 @@ import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; */ export const GHOST_ITEM_KEY = '__rc_ghost_item__'; +export type Key = string | number; + interface LocationItemResult { /** Located item index */ index: number; @@ -108,7 +110,7 @@ interface ItemTopConfig { scrollPtg: number; clientHeight: number; - getItemKey: (index: number) => string; + getItemKey: (index: number) => Key; } /** @@ -139,7 +141,7 @@ interface CompareItemConfig { locatedItemRelativeTop: number; locatedItemIndex: number; compareItemIndex: number; - getItemKey: (index: number) => string; + getItemKey: (index: number) => Key; startIndex: number; endIndex: number; itemElementHeights: { [key: string]: number }; diff --git a/tests/scroll.test.js b/tests/scroll.test.js index 5e4d35ac..416601f6 100644 --- a/tests/scroll.test.js +++ b/tests/scroll.test.js @@ -4,7 +4,7 @@ import List from '../src'; import { spyElementPrototypes } from './utils/domHook'; function genData(count) { - return new Array(count).fill(null).map((_, id) => ({ id })); + return new Array(count).fill(null).map((_, index) => ({ id: String(index) })); } describe('List.Scroll', () => { @@ -60,7 +60,7 @@ describe('List.Scroll', () => { mockElement.mockRestore(); }); - function testPlots(type, props) { + function testPlots(type, scrollConfig, props) { describe(`${type} list`, () => { let listRef; let wrapper; @@ -81,11 +81,11 @@ describe('List.Scroll', () => { }); it('top', () => { - listRef.current.scrollTo({ index: 10, align: 'top' }); + listRef.current.scrollTo({ ...scrollConfig, align: 'top' }); expect(scrollTop).toEqual(200); }); it('bottom', () => { - listRef.current.scrollTo({ index: 10, align: 'bottom' }); + listRef.current.scrollTo({ ...scrollConfig, align: 'bottom' }); expect(scrollTop).toEqual(120); }); describe('auto', () => { @@ -96,7 +96,7 @@ describe('List.Scroll', () => { .last() .simulate('scroll'); expect(onScroll).toHaveBeenCalled(); - listRef.current.scrollTo({ index: 10, align: 'auto' }); + listRef.current.scrollTo({ ...scrollConfig, align: 'auto' }); expect(scrollTop).toEqual(200); }); it('lower of', () => { @@ -106,7 +106,7 @@ describe('List.Scroll', () => { .last() .simulate('scroll'); expect(onScroll).toHaveBeenCalled(); - listRef.current.scrollTo({ index: 10, align: 'auto' }); + listRef.current.scrollTo({ ...scrollConfig, align: 'auto' }); expect(scrollTop).toEqual(120); }); it('in range', () => { @@ -116,14 +116,16 @@ describe('List.Scroll', () => { .last() .simulate('scroll'); expect(onScroll).toHaveBeenCalled(); - listRef.current.scrollTo({ index: 10, align: 'auto' }); + listRef.current.scrollTo({ ...scrollConfig, align: 'auto' }); expect(scrollTop).toEqual(150); }); }); }); } - testPlots('virtual list'); - testPlots('raw list', { itemHeight: null }); + testPlots('virtual list', { index: 10 }); + testPlots('raw list', { index: 10 }, { itemHeight: null }); + testPlots('virtual list by key', { key: '10' }); + testPlots('raw list by key', { key: '10' }, { itemHeight: null }); }); });