import { useEffect, useRef } from 'react'; import useStableMemo from '@cleartrip/ct-design-use-stable-memo'; import { ContainerRef } from '@cleartrip/ct-design-container'; import AnimatedStyle from '../AnimatedStyle/AnimatedStyle'; import { IAnimatedRef, IAnimationOptions } from '../type'; import { makeAnimatedClassNames, updateTransformInlineStyles } from '../utils'; import type { IAnimatedStyleClass } from '../AnimatedStyle/IAnimatedStyleClass'; /** * @description Hook to create animated styles and controller * @param animatedStyle - The animated style to create * @param deps - The dependencies to create the styles * @returns A tuple of the styles and controller * @example * const [styles, controller] = useAnimatedStyles({ * from: { * opacity: 0, * }, * to: { * opacity: 1, * }, * }, [dependencies]); */ export const useAnimatedStyles = (animatedStyle: IAnimationOptions, deps: unknown[]) => { const { from, to, config: animatedConfig = {} } = animatedStyle; const fromStylesRef = useRef(makeAnimatedClassNames(from, animatedConfig)[0]); const elementRef = useRef(null); const classListRef = useRef([fromStylesRef.current]); const springRef = useRef(null); const setElementRef = (element: React.MutableRefObject) => { elementRef.current = element?.current ?? null; }; const styles: IAnimatedStyleClass = useStableMemo(() => { return new AnimatedStyle(animatedStyle, setElementRef, classListRef, springRef as React.RefObject); }, deps); const controller = { /** * @description Start the animation * @param animatedStyle - The animated style to start * @returns void * @example * controller.start?.(); * controller.start?.({ * to: { * opacity: 1, * }, * }); */ start(animatedStyle?: IAnimationOptions, customElementRef?: React.MutableRefObject) { if (animatedConfig.animationType === 'spring') { springRef.current?.start?.(animatedStyle); return; } const element = customElementRef?.current ?? elementRef?.current; const targetTo = animatedStyle?.to ?? to ?? {}; const targetConfig = animatedStyle?.config ?? animatedConfig; const [toStyles, toTransform] = makeAnimatedClassNames(targetTo, targetConfig); if (toTransform) { updateTransformInlineStyles(element, toTransform); } element?.classList?.add?.(toStyles); classListRef.current.push(toStyles); }, }; const controllerRef = useRef(controller); useEffect(() => { controllerRef.current = controller; // eslint-disable-next-line react-hooks/exhaustive-deps }, deps); useEffect(() => { const fromTransform = makeAnimatedClassNames(from, animatedConfig)[1]; if (fromTransform) { updateTransformInlineStyles(elementRef.current, fromTransform); } if (animatedConfig?.immediate) { controllerRef.current.start(); } }, [animatedConfig, from]); return [styles, controllerRef.current] as unknown as [IAnimatedStyleClass, typeof controllerRef.current]; };