@navinc/base-react-components
Version:
Nav's Pattern Library
54 lines (45 loc) • 1.55 kB
JavaScript
import React, { useState, useRef, useMemo, useEffect } from 'react'
import debounce from 'lodash.debounce'
import styled from 'styled-components'
export const AnimateHeightContainer = styled.div`
overflow: hidden;
height: ${({ height }) => (height === 'auto' ? 'auto' : `${Math.ceil(height)}px`)};
transition: height 0.4s ease-in-out;
will-change: height;
`
AnimateHeightContainer.displayName = 'AnimateHeightContainer'
export const AnimateHeight = ({ children, ...props }) => {
const [contentHeight, setContentHeight] = useState('auto')
const contentRef = useRef(null)
const setHeight = useMemo(
() =>
debounce(() => {
window.requestAnimationFrame(() => {
setContentHeight(contentRef?.current?.getBoundingClientRect().height ?? 'auto')
})
}, 100),
[]
)
useEffect(() => {
window.addEventListener('resize', setHeight)
// ResizeObserver is not available in older browsers like Safari v12
const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(setHeight) : undefined
if (observer) {
observer.observe(contentRef.current)
}
return () => {
setHeight.cancel()
window.removeEventListener('resize', setHeight)
if (observer) {
observer.disconnect()
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<AnimateHeightContainer {...props} height={contentHeight}>
<div ref={contentRef}>{children}</div>
</AnimateHeightContainer>
)
}
export default AnimateHeight