fix: scroll issue playground
This commit is contained in:
@@ -1,106 +0,0 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { useMessageOption } from "./useMessageOption"
|
||||
|
||||
export const useScrollAnchor = () => {
|
||||
const { isProcessing, messages } = useMessageOption()
|
||||
|
||||
const [isAtTop, setIsAtTop] = useState(false)
|
||||
const [isAtBottom, setIsAtBottom] = useState(true)
|
||||
const [userScrolled, setUserScrolled] = useState(false)
|
||||
const [isOverflowing, setIsOverflowing] = useState(false)
|
||||
|
||||
const messagesStartRef = useRef<HTMLDivElement>(null)
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const isAutoScrolling = useRef(false)
|
||||
|
||||
console.log(`isAtTop: ${isAtTop}, isAtBottom: ${isAtBottom}, userScrolled: ${userScrolled}, isOverflowing: ${isOverflowing}`)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isProcessing && userScrolled) {
|
||||
console.log("userScrolled")
|
||||
setUserScrolled(false)
|
||||
}
|
||||
}, [isProcessing])
|
||||
|
||||
useEffect(() => {
|
||||
if (isProcessing && !userScrolled) {
|
||||
scrollToBottom()
|
||||
}
|
||||
}, [messages])
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current
|
||||
if (!container) return
|
||||
|
||||
const topObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
setIsAtTop(entry.isIntersecting)
|
||||
},
|
||||
{ threshold: 1 }
|
||||
)
|
||||
|
||||
const bottomObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
setIsAtBottom(entry.isIntersecting)
|
||||
if (entry.isIntersecting) {
|
||||
setUserScrolled(false)
|
||||
} else if (!isAutoScrolling.current) {
|
||||
setUserScrolled(true)
|
||||
}
|
||||
},
|
||||
{ threshold: 1 }
|
||||
)
|
||||
|
||||
if (messagesStartRef.current) {
|
||||
topObserver.observe(messagesStartRef.current)
|
||||
}
|
||||
|
||||
if (messagesEndRef.current) {
|
||||
bottomObserver.observe(messagesEndRef.current)
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
setIsOverflowing(container.scrollHeight > container.clientHeight)
|
||||
})
|
||||
|
||||
resizeObserver.observe(container)
|
||||
|
||||
return () => {
|
||||
topObserver.disconnect()
|
||||
bottomObserver.disconnect()
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const scrollToTop = useCallback(() => {
|
||||
if (messagesStartRef.current) {
|
||||
messagesStartRef.current.scrollIntoView({ behavior: "smooth" })
|
||||
}
|
||||
}, [])
|
||||
|
||||
const scrollToBottom = useCallback(() => {
|
||||
isAutoScrolling.current = true
|
||||
|
||||
setTimeout(() => {
|
||||
if (messagesEndRef.current) {
|
||||
messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
|
||||
}
|
||||
|
||||
isAutoScrolling.current = false
|
||||
}, 100)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
messagesStartRef,
|
||||
messagesEndRef,
|
||||
containerRef,
|
||||
isAtTop,
|
||||
isAtBottom,
|
||||
userScrolled,
|
||||
isOverflowing,
|
||||
scrollToTop,
|
||||
scrollToBottom,
|
||||
setIsAtBottom
|
||||
}
|
||||
}
|
||||
35
src/hooks/useSmartScroll.tsx
Normal file
35
src/hooks/useSmartScroll.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useRef, useEffect, useState } from 'react';
|
||||
|
||||
export const useSmartScroll = (messages: any[], streaming: boolean) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [isAtBottom, setIsAtBottom] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const handleScroll = () => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = container;
|
||||
setIsAtBottom(scrollHeight - scrollTop - clientHeight < 50);
|
||||
};
|
||||
|
||||
container.addEventListener('scroll', handleScroll);
|
||||
return () => container.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAtBottom && containerRef.current) {
|
||||
const scrollOptions: ScrollIntoViewOptions = streaming
|
||||
? { behavior: 'smooth', block: 'end' }
|
||||
: { behavior: 'auto', block: 'end' };
|
||||
containerRef.current.lastElementChild?.scrollIntoView(scrollOptions);
|
||||
}
|
||||
}, [messages, streaming, isAtBottom]);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
containerRef.current?.lastElementChild?.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
};
|
||||
|
||||
|
||||
return { containerRef, isAtBottom, scrollToBottom };
|
||||
};
|
||||
Reference in New Issue
Block a user