You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
5.0 KiB
151 lines
5.0 KiB
import * as React from 'react';
|
|
import { Key } from './utils/itemUtil';
|
|
declare type ScrollAlign = 'top' | 'bottom' | 'auto';
|
|
declare type ScrollConfig = {
|
|
index: number;
|
|
align?: ScrollAlign;
|
|
} | {
|
|
key: Key;
|
|
align?: ScrollAlign;
|
|
};
|
|
export declare type RenderFunc<T> = (item: T, index: number, props: {
|
|
style: React.CSSProperties;
|
|
}) => React.ReactNode;
|
|
export interface RelativeScroll {
|
|
itemIndex: number;
|
|
relativeTop: number;
|
|
}
|
|
export interface ScrollInfo {
|
|
scrollTop: number;
|
|
startItemTop: number;
|
|
startIndex: number;
|
|
}
|
|
export interface ListProps<T> extends React.HTMLAttributes<any> {
|
|
prefixCls?: string;
|
|
children: RenderFunc<T>;
|
|
data: T[];
|
|
height?: number;
|
|
itemHeight?: number;
|
|
/** If not match virtual scroll condition, Set List still use height of container. */
|
|
fullHeight?: boolean;
|
|
itemKey: Key | ((item: T) => Key);
|
|
component?: string | React.FC<any> | React.ComponentClass<any>;
|
|
/** Disable scroll check. Usually used on animation control */
|
|
disabled?: boolean;
|
|
/** Set `false` will always use real scroll instead of virtual one */
|
|
virtual?: boolean;
|
|
/** When `disabled`, trigger if changed item not render. */
|
|
onSkipRender?: () => void;
|
|
onScroll?: React.UIEventHandler<HTMLElement>;
|
|
}
|
|
declare type Status = 'NONE' | 'MEASURE_START' | 'MEASURE_DONE' | 'SWITCH_TO_VIRTUAL' | 'SWITCH_TO_RAW';
|
|
interface ListState<T> {
|
|
status: Status;
|
|
scrollTop: number | null;
|
|
/** Located item index */
|
|
itemIndex: number;
|
|
/** Located item bind its height percentage with the `scrollTop` */
|
|
itemOffsetPtg: number;
|
|
startIndex: number;
|
|
endIndex: number;
|
|
/**
|
|
* Calculated by `scrollTop`.
|
|
* We cache in the state since if `data` length change,
|
|
* we need revert back to the located item index.
|
|
*/
|
|
startItemTop: number;
|
|
/**
|
|
* Tell if is using virtual scroll
|
|
*/
|
|
isVirtual: boolean;
|
|
/**
|
|
* Only used when turn virtual list to raw list
|
|
*/
|
|
cacheScroll?: RelativeScroll;
|
|
/**
|
|
* Cache `data.length` to use for `disabled` status.
|
|
*/
|
|
itemCount: number;
|
|
}
|
|
/**
|
|
* We use class component here since typescript can not support generic in function component
|
|
*
|
|
* Virtual list display logic:
|
|
* 1. scroll / initialize trigger measure
|
|
* 2. Get location item of current `scrollTop`
|
|
* 3. [Render] Render visible items
|
|
* 4. Get all the visible items height
|
|
* 5. [Render] Update top item `margin-top` to fit the position
|
|
*
|
|
* Algorithm:
|
|
* We split scroll bar into equal slice. An item with whatever height occupy the same range slice.
|
|
* When `scrollTop` change,
|
|
* it will calculate the item percentage position and move item to the position.
|
|
* Then calculate other item position base on the located item.
|
|
*
|
|
* Concept:
|
|
*
|
|
* # located item
|
|
* The base position item which other items position calculate base on.
|
|
*/
|
|
declare class List<T = any> extends React.Component<ListProps<T>, ListState<T>> {
|
|
static defaultProps: {
|
|
itemHeight: number;
|
|
data: any[];
|
|
};
|
|
rafId: number;
|
|
listRef: React.RefObject<HTMLElement>;
|
|
itemElements: {
|
|
[index: number]: HTMLElement;
|
|
};
|
|
itemElementHeights: {
|
|
[index: number]: number;
|
|
};
|
|
/**
|
|
* Always point to the latest props if `disabled` is `false`
|
|
*/
|
|
cachedProps: Partial<ListProps<T>>;
|
|
/**
|
|
* Lock scroll process with `onScroll` event.
|
|
* This is used for `data` length change and `scrollTop` restore
|
|
*/
|
|
lockScroll: boolean;
|
|
constructor(props: ListProps<T>);
|
|
static getDerivedStateFromProps(nextProps: ListProps<any>): {
|
|
itemCount: number;
|
|
};
|
|
/**
|
|
* Phase 1: Initial should sync with default scroll top
|
|
*/
|
|
componentDidMount(): void;
|
|
/**
|
|
* Phase 4: Record used item height
|
|
* Phase 5: Trigger re-render to use correct position
|
|
*/
|
|
componentDidUpdate(): void;
|
|
componentWillUnmount(): void;
|
|
/**
|
|
* Phase 2: Trigger render since we should re-calculate current position.
|
|
*/
|
|
onScroll: React.UIEventHandler<HTMLElement>;
|
|
onRawScroll: React.UIEventHandler<HTMLElement>;
|
|
triggerOnScroll: React.UIEventHandler<HTMLElement>;
|
|
getIndexKey: (index: number, props?: Partial<ListProps<T>>) => string | number;
|
|
getItemKey: (item: T, props?: Partial<ListProps<T>>) => Key;
|
|
/**
|
|
* Collect current rendered dom element item heights
|
|
*/
|
|
collectItemHeights: (range?: {
|
|
startIndex: number;
|
|
endIndex: number;
|
|
}) => void;
|
|
scrollTo: (arg0: number | ScrollConfig) => void;
|
|
internalScrollTo(relativeScroll: RelativeScroll): void;
|
|
/**
|
|
* Phase 4: Render item and get all the visible items height
|
|
*/
|
|
renderChildren: (list: T[], startIndex: number, renderFunc: RenderFunc<T>) => React.ReactElement<any, string | ((props: any) => React.ReactElement<any, string | any | (new (props: any) => React.Component<any, any, any>)>) | (new (props: any) => React.Component<any, any, any>)>[];
|
|
render(): JSX.Element;
|
|
}
|
|
export default List;
|