mirror of https://github.com/msgbyte/tailchat
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
83 lines
2.2 KiB
TypeScript
83 lines
2.2 KiB
TypeScript
import React, { useEffect, useMemo, useRef } from 'react';
|
|
import { ResizeWatcher } from './ResizeWatcher';
|
|
import { Scroller, ScrollerRef } from './Scroller';
|
|
import { useUpdate } from 'ahooks';
|
|
|
|
interface VirtualChatListProps<ItemType> {
|
|
className?: string;
|
|
style?: React.CSSProperties;
|
|
innerStyle?: React.CSSProperties;
|
|
getItemKey?: (item: ItemType) => string;
|
|
items: ItemType[];
|
|
itemContent: (item: ItemType, index: number) => React.ReactNode;
|
|
}
|
|
|
|
const defaultContainerStyle: React.CSSProperties = {
|
|
overflow: 'hidden',
|
|
};
|
|
|
|
const defaultInnerStyle: React.CSSProperties = {
|
|
height: '100%',
|
|
};
|
|
|
|
const scrollerStyle: React.CSSProperties = {
|
|
height: '100%',
|
|
};
|
|
|
|
const InternalVirtualChatList = <ItemType extends object>(
|
|
props: VirtualChatListProps<ItemType>
|
|
) => {
|
|
const scrollerRef = useRef<ScrollerRef>(null);
|
|
const itemHeightCache = useMemo(() => new Map<ItemType, number>(), []);
|
|
const forceUpdate = useUpdate();
|
|
const style = useMemo(
|
|
() => ({
|
|
...defaultContainerStyle,
|
|
...props.style,
|
|
}),
|
|
[props.style]
|
|
);
|
|
const innerStyle = useMemo(
|
|
() => ({
|
|
...defaultInnerStyle,
|
|
...props.innerStyle,
|
|
}),
|
|
[props.innerStyle]
|
|
);
|
|
|
|
useEffect(() => {
|
|
// 挂载后滚动到底部
|
|
scrollerRef.current?.scrollToBottom();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="virtual-chat-list" style={style}>
|
|
<Scroller ref={scrollerRef} style={scrollerStyle} innerStyle={innerStyle}>
|
|
{props.items.map((item, i) => (
|
|
<div
|
|
key={props.getItemKey ? props.getItemKey(item) : i}
|
|
className="virtual-chat-list__item"
|
|
style={{ height: itemHeightCache.get(item) }}
|
|
>
|
|
<ResizeWatcher
|
|
onResize={(size) => {
|
|
itemHeightCache.set(item, size.height);
|
|
forceUpdate();
|
|
}}
|
|
>
|
|
{props.itemContent(item, i)}
|
|
</ResizeWatcher>
|
|
</div>
|
|
))}
|
|
</Scroller>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
type VirtualChatListInterface = typeof InternalVirtualChatList & React.FC;
|
|
|
|
export const VirtualChatList: VirtualChatListInterface = React.memo(
|
|
InternalVirtualChatList
|
|
) as any;
|
|
VirtualChatList.displayName = 'VirtualChatList';
|