| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- import { useCallback, useEffect, useRef, useState } from 'react'
- interface UseAutoScrollProps {
- enabled?: boolean
- }
- export function useAutoScroll({ enabled = true }: UseAutoScrollProps = {}) {
- const [container, setContainer] = useState<HTMLDivElement | null>(null)
- const [isSticky, setIsSticky] = useState(true)
- const isStickyRef = useRef(true)
- const lastScrollHeightRef = useRef<number>(null)
- const ref = useCallback((element: HTMLDivElement | null) => {
- if (element) {
- setContainer(element)
- }
- }, [])
- const scrollToEnd = useCallback(() => {
- if (container) {
- isStickyRef.current = true
- setIsSticky(true)
- container.scrollTo({
- top: container.scrollHeight,
- behavior: 'smooth',
- })
- }
- }, [container])
- useEffect(() => {
- if (!container || !enabled) return
- let timeoutId: NodeJS.Timeout
- const resizeObserver = new ResizeObserver(() => {
- clearTimeout(timeoutId)
- timeoutId = setTimeout(() => {
- if (
- lastScrollHeightRef.current != null &&
- container.scrollHeight !== lastScrollHeightRef.current
- ) {
- lastScrollHeightRef.current = container.scrollHeight
- if (isStickyRef.current) {
- scrollToEnd()
- }
- }
- }, 100)
- })
- const handleScroll = () => {
- const isAtBottom =
- Math.abs(container.scrollHeight - container.scrollTop - container.clientHeight) < 10
- isStickyRef.current = isAtBottom
- setIsSticky(isAtBottom)
- }
- // Observe all children of the container
- Array.from(container.children).forEach((child) => {
- resizeObserver.observe(child)
- })
- container.addEventListener('scroll', handleScroll)
- return () => {
- clearTimeout(timeoutId)
- resizeObserver.disconnect()
- container.removeEventListener('scroll', handleScroll)
- }
- }, [container, enabled, scrollToEnd])
- return { ref, isSticky, scrollToEnd, setIsSticky }
- }
|