Improve scroll anchor (#275)

This commit is contained in:
Jeremy 2024-03-20 04:37:08 +03:00 committed by GitHub
parent b6cab643ef
commit 43c7cbb21e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 169 additions and 101 deletions

View file

@ -1,23 +0,0 @@
import * as React from 'react'
export function useAtBottom(offset = 0) {
const [isAtBottom, setIsAtBottom] = React.useState(false)
React.useEffect(() => {
const handleScroll = () => {
setIsAtBottom(
window.innerHeight + window.scrollY >=
document.body.offsetHeight - offset
)
}
window.addEventListener('scroll', handleScroll, { passive: true })
handleScroll()
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [offset])
return isAtBottom
}

View file

@ -0,0 +1,86 @@
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
}
}