import { RefObject, useEffect, useRef } from 'react' interface UseIntersectionLoadOptions extends IntersectionObserverInit { earlyTriggerPx?: number debounceMs?: number } export function useIntersectionLoad( callback: () => Promise, elementRef: RefObject, { earlyTriggerPx = 1200, debounceMs = 300, root = null, threshold = 0.1, rootMargin = '0px', }: UseIntersectionLoadOptions = {}, ) { const observerRef = useRef(null) const loading = useRef(false) const timeoutRef = useRef(null) useEffect(() => { const el = elementRef.current if (!el) return const margin = computeAdjustedRootMargin(rootMargin, earlyTriggerPx) const debouncedCallback = () => { if (timeoutRef.current) return timeoutRef.current = setTimeout(async () => { timeoutRef.current = null if (!loading.current) { loading.current = true try { await callback() } finally { loading.current = false if (elementRef.current && observerRef.current) { observerRef.current.unobserve(elementRef.current) observerRef.current.observe(elementRef.current) } } } }, debounceMs) } observerRef.current = new IntersectionObserver( (entries) => { const entry = entries[0] if (entry.isIntersecting) { debouncedCallback() } }, { root, threshold, rootMargin: margin, }, ) observerRef.current.observe(el) return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current) timeoutRef.current = null } if (el && observerRef.current) { observerRef.current.unobserve(el) observerRef.current.disconnect() } } }, [callback, elementRef, root, rootMargin, threshold, earlyTriggerPx, debounceMs]) } // Utility to adjust rootMargin's top value function computeAdjustedRootMargin(baseMargin: string, extraTopPx: number): string { const parts = baseMargin.split(' ') const top = parts[0] || '0px' const adjustedTop = `${parseInt(top, 10) + extraTopPx}px` // Maintain format: top [right bottom left] if (parts.length === 1) { return `${adjustedTop}` } else if (parts.length === 2) { return `${adjustedTop} ${parts[1]}` } else if (parts.length === 3) { return `${adjustedTop} ${parts[1]} ${parts[2]}` } else { return `${adjustedTop} ${parts[1]} ${parts[2]} ${parts[3]}` } }