113 lines
3.1 KiB
TypeScript
113 lines
3.1 KiB
TypeScript
/**
|
|
* Created by jiangzhixiong
|
|
*/
|
|
|
|
import React, { forwardRef, ReactNode, useContext, useEffect, useImperativeHandle, useRef } from 'react'
|
|
import { ConfigProvider, Spin, SpinProps } from '@zhst/meta';
|
|
import classNames from 'classnames';
|
|
import InfiniteScroll from 'react-infinite-scroll-component';
|
|
import { useSize } from '@zhst/hooks';
|
|
import './index.less'
|
|
|
|
const { ConfigContext } = ConfigProvider
|
|
|
|
export interface IData {
|
|
key: string;
|
|
title: string;
|
|
index?: number;
|
|
[k: string]: any;
|
|
}
|
|
|
|
export interface InfiniteListProps extends React.HtmlHTMLAttributes<HTMLDivElement> {
|
|
type?: 'custom' | 'auto'
|
|
prefixCls?: string;
|
|
height?: number;
|
|
itemRender?: (data?: IData, index?: number) => React.ReactNode
|
|
loading?: boolean; //
|
|
data: IData[];
|
|
targetId?: string; // 滚动列表 ID
|
|
loadMore?: (data?: any) => any;
|
|
params?: {
|
|
[key: string]: any;
|
|
}
|
|
hasMore: boolean;
|
|
endMessage?: ReactNode
|
|
loadingMessage?: ReactNode
|
|
loadingProps?: SpinProps
|
|
}
|
|
|
|
export interface InfiniteListRefProps {
|
|
scrollViewSize?: { width: number; height: number }
|
|
listSize?: { width: number; height: number }
|
|
}
|
|
|
|
const InfiniteList = forwardRef<InfiniteListRefProps, InfiniteListProps>((props, ref) => {
|
|
const {
|
|
prefixCls: customizePrefixCls,
|
|
height = 600,
|
|
loading,
|
|
type = 'auto',
|
|
loadingMessage = <p style={{ textAlign: 'center' }}>加载中...</p>,
|
|
targetId = 'scrollableDiv',
|
|
itemRender = (data) => <div>{data?.title}</div>,
|
|
hasMore,
|
|
loadMore,
|
|
data = [],
|
|
endMessage = <div style={{ textAlign: 'center' }} >没有更多数据了...🤐</div>,
|
|
style,
|
|
loadingProps,
|
|
className
|
|
} = props
|
|
const { getPrefixCls } = useContext(ConfigContext);
|
|
const componentName = getPrefixCls('biz-infinite-list', customizePrefixCls);
|
|
const listRef = useRef<HTMLDivElement>(null);
|
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
const scrollViewSize = useSize(listRef.current) || { width: 0, height: 0 }; // 无限滚动视窗大小
|
|
// @ts-ignore
|
|
const listSize = useSize(scrollRef.current?._infScroll) || { width: 0, height: 0 } // 无限滚动列表大小
|
|
|
|
useEffect(() => {
|
|
// 当数据不够一屏时继续加载
|
|
if (listSize.height < scrollViewSize.height) {
|
|
loadMore?.()
|
|
}
|
|
}, [listSize.height])
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
scrollViewSize,
|
|
listSize
|
|
}))
|
|
|
|
return (
|
|
<Spin spinning={loading} {...loadingProps}>
|
|
<div
|
|
id={targetId}
|
|
className={classNames(componentName, className)}
|
|
ref={listRef}
|
|
style={{
|
|
height,
|
|
overflow: 'auto',
|
|
...style
|
|
}}
|
|
>
|
|
<InfiniteScroll
|
|
// @ts-ignore
|
|
ref={scrollRef}
|
|
dataLength={data.length}
|
|
next={type === 'auto' ? loadMore! : () => {}}
|
|
hasMore={hasMore}
|
|
loader={loadingMessage}
|
|
endMessage={endMessage}
|
|
scrollableTarget={targetId}
|
|
>
|
|
{data?.map((item, idx) => (
|
|
itemRender?.({ ...item, index: idx})
|
|
))}
|
|
</InfiniteScroll>
|
|
</div>
|
|
</Spin>
|
|
)
|
|
})
|
|
|
|
export default InfiniteList
|