import { useEffect , useState , useCallback } from 'react' ; import throttle from 'lodash/throttle' ; import debounce from 'lodash/debounce' ; import { useEvent } from 'react-use' ; import { polyfill } from 'smoothscroll-polyfill' ; const guessVisibleSection = ( ids , marginTop ) : string | null => { if ( ids . length === 0 ) return null ; if ( window . scrollY <= 0 ) return ids [ 0 ] ; return ids . reduce ( ( match , id ) => { const el = document . getElementById ( id ) ; if ( ! el ) { return '0' ; } const elPosY = Math . floor ( el . getBoundingClientRect ( ) . top ) + window . scrollY ; return window . scrollY + marginTop >= elPosY ? id : match ; } , ids [ 0 ] ) ; } ; const maybeWindow = process . browser ? window : undefined ; export const scrollTo = ( id , { marginTop = 0 , smooth = false , } = { } ) => { polyfill ( ) ; const elm = document . getElementById ( id ) ; if ( ! elm ) { return ; } const rect = elm . getBoundingClientRect ( ) ; window . scrollTo ( { top : Math . floor ( rect . top ) + window . scrollY - marginTop , behavior : smooth ? 'smooth' : 'auto' , } ) ; } ; const useScrollSpy = ( ids , { marginTop = 80 , smooth = false , } = { } ) : [ string | undefined , ( id : string ) => void ] => { const [ current , setCurrent ] = useState ( ids [ 0 ] || '' ) ; const [ ignore , setIgnore ] = useState ( false ) ; const checkScrollStop = useCallback ( debounce ( ( ) => setIgnore ( false ) , 50 ) , [ setIgnore ] ) ; useEvent ( 'scroll' , checkScrollStop , maybeWindow ) ; const navigate = useCallback ( ( id ) => { setCurrent ( id ) ; setIgnore ( true ) ; scrollTo ( id , { marginTop : marginTop , smooth : smooth } ) ; } , [ ] ) ; const updateCurrent = useCallback ( throttle ( ( ) => { if ( ! ignore ) { setCurrent ( guessVisibleSection ( ids , marginTop ) || '' ) ; } } , 100 ) , [ ids , ignore , setCurrent ] ) ; useEffect ( updateCurrent , [ ids ] ) ; useEvent ( 'scroll' , updateCurrent , maybeWindow ) ; useEvent ( 'resize' , updateCurrent , maybeWindow ) ; return [ current , navigate ] ; } ; export default useScrollSpy ;