chatbot-template/hooks/use-scroll-to-bottom.tsx

69 lines
1.8 KiB
TypeScript

import useSWR from 'swr';
import { useRef, useEffect, useCallback, useState } from 'react';
type ScrollFlag = ScrollBehavior | false;
export function useScrollToBottom() {
const containerRef = useRef<HTMLDivElement>(null);
const endRef = useRef<HTMLDivElement>(null);
const [isAtBottom, setIsAtBottom] = useState(true);
const { data: scrollBehavior = false, mutate: setScrollBehavior } =
useSWR<ScrollFlag>('messages:should-scroll', null, { fallbackData: false });
const handleScroll = useCallback(() => {
if (!containerRef.current) return;
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
// Check if we are within 100px of the bottom (like v0 does)
setIsAtBottom(scrollTop + clientHeight >= scrollHeight - 100);
}, []);
useEffect(() => {
const container = containerRef.current;
if (!container) return;
container.addEventListener('scroll', handleScroll);
handleScroll(); // Check initial state
return () => {
container.removeEventListener('scroll', handleScroll);
};
}, [handleScroll]);
useEffect(() => {
if (scrollBehavior && containerRef.current) {
const container = containerRef.current;
const scrollOptions: ScrollToOptions = {
top: container.scrollHeight,
behavior: scrollBehavior
};
container.scrollTo(scrollOptions);
setScrollBehavior(false);
}
}, [scrollBehavior, setScrollBehavior]);
const scrollToBottom = useCallback(
(scrollBehavior: ScrollBehavior = 'smooth') => {
setScrollBehavior(scrollBehavior);
},
[setScrollBehavior],
);
function onViewportEnter() {
setIsAtBottom(true);
}
function onViewportLeave() {
setIsAtBottom(false);
}
return {
containerRef,
endRef,
isAtBottom,
scrollToBottom,
onViewportEnter,
onViewportLeave,
};
}