UNPKG

polaris-glints

Version:

Glints forked polaris react

112 lines (99 loc) 3.42 kB
import React, { useState, useRef, useCallback, useEffect } from 'react'; import { classNames } from '../../utilities/css.js'; import styles from './Collapsible.scss.js'; function Collapsible({ id, expandOnPrint, open, transition = true, preventMeasuringOnChildrenUpdate: _preventMeasuringOnChildrenUpdate, children, onAnimationEnd }) { const [height, setHeight] = useState(0); const [isOpen, setIsOpen] = useState(open); const [animationState, setAnimationState] = useState('idle'); const collapsibleContainer = useRef(null); const isFullyOpen = animationState === 'idle' && open && isOpen; const isFullyClosed = animationState === 'idle' && !open && !isOpen; const content = expandOnPrint || !isFullyClosed ? children : null; const wrapperClassName = classNames(styles.Collapsible, isFullyClosed && styles.isFullyClosed, expandOnPrint && styles.expandOnPrint); const transitionDisabled = isTransitionDisabled(transition); const transitionStyles = typeof transition === 'object' && { transitionDuration: transition.duration, transitionTimingFunction: transition.timingFunction }; const collapsibleStyles = { ...transitionStyles, ...{ maxHeight: isFullyOpen ? 'none' : `${height}px`, overflow: isFullyOpen ? 'visible' : 'hidden' } }; const handleCompleteAnimation = useCallback(({ target }) => { if (target === collapsibleContainer.current) { setAnimationState('idle'); setIsOpen(open); onAnimationEnd && onAnimationEnd(); } }, [onAnimationEnd, open]); const startAnimation = useCallback(() => { if (transitionDisabled) { setIsOpen(open); setAnimationState('idle'); if (open && collapsibleContainer.current) { setHeight(collapsibleContainer.current.scrollHeight); } else { setHeight(0); } } else { setAnimationState('measuring'); } }, [open, transitionDisabled]); useEffect(() => { if (open !== isOpen) { startAnimation(); } // startAnimation should only be fired if the open state changes. // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, isOpen]); useEffect(() => { if (!open || !collapsibleContainer.current) return; // If collapsible defaults to open, set an initial height setHeight(collapsibleContainer.current.scrollHeight); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (!collapsibleContainer.current) return; switch (animationState) { case 'idle': break; case 'measuring': setHeight(collapsibleContainer.current.scrollHeight); setAnimationState('animating'); break; case 'animating': setHeight(open ? collapsibleContainer.current.scrollHeight : 0); } }, [animationState, open, isOpen]); return /*#__PURE__*/React.createElement("div", { id: id, style: collapsibleStyles, ref: collapsibleContainer, className: wrapperClassName, onTransitionEnd: handleCompleteAnimation, "aria-hidden": !open }, content); } const zeroDurationRegex = /^0(ms|s)$/; function isTransitionDisabled(transitionProp) { if (typeof transitionProp === 'boolean') { return !transitionProp; } const { duration } = transitionProp; if (duration && zeroDurationRegex.test(duration.trim())) { return true; } return false; } export { Collapsible };