Spaces:
Running
Running
File size: 2,360 Bytes
5012205 d8c725b 5012205 d8c725b 5012205 d8c725b 5012205 d8c725b 5012205 d8c725b 5012205 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
import { useEffect, useRef, type RefObject } from 'react';
export function useScrollToBottom(): [
RefObject<HTMLDivElement>,
RefObject<HTMLDivElement>,
] {
const containerRef = useRef<HTMLDivElement>(null);
const endRef = useRef<HTMLDivElement>(null);
const isUserScrollingRef = useRef(false);
useEffect(() => {
const container = containerRef.current;
const end = endRef.current;
if (!container || !end) return;
// Initial scroll to bottom
setTimeout(() => {
end.scrollIntoView({ behavior: 'instant', block: 'end' });
}, 100);
// Track if user has manually scrolled up
const handleScroll = () => {
if (!container) return;
const { scrollTop, scrollHeight, clientHeight } = container;
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
// If user is scrolled up, mark as manually scrolling
isUserScrollingRef.current = distanceFromBottom > 100;
};
// Handle mutations
const observer = new MutationObserver((mutations) => {
if (!container || !end) return;
// Check if mutation is related to expand/collapse
const isToggleSection = mutations.some(mutation => {
// Check if the target or parent is a motion-div (expanded content)
let target = mutation.target as HTMLElement;
let isExpand = false;
while (target && target !== container) {
if (target.classList?.contains('motion-div')) {
isExpand = true;
break;
}
target = target.parentElement as HTMLElement;
}
return isExpand;
});
// Don't scroll for expand/collapse actions
if (isToggleSection) return;
// Only auto-scroll if user hasn't manually scrolled up
if (!isUserScrollingRef.current) {
// For new messages, use smooth scrolling
end.scrollIntoView({ behavior: 'smooth', block: 'end' });
}
});
observer.observe(container, {
childList: true,
subtree: true,
});
// Add scroll event listener
container.addEventListener('scroll', handleScroll);
return () => {
observer.disconnect();
container.removeEventListener('scroll', handleScroll);
};
}, []);
return [containerRef, endRef] as [RefObject<HTMLDivElement>, RefObject<HTMLDivElement>];
} |