Improve scroll anchor (#275)
This commit is contained in:
parent
b6cab643ef
commit
43c7cbb21e
12 changed files with 169 additions and 101 deletions
|
|
@ -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
|
||||
}
|
||||
86
lib/hooks/use-scroll-anchor.tsx
Normal file
86
lib/hooks/use-scroll-anchor.tsx
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue