Gitako/src/components/SizeObserver.tsx
2020-11-11 17:14:23 +08:00

55 lines
1.4 KiB
TypeScript

import * as React from 'react'
import * as features from 'utils/features'
type Size = {
width: number
height: number
}
type Props = {
type?: string | React.ComponentType
children(size: Partial<Size>): React.ReactNode
} & React.HTMLAttributes<HTMLElement>
export function SizeObserver({ type = 'div', children, ...rest }: Props) {
const ref = React.useRef<any>()
const [size, setSize] = React.useState<Partial<Size>>({
width: undefined,
height: undefined,
})
const safeSetSize = React.useCallback(function safeSetSize(rect: DOMRectReadOnly) {
// requestAnimationFrame fixes "ResizeObserver loop limit exceeded" error
requestAnimationFrame(() =>
setSize({
width: rect.width,
height: rect.height,
}),
)
}, [])
React.useEffect(() => {
if (ref.current) {
if (features.resize) {
const observer = new window.ResizeObserver(entries => {
const entry = entries[0]
if (!entry) return
const rect = entry.contentRect
safeSetSize(rect)
})
observer.observe(ref.current)
return () => observer.disconnect()
} else {
if ('getBoundingClientRect' in ref.current) {
const rect = ref.current.getBoundingClientRect()
setSize(rect)
}
}
}
}, [])
const props: any = { ...rest, ref } // :)
return React.createElement(type, props, children(size))
}