import { RefObject , useLayoutEffect , useState } from 'react' ; import { debounce } from '@proton/shared/lib/helpers/function' ; import useInstance from './useInstance' ; const keys = [ 'bottom' , 'height' , 'left' , 'right' , 'top' , 'width' , 'x' , 'y' ] ; const isEquivalent = ( aRect ? , bRect ? ) => { if ( ! aRect && bRect ) { return false ; } if ( aRect && ! bRect ) { return false ; } for ( const key of keys ) { if ( aRect ?. [ key as keyof DOMRect ] !== bRect ?. [ key as keyof DOMRect ] ) { return false ; } } return true ; } ; const getElementRect = ( target ? ) => { if ( ! target ) { return ; } return target . getBoundingClientRect ( ) ; } ; type RateLimiter = < A extends any [ ] > ( func : ( ... args : A ) => void , wait : number ) => ( ... args : A ) => void ; const equivalentReducer = ( oldRect ? , newRect ? ) => { return isEquivalent ( oldRect , newRect ) ? oldRect : newRect ; } ; const defaultReducer = ( oldRect , newRect , sizeCache ) => { const [ prevOld , prevNew ] = sizeCache ; if ( isEquivalent ( prevOld , newRect ) && isEquivalent ( prevNew , oldRect ) ) { return oldRect ; } sizeCache [ 0 ] = oldRect ; sizeCache [ 1 ] = newRect ; return equivalentReducer ( oldRect , newRect ) ; } ; type Reducer = typeof defaultReducer ; const useElementRect = < E extends HTMLElement > ( ref , rateLimiter = debounce , reducer = defaultReducer ) => { const sizeCache = useInstance ( ( ) : ( DOMRect | undefined ) [ ] => [ ] ) ; const [ elementRect , setElementRect ] = useState ( ( ) => getElementRect ( ref . current ) ) ; useLayoutEffect ( ( ) => { const target = ref . current ; if ( ! target ) { return ; } if ( typeof ResizeObserver === 'function' ) { const resizeObserver = new ResizeObserver ( rateLimiter ( ( [ { contentRect } ] ) => { setElementRect ( ( old ) => reducer ( old , getElementRect ( target ) , sizeCache ) ) ; } , 100 ) ) ; resizeObserver . observe ( target , { box : 'border-box' } ) ; setElementRect ( ( old ) => reducer ( old , getElementRect ( target ) , sizeCache ) ) ; return ( ) => { resizeObserver . disconnect ( ) ; } ; } const onResize = rateLimiter ( ( ) => { setElementRect ( ( old ) => reducer ( old , getElementRect ( target ) , sizeCache ) ) ; } , 100 ) ; window . addEventListener ( 'resize' , onResize ) ; setElementRect ( ( old ) => reducer ( old , getElementRect ( target ) , sizeCache ) ) ; return ( ) => { window . removeEventListener ( 'resize' , onResize ) ; } ; } , [ ref . current ] ) ; return elementRect ; } ; export default useElementRect ;