manifold/web/hooks/use-measure-size.ts

64 lines
1.8 KiB
TypeScript
Raw Normal View History

import { debounce } from 'lodash'
import { RefObject, useMemo, useLayoutEffect, useRef, useState } from 'react'
type elem_size =
| { width: number; height: number }
| { width: undefined; height: undefined }
const getSize = (elem: HTMLElement | null) =>
elem
? { width: elem.offsetWidth, height: elem.offsetHeight }
: { width: undefined, height: undefined }
export function useListenElemSize<T extends HTMLElement>(
elemRef: RefObject<T | null>,
callback: (size: elem_size) => void,
debounceMs: number | undefined = undefined
) {
const handleResize = useMemo(() => {
const updateSize = () => {
if (elemRef.current) callback(getSize(elemRef.current))
}
return debounceMs
? debounce(updateSize, debounceMs, { leading: false, trailing: true })
: updateSize
}, [callback, elemRef, debounceMs])
const elem = elemRef.current
useLayoutEffect(() => {
if (!elemRef.current) return
const resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(elemRef.current)
return () => resizeObserver.disconnect()
}, [elemRef, elem, handleResize])
}
export function useMeasureSize(debounceMs: number | undefined = undefined) {
const elemRef = useRef<HTMLElement | null>(null)
const [size, setSize] = useState(() => getSize(null))
const sizeRef = useRef<elem_size>(size)
const setSizeIfDifferent = (newSize: typeof size) => {
if (newSize?.height !== size?.height || newSize?.width !== size?.width) {
sizeRef.current = newSize
setSize(newSize)
}
}
useListenElemSize(elemRef, setSizeIfDifferent, debounceMs)
const setElem = (elem: HTMLElement | null) => {
elemRef.current = elem
if (elem) {
setSizeIfDifferent(getSize(elem))
}
}
return { setElem, elemRef, sizeRef, ...size }
}