UNPKG

@wix/design-system

Version:

@wix/design-system

121 lines 5.74 kB
import React, { Children, useEffect, useMemo, useState, useRef, } from 'react'; import { TransitionGroup, Transition as ReactTransition, } from 'react-transition-group'; import { dataHooks, timingMap } from './constants'; import { classes } from './Transition.classes'; import { getSwooshProps, getTimeout, getTransitionStyles } from './utils/utils'; const swooshEdgeMap = { leftToRight: 'left', rightToLeft: 'right', topToBottom: 'top', bottomToTop: 'bottom', }; /** Transition is a wrapper that allows animations of other components. */ const Transition = ({ animateOnLoad = false, dataHook, children, enterAnimation, exitAnimation, mountOnEnter, unmountOnExit, onEnd, onStart, show, className, }) => { const [dimensions, setDimensions] = useState({}); const [stateRef, setStateRef] = useState(null); const ref = useRef(null); // swoosh plays only for children that ran an enter phase const swooshEnteredKeys = useRef(new Set()).current; const swoosh = useMemo(() => { if (!enterAnimation?.swoosh) { return undefined; } const swooshProps = getSwooshProps(enterAnimation.swoosh); return { className: classes.root({ swoosh: swooshEdgeMap[swooshProps.direction], }), delayStyle: swooshProps.delay ? { animationDelay: `${timingMap[swooshProps.delay]}ms` } : undefined, }; }, [enterAnimation]); const swooshExit = useMemo(() => { if (!exitAnimation?.swoosh) { return undefined; } const swooshProps = getSwooshProps(exitAnimation.swoosh); return { className: classes.root({ swooshExit: swooshEdgeMap[swooshProps.direction], }), delayStyle: swooshProps.delay ? { animationDelay: `${timingMap[swooshProps.delay]}ms` } : undefined, }; }, [exitAnimation]); useEffect(() => { if (stateRef) { setDimensions({ height: stateRef.scrollHeight, width: stateRef.scrollWidth, }); } }, [stateRef]); const { fade, move, scale, toggle } = useMemo(() => { if (dimensions) { return getTransitionStyles(dimensions, enterAnimation, exitAnimation); } return { fade: {}, move: {}, scale: {}, toggle: {}, }; }, [enterAnimation, exitAnimation, dimensions]); const timeout = useMemo(() => { return getTimeout(enterAnimation, exitAnimation); }, [enterAnimation, exitAnimation]); const childrenClone = Children.map(children, child => { if (React.isValidElement(child) && (show === undefined || !!show)) { return React.cloneElement(child); } return; }); const handleStart = (animationTiming) => { onStart?.(animationTiming); }; const handleEnd = (animationTiming) => { onEnd?.(animationTiming); }; return (React.createElement("div", { "data-hook": dataHook, className: className }, React.createElement(TransitionGroup, { appear: animateOnLoad }, childrenClone && childrenClone.map(child => { const childKey = child.key; return (React.createElement(ReactTransition // Transition component needs React key property to find correct child in the list , { // Transition component needs React key property to find correct child in the list key: childKey, "data-hook": dataHooks.transition, mountOnEnter: mountOnEnter, unmountOnExit: unmountOnExit, in: show, timeout: { // Transition component won't show enter animation when enter timeout is set enter: 0, exit: timeout.exit, }, // onEnter fires before the entering render onEnter: () => swoosh && swooshEnteredKeys.add(childKey), onEntering: () => handleStart('enter'), onExiting: () => handleStart('exit'), onEntered: () => handleEnd('enter'), onExited: () => { swooshEnteredKeys.delete(childKey); handleEnd('exit'); }, nodeRef: ref }, (state) => { const exiting = state === 'exiting' || state === 'exited'; let activeSwoosh; if (swooshExit && exiting) { activeSwoosh = swooshExit; } else if (swoosh && swooshEnteredKeys.has(childKey)) { activeSwoosh = swoosh; } return (React.createElement("div", { "data-hook": dataHooks.fadeStyles, className: activeSwoosh?.className, style: activeSwoosh?.delayStyle ? { ...fade[state], ...activeSwoosh.delayStyle } : fade[state] }, React.createElement("div", { "data-hook": dataHooks.moveStyles, style: move[state] }, React.createElement("div", { "data-hook": dataHooks.scaleStyles, style: scale[state] }, React.createElement("div", { "data-hook": dataHooks.toggleStyles, style: toggle[state], ref: callback => { setStateRef(callback); ref.current = callback; } }, child))))); })); })))); }; Transition.displayName = 'Transition'; export { Transition }; //# sourceMappingURL=Transition.js.map