Files
Compass/web/components/widgets/visibility-observer.tsx
2026-03-08 01:17:00 +01:00

62 lines
1.7 KiB
TypeScript

import {useEffect, useRef, useState} from 'react'
import {useEvent} from 'web/hooks/use-event'
export function VisibilityObserver(props: {
className?: string
onVisibilityUpdated: (visible: boolean) => void
}) {
const {className} = props
const [elem, setElem] = useState<HTMLElement | null>(null)
const onVisibilityUpdated = useEvent(props.onVisibilityUpdated)
useEffect(() => {
if (elem) {
const observer = new IntersectionObserver(([entry]) => {
onVisibilityUpdated(entry.isIntersecting)
}, {})
observer.observe(elem)
return () => observer.unobserve(elem)
}
}, [elem, onVisibilityUpdated])
return <div ref={setElem} className={className}></div>
}
export function LoadMoreUntilNotVisible(props: {
// Returns true if there are more results.
loadMore: () => Promise<boolean>
}) {
const {loadMore} = props
const isVisibleRef = useRef(false)
const loadMoreIfVisible = useEvent(async () => {
// debug('loadMoreIfVisible called')
if (isVisibleRef.current && loadMore) {
// debug('loadMore calling')
await loadMore()
// const hasMoreResults = await loadMore()
// if (hasMoreResults) {
// setTimeout(() => {
// if (isVisibleRef.current) {
// loadMoreIfVisible()
// }
// }, 500)
// }
}
})
const onVisibilityUpdated = useEvent((visible: boolean) => {
isVisibleRef.current = visible
loadMoreIfVisible()
})
return (
<div className="relative">
<VisibilityObserver
// Loads more as soon as one screen height of content is left.
className="pointer-events-none absolute bottom-0 h-screen w-full select-none"
onVisibilityUpdated={onVisibilityUpdated}
/>
</div>
)
}