import React, { useEffect, useRef, useState } from 'react' import withDefaults from '../utils/with-defaults' import useRealShape from '../utils/use-real-shape' interface Props { isExpanded?: boolean delay?: number } const defaultProps = { isExpanded: false, delay: 200, } export type ExpandProps = Props & typeof defaultProps const Expand: React.FC> = ({ isExpanded, delay, children, }) => { const [height, setHeight] = useState(isExpanded ? 'auto' : '0') const [selfExpanded, setSelfExpanded] = useState(isExpanded) const [visible, setVisible] = useState(isExpanded) const contentRef = useRef(null) const entryTimer = useRef() const leaveTimer = useRef() const resetTimer = useRef() const [state, updateShape] = useRealShape(contentRef) useEffect(() => setHeight(`${state.height}px`), [state.height]) useEffect(() => { // show element or reset height. // force an update once manually, even if the element does not change. // (the height of the element might be "auto") if (isExpanded) { setVisible(isExpanded) } else { updateShape() setHeight(`${state.height}px`) } // show expand animation entryTimer.current = window.setTimeout(() => { setSelfExpanded(isExpanded) clearTimeout(entryTimer.current) }, 30) // Reset height after animation if (isExpanded) { resetTimer.current = window.setTimeout(() => { setHeight('auto') clearTimeout(resetTimer.current) }, delay) } else { leaveTimer.current = window.setTimeout(() => { setVisible(isExpanded) clearTimeout(leaveTimer.current) }, delay / 2) } return () => { clearTimeout(entryTimer.current) clearTimeout(leaveTimer.current) clearTimeout(resetTimer.current) } }, [isExpanded]) return (
{children}
) } export default withDefaults(Expand, defaultProps)