UNPKG

@redocly/theme

Version:

Shared UI components lib

99 lines (83 loc) 2.73 kB
import { useState, useCallback, RefObject, useLayoutEffect, useRef } from 'react'; type Size = { width: number; height: number; }; const getInitialSize = (ref?: RefObject<HTMLElement | null>): Size => { if (ref?.current) { return { width: ref.current.offsetWidth, height: ref.current.offsetHeight, }; } if (ref?.current === null) { return { width: 0, height: 0, }; } return { width: typeof window !== 'undefined' ? window.innerWidth : 0, height: typeof window !== 'undefined' ? window.innerHeight : 0, }; }; export function useElementSize<T extends HTMLElement = HTMLElement>({ delay = 50, detectSizes = 'both', }: { delay?: number; detectSizes?: 'width' | 'height' | 'both' } = {}): [ Size, RefObject<T | null>, ] { const ref = useRef<T | null>(null); const previousSize = useRef<Size>(getInitialSize(ref)); const [size, setSize] = useState<Size>(getInitialSize(ref)); const isFirstUpdate = useRef(true); const updateSize = useCallback( (newSize: Size) => { const shouldUpdateWidth = detectSizes === 'both' || detectSizes === 'width'; const shouldUpdateHeight = detectSizes === 'both' || detectSizes === 'height'; const widthChanged = shouldUpdateWidth && newSize.width !== previousSize.current.width; const heightChanged = shouldUpdateHeight && newSize.height !== previousSize.current.height; if (widthChanged || heightChanged) { const updatedSize = { width: shouldUpdateWidth ? newSize.width : previousSize.current.width, height: shouldUpdateHeight ? newSize.height : previousSize.current.height, }; setSize(updatedSize); previousSize.current = updatedSize; } }, [detectSizes], ); useLayoutEffect(() => { let timeoutId: number | null = null; const triggerUpdateWithThrottling = (newSize: Size) => { if (isFirstUpdate.current) { updateSize(newSize); isFirstUpdate.current = false; return; } if (timeoutId !== null) return; timeoutId = window.setTimeout(() => { updateSize(newSize); timeoutId = null; }, delay); }; if (ref?.current && typeof ResizeObserver !== 'undefined') { const observer = new ResizeObserver((entries) => { for (const entry of entries) { const { width, height } = entry.contentRect; triggerUpdateWithThrottling({ width, height }); } }); observer.observe(ref.current); return () => { observer.disconnect(); if (timeoutId !== null) { clearTimeout(timeoutId); } }; } }, [ref, updateSize, delay]); return [size, ref]; }