Update InfiniteScroll, fix chat history scroll

This commit is contained in:
Bill 2023-01-03 22:33:18 -05:00
parent 16e583594a
commit 558067c531
5 changed files with 22 additions and 14 deletions

View File

@ -1,30 +1,37 @@
import { useVirtual } from '@tanstack/react-virtual'; import { useVirtual } from '@tanstack/react-virtual';
import { FC, Fragment, ReactElement, useRef } from 'react'; import { FC, Fragment, ReactElement, useEffect, useRef, useState } from 'react';
import { Base } from './Base'; import { Base } from './Base';
interface InfiniteScrollProps<T = any> interface InfiniteScrollProps<T = any>
{ {
rows: T[]; rows: T[];
estimateSize: number;
overscan?: number; overscan?: number;
scrollToBottom?: boolean;
rowRender: (row: T) => ReactElement; rowRender: (row: T) => ReactElement;
} }
export const InfiniteScroll: FC<InfiniteScrollProps> = props => export const InfiniteScroll: FC<InfiniteScrollProps> = props =>
{ {
const { rows = [], estimateSize = 0, overscan = 5, rowRender = null } = props; const { rows = [], overscan = 5, scrollToBottom = false, rowRender = null } = props;
const [ scrollIndex, setScrollIndex ] = useState<number>(rows.length - 1);
const elementRef = useRef<HTMLDivElement>(null); const elementRef = useRef<HTMLDivElement>(null);
const rowVirtualizer = useVirtual({ const { virtualItems = [], totalSize = 0, scrollToIndex = null } = useVirtual({
parentRef: elementRef, parentRef: elementRef,
size: rows.length, size: rows.length,
overscan: 5 overscan
}); });
const { virtualItems, totalSize, } = rowVirtualizer;
const paddingTop = (virtualItems.length > 0) ? (virtualItems?.[0]?.start || 0) : 0 const paddingTop = (virtualItems.length > 0) ? (virtualItems?.[0]?.start || 0) : 0
const paddingBottom = (virtualItems.length > 0) ? (totalSize - (virtualItems?.[virtualItems.length - 1]?.end || 0)) : 0; const paddingBottom = (virtualItems.length > 0) ? (totalSize - (virtualItems?.[virtualItems.length - 1]?.end || 0)) : 0;
useEffect(() =>
{
if(!scrollToBottom) return;
scrollToIndex(scrollIndex);
}, [ scrollToBottom, scrollIndex, scrollToIndex ]);
return ( return (
<Base fit innerRef={ elementRef } position="relative" overflow="auto"> <Base fit innerRef={ elementRef } position="relative" overflow="auto">
{ (paddingTop > 0) && { (paddingTop > 0) &&

View File

@ -1,5 +1,5 @@
import { ILinkEventTracker } from '@nitrots/nitro-renderer'; import { ILinkEventTracker } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useState } from 'react'; import { FC, useEffect, useMemo, useRef, useState } from 'react';
import { AddEventLinkTracker, ChatEntryType, LocalizeText, RemoveLinkEventTracker } from '../../api'; import { AddEventLinkTracker, ChatEntryType, LocalizeText, RemoveLinkEventTracker } from '../../api';
import { Flex, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; import { Flex, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
import { useChatHistory } from '../../hooks'; import { useChatHistory } from '../../hooks';
@ -9,6 +9,7 @@ export const ChatHistoryView: FC<{}> = props =>
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ searchText, setSearchText ] = useState<string>(''); const [ searchText, setSearchText ] = useState<string>('');
const { chatHistory = [] } = useChatHistory(); const { chatHistory = [] } = useChatHistory();
const elementRef = useRef<HTMLDivElement>(null);
const filteredChatHistory = useMemo(() => const filteredChatHistory = useMemo(() =>
{ {
@ -19,10 +20,10 @@ export const ChatHistoryView: FC<{}> = props =>
return chatHistory.filter(entry => ((entry.message && entry.message.toLowerCase().includes(text))) || (entry.name && entry.name.toLowerCase().includes(text))); return chatHistory.filter(entry => ((entry.message && entry.message.toLowerCase().includes(text))) || (entry.name && entry.name.toLowerCase().includes(text)));
}, [ chatHistory, searchText ]); }, [ chatHistory, searchText ]);
/* useEffect(() => useEffect(() =>
{ {
if(elementRef && elementRef.current && isVisible) elementRef.current.scrollTop = elementRef.current.scrollHeight; if(elementRef && elementRef.current && isVisible) elementRef.current.scrollTop = elementRef.current.scrollHeight;
}, [ isVisible ]); */ }, [ isVisible ]);
useEffect(() => useEffect(() =>
{ {
@ -59,9 +60,9 @@ export const ChatHistoryView: FC<{}> = props =>
return ( return (
<NitroCardView uniqueKey="chat-history" className="nitro-chat-history" theme="primary-slim"> <NitroCardView uniqueKey="chat-history" className="nitro-chat-history" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('room.chathistory.button.text') } onCloseClick={ event => setIsVisible(false) }/> <NitroCardHeaderView headerText={ LocalizeText('room.chathistory.button.text') } onCloseClick={ event => setIsVisible(false) }/>
<NitroCardContentView overflow="hidden" gap={ 2 }> <NitroCardContentView innerRef={ elementRef } overflow="hidden" gap={ 2 }>
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchText } onChange={ event => setSearchText(event.target.value) } /> <input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchText } onChange={ event => setSearchText(event.target.value) } />
<InfiniteScroll rows={ filteredChatHistory } estimateSize={ 35 } rowRender={ row => <InfiniteScroll rows={ filteredChatHistory } scrollToBottom={ true } rowRender={ row =>
{ {
return ( return (
<Flex alignItems="center" className="p-1" gap={ 2 }> <Flex alignItems="center" className="p-1" gap={ 2 }>

View File

@ -70,7 +70,7 @@ export const ChatlogView: FC<ChatlogViewProps> = props =>
</Grid> </Grid>
</Column> </Column>
{ (records && (records.length > 0)) && { (records && (records.length > 0)) &&
<InfiniteScroll rows={ allRecords } estimateSize={ 25 } rowRender={ (row: ChatlogRecord) => <InfiniteScroll rows={ allRecords } rowRender={ (row: ChatlogRecord) =>
{ {
return ( return (
<> <>

View File

@ -43,7 +43,7 @@ export const ModToolsUserRoomVisitsView: FC<ModToolsUserRoomVisitsViewProps> = p
<Base className="g-col-3">Visit</Base> <Base className="g-col-3">Visit</Base>
</Grid> </Grid>
</Column> </Column>
<InfiniteScroll rows={ roomVisitData?.rooms ?? [] } estimateSize={ 25 } rowRender={ row => <InfiniteScroll rows={ roomVisitData?.rooms ?? [] } rowRender={ row =>
{ {
return ( return (
<Grid fullHeight={ false } gap={ 1 } alignItems="center" className="text-black py-1 border-bottom"> <Grid fullHeight={ false } gap={ 1 } alignItems="center" className="text-black py-1 border-bottom">

View File

@ -36,7 +36,7 @@ export const ChooserWidgetView: FC<ChooserWidgetViewProps> = props =>
<NitroCardHeaderView headerText={ title } onCloseClick={ onClose } /> <NitroCardHeaderView headerText={ title } onCloseClick={ onClose } />
<NitroCardContentView overflow="hidden" gap={ 2 }> <NitroCardContentView overflow="hidden" gap={ 2 }>
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } /> <input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
<InfiniteScroll rows={ filteredItems } estimateSize={ 25 } rowRender={ row => <InfiniteScroll rows={ filteredItems } rowRender={ row =>
{ {
return ( return (
<Flex alignItems="center" className={ classNames('rounded p-1', (selectedItem === row) && 'bg-muted') } pointer onClick={ event => setSelectedItem(row) }> <Flex alignItems="center" className={ classNames('rounded p-1', (selectedItem === row) && 'bg-muted') } pointer onClick={ event => setSelectedItem(row) }>