| import { useRef, useCallback } from 'react'; | |
| export function useSnapScroll() { | |
| const autoScrollRef = useRef(true); | |
| const scrollNodeRef = useRef<HTMLDivElement>(); | |
| const onScrollRef = useRef<() => void>(); | |
| const observerRef = useRef<ResizeObserver>(); | |
| const messageRef = useCallback((node: HTMLDivElement | null) => { | |
| if (node) { | |
| const observer = new ResizeObserver(() => { | |
| if (autoScrollRef.current && scrollNodeRef.current) { | |
| const { scrollHeight, clientHeight } = scrollNodeRef.current; | |
| const scrollTarget = scrollHeight - clientHeight; | |
| scrollNodeRef.current.scrollTo({ | |
| top: scrollTarget, | |
| }); | |
| } | |
| }); | |
| observer.observe(node); | |
| } else { | |
| observerRef.current?.disconnect(); | |
| observerRef.current = undefined; | |
| } | |
| }, []); | |
| const scrollRef = useCallback((node: HTMLDivElement | null) => { | |
| if (node) { | |
| onScrollRef.current = () => { | |
| const { scrollTop, scrollHeight, clientHeight } = node; | |
| const scrollTarget = scrollHeight - clientHeight; | |
| autoScrollRef.current = Math.abs(scrollTop - scrollTarget) <= 10; | |
| }; | |
| node.addEventListener('scroll', onScrollRef.current); | |
| scrollNodeRef.current = node; | |
| } else { | |
| if (onScrollRef.current) { | |
| scrollNodeRef.current?.removeEventListener('scroll', onScrollRef.current); | |
| } | |
| scrollNodeRef.current = undefined; | |
| onScrollRef.current = undefined; | |
| } | |
| }, []); | |
| return [messageRef, scrollRef]; | |
| } | |