1import { useEffect, useRef } from 'react'; 2 3const UPPER_THRESHOLD = 1 / 4; 4const LOWER_THRESHOLD = 3 / 4; 5 6/** 7 * Automatically scroll to a child element, by selector. 8 * Whenever the selector changes, it tries to detect if the selector is out of view. 9 * If it is, it will scroll to the closest upper or lower bound within the ref's scroll area. 10 */ 11export function useAutoScrollTo<T extends HTMLElement = HTMLDivElement>(selector: string) { 12 const ref = useRef<T>(null); 13 14 useEffect( 15 function onMaybeScroll() { 16 if (!selector || !ref.current) return; 17 const target = ref.current.querySelector<HTMLElement>(selector); 18 if (target) { 19 const bounds = ref.current.getBoundingClientRect(); 20 const position = ref.current.scrollTop; 21 const targetPosition = target.offsetTop; 22 const upperThreshold = bounds.height * UPPER_THRESHOLD; 23 const lowerThreshold = bounds.height * LOWER_THRESHOLD; 24 25 if (targetPosition < position + upperThreshold) { 26 ref.current.scrollTo({ top: Math.max(0, targetPosition - upperThreshold) }); 27 } else if (targetPosition > position + lowerThreshold) { 28 ref.current.scrollTo({ top: targetPosition - lowerThreshold }); 29 } 30 } 31 }, 32 [ref.current, selector] 33 ); 34 35 return { ref }; 36} 37