vision-agent / lib /hooks /useScrollAnchor.tsx
MingruiZhang's picture
feat: Customized Img and scroll to bottom (#18)
8e3dbd3 unverified
raw
history blame
No virus
2 kB
import { useCallback, useEffect, useRef, useState } from 'react';
export const useScrollAnchor = () => {
const messagesRef = useRef<HTMLDivElement>(null);
const scrollRef = useRef<HTMLDivElement>(null);
const visibilityRef = useRef<HTMLDivElement>(null);
const [isAtBottom, setIsAtBottom] = useState(true);
const [isVisible, setIsVisible] = useState(false);
const scrollToBottom = useCallback(() => {
if (messagesRef.current) {
messagesRef.current.scrollIntoView({
block: 'end',
behavior: 'smooth',
});
}
}, []);
useEffect(() => {
if (messagesRef.current) {
if (isAtBottom && !isVisible) {
messagesRef.current.scrollIntoView({
block: 'end',
});
}
}
}, [isAtBottom, isVisible]);
useEffect(() => {
const { current } = scrollRef;
if (current) {
const handleScroll = (event: Event) => {
const target = event.target as HTMLDivElement;
const offset = 25;
const isAtBottom =
target.scrollTop + target.clientHeight >=
target.scrollHeight - offset;
setIsAtBottom(isAtBottom);
};
current.addEventListener('scroll', handleScroll, {
passive: true,
});
return () => {
current.removeEventListener('scroll', handleScroll);
};
}
}, []);
useEffect(() => {
if (visibilityRef.current) {
let observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setIsVisible(true);
} else {
setIsVisible(false);
}
});
},
{
rootMargin: '0px 0px -150px 0px',
},
);
observer.observe(visibilityRef.current);
return () => {
observer.disconnect();
};
}
});
return {
messagesRef,
scrollRef,
visibilityRef,
scrollToBottom,
isAtBottom,
isVisible,
};
};