nicecode-v2/packages/biz/src/infiniteList/InfiniteList.tsx

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