import React, { useMemo } from 'react'; import { useEffect, useImperativeHandle } from 'react'; import { animated as a, Controller, useSpring } from '@react-spring/web'; import { useWebMergeStyles } from '@cleartrip/ct-design-style-manager'; import { IAnimatedComponentProps, IAnimationOptions } from '../type'; import { IAnimatedStyleClass } from '../AnimatedStyle/IAnimatedStyleClass'; function getAnimatedStyle(animated: IAnimatedStyleClass) { if (animated.getWebIsSequence()) { const animatedStyles = animated.getWebAnimatedSequenceStyles(); const firstAnimatedStyle = animatedStyles[0][0]; if (!firstAnimatedStyle) { return null; } return firstAnimatedStyle.getWebAnimatedStyle(); } return animated.getWebAnimatedStyle() ?? null; } function getSpringRef(animated: IAnimatedStyleClass) { return animated.getWebSpringRef() ?? null; } function getInitialSpringValue(animated: IAnimatedStyleClass) { if (animated.getWebIsSequence()) { const animatedStyles = animated.getWebAnimatedSequenceStyles(); const initialSpringValue: Record = {}; for (const [animatedStyle] of animatedStyles) { const animatedStyleValue = animatedStyle.getWebAnimatedStyle(); if (animatedStyleValue?.from) { // Only assign properties that don't already exist in mergedFrom Object.keys(animatedStyleValue.from).forEach((key) => { if (!(key in initialSpringValue)) { initialSpringValue[key] = animatedStyleValue.from?.[key]; } }); } } return initialSpringValue; } return animated.getWebAnimatedStyle()?.from ?? {}; } const SpringContainer = ({ children, animated, styleConfig = {}, onTransitionEnd }: IAnimatedComponentProps) => { const { from, to, config: defaultConfig } = getAnimatedStyle(animated) ?? {}; const { springConfig: defaultSpringConfig = {}, delay: defaultDelay } = defaultConfig ?? {}; const { root: rootStyles = [] } = styleConfig || {}; const handleTransitionEnd = () => { onTransitionEnd?.(); }; const [springStyles, springController] = useSpring(() => ({ from, to, config: defaultSpringConfig, delay: defaultDelay, })); // eslint-disable-next-line react-hooks/exhaustive-deps const springAnimation = useMemo(() => new Controller({ ...getInitialSpringValue(animated) }), []); useEffect(() => { if (defaultConfig?.immediate) { getSpringRef(animated)?.current?.start?.(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleSequenceAnimationStart = () => { const animatedStyles = animated.getWebAnimatedSequenceStyles(); const startAnimationAtIndex = (index: number) => { if (index >= animatedStyles.length) { handleTransitionEnd(); return; } const animatedStyle = animatedStyles[index][0]; const animatedStyleValue = animatedStyle.getWebAnimatedStyle(); const delay = animatedStyleValue?.config?.delay ?? defaultDelay; const springConfig = animatedStyleValue?.config?.springConfig ?? {}; springAnimation.start?.({ from: animatedStyleValue?.from, to: animatedStyleValue?.to, config: springConfig, delay, onRest: () => { startAnimationAtIndex(index + 1); }, }); }; startAnimationAtIndex(0); }; useImperativeHandle( getSpringRef(animated), () => { return { start(animatedStyle?: IAnimationOptions) { if (animated.getWebIsSequence()) { handleSequenceAnimationStart(); return; } if (animatedStyle) { const { from = {}, to = {}, config: { springConfig = defaultSpringConfig, delay } = {}, } = animatedStyle ?? {}; springController.start?.({ from, to, config: springConfig, delay, onRest: handleTransitionEnd, }); } else { springController.start?.({ to, config: defaultSpringConfig, delay: defaultDelay, onRest: handleTransitionEnd, }); } }, }; }, // eslint-disable-next-line react-hooks/exhaustive-deps [], ); const mergedStyles = useWebMergeStyles([...rootStyles], [rootStyles]); return ( {children} ); }; export default React.memo(SpringContainer);