import { useEffect , useState , useRef , useMemo , RefObject , RefCallback , useCallback , } from "react" ; type SubscriberCleanup = ( ) => void ; type SubscriberResponse = SubscriberCleanup | void ; function useResolvedElement < T extends HTMLElement > ( subscriber , refOrElement ? ) { const callbackRefElement = useRef < T | null > ( null ) ; const lastReportRef = useRef < { reporter : ( ) => void ; element : T | null ; } | null > ( null ) ; const cleanupRef = useRef < SubscriberResponse | null > ( ) ; const callSubscriber = useCallback ( ( ) => { let element = null ; if ( callbackRefElement . current ) { element = callbackRefElement . current ; } else if ( refOrElement ) { if ( refOrElement instanceof HTMLElement ) { element = refOrElement ; } else { element = refOrElement . current ; } } if ( lastReportRef . current && lastReportRef . current . element === element && lastReportRef . current . reporter === callSubscriber ) { return ; } if ( cleanupRef . current ) { cleanupRef . current ( ) ; cleanupRef . current = null ; } lastReportRef . current = { reporter : callSubscriber , element , } ; if ( element ) { cleanupRef . current = subscriber ( element ) ; } } , [ refOrElement , subscriber ] ) ; useEffect ( ( ) => { callSubscriber ( ) ; } , [ callSubscriber ] ) ; return useCallback < RefCallback < T > > ( ( element ) => { callbackRefElement . current = element ; callSubscriber ( ) ; } , [ callSubscriber ] ) ; } type ObservedSize = { width : number | undefined ; height : number | undefined ; } ; type ResizeHandler = ( size : ObservedSize ) => void ; type HookResponse < T extends HTMLElement > = { ref : RefCallback < T > ; } & ObservedSize ; type ResizeObserverBoxOptions = "border-box" | "content-box" | "device-pixel-content-box" ; declare global { interface ResizeObserverEntry { readonly devicePixelContentBoxSize : ReadonlyArray < ResizeObserverSize > ; } } const extractSize = ( entry , boxProp , sizeType ) : number | undefined => { if ( ! entry [ boxProp ] ) { if ( boxProp === "contentBoxSize" ) { return entry . contentRect [ sizeType === "inlineSize" ? "width" : "height" ] ; } return undefined ; } return entry [ boxProp ] [ 0 ] ? entry [ boxProp ] [ 0 ] [ sizeType ] : entry [ boxProp ] [ sizeType ] ; } ; type RoundingFunction = ( n : number ) => number ; function useResizeObserver < T extends HTMLElement > ( opts = { } ) { const onResize = opts . onResize ; const onResizeRef = useRef < ResizeHandler | undefined > ( undefined ) ; onResizeRef . current = onResize ; const round = opts . round || Math . round ; const resizeObserverRef = useRef < { box ? : ResizeObserverBoxOptions ; round ? : RoundingFunction ; instance : ResizeObserver ; } > ( ) ; const [ size , setSize ] = useState < { width ? : number ; height ? : number ; } > ( { width : undefined , height : undefined , } ) ; const didUnmount = useRef ( false ) ; useEffect ( ( ) => { return ( ) => { didUnmount . current = true ; } ; } , [ ] ) ; const previous = useRef ( { width : undefined , height : undefined , } ) ; const refCallback = useResolvedElement < T > ( useCallback ( ( element ) => { if ( ! resizeObserverRef . current || resizeObserverRef . current . box !== opts . box || resizeObserverRef . current . round !== round ) { resizeObserverRef . current = { box : opts . box , round , instance : new ResizeObserver ( ( entries ) => { const entry = entries [ 0 ] ; const boxProp = opts . box === "border-box" ? "borderBoxSize" : opts . box === "device-pixel-content-box" ? "devicePixelContentBoxSize" : "contentBoxSize" ; const reportedWidth = extractSize ( entry , boxProp , "inlineSize" ) ; const reportedHeight = extractSize ( entry , boxProp , "blockSize" ) ; const newWidth = reportedWidth ? round ( reportedWidth ) : undefined ; const newHeight = reportedHeight ? round ( reportedHeight ) : undefined ; if ( previous . current . width !== newWidth || previous . current . height !== newHeight ) { const newSize = { width : newWidth , height : newHeight } ; previous . current . width = newWidth ; previous . current . height = newHeight ; if ( onResizeRef . current ) { onResizeRef . current ( newSize ) ; } else { if ( ! didUnmount . current ) { setSize ( newSize ) ; } } } } ) , } ; } resizeObserverRef . current . instance . observe ( element , { box : opts . box } ) ; return ( ) => { if ( resizeObserverRef . current ) { resizeObserverRef . current . instance . unobserve ( element ) ; } } ; } , [ opts . box , round ] ) , opts . ref ) ; return useMemo ( ( ) => ( { ref : refCallback , width : size . width , height : size . height , } ) , [ refCallback , size ? size . width : null , size ? size . height : null ] ) ; } export default useResizeObserver ;