'use client'; import { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; import * as React from 'react'; import { useAnimateAtoms } from '../hooks/useAnimateAtoms'; import { useMotionImperativeRef } from '../hooks/useMotionImperativeRef'; import { useIsReducedMotion } from '../hooks/useIsReducedMotion'; import { useChildElement } from '../utils/useChildElement'; import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext'; /** * A private symbol to store the motion definition on the component for variants. * * @internal */ export const MOTION_DEFINITION = Symbol('MOTION_DEFINITION'); /** * Creates a component that will animate the children using the provided motion. * * @param value - A motion definition. */ export function createMotionComponent(value) { const Atom = (props)=>{ 'use no memo'; const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, ..._rest } = props; const params = _rest; const [child, childRef] = useChildElement(children); const handleRef = useMotionImperativeRef(imperativeRef); const skipMotions = useMotionBehaviourContext() === 'skip'; const optionsRef = React.useRef({ skipMotions, params }); const animateAtoms = useAnimateAtoms(); const isReducedMotion = useIsReducedMotion(); const onMotionStart = useEventCallback(()=>{ onMotionStartProp === null || onMotionStartProp === void 0 ? void 0 : onMotionStartProp(null); }); const onMotionFinish = useEventCallback(()=>{ onMotionFinishProp === null || onMotionFinishProp === void 0 ? void 0 : onMotionFinishProp(null); }); const onMotionCancel = useEventCallback(()=>{ onMotionCancelProp === null || onMotionCancelProp === void 0 ? void 0 : onMotionCancelProp(null); }); useIsomorphicLayoutEffect(()=>{ // Heads up! // We store the params in a ref to avoid re-rendering the component when the params change. optionsRef.current = { skipMotions, params }; }); useIsomorphicLayoutEffect(()=>{ const element = childRef.current; if (element) { const atoms = typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : value; onMotionStart(); const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() }); handleRef.current = handle; handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel); if (optionsRef.current.skipMotions) { handle.finish(); } return ()=>{ handle.cancel(); }; } }, [ animateAtoms, childRef, handleRef, isReducedMotion, onMotionFinish, onMotionStart, onMotionCancel ]); return child; }; return Object.assign(Atom, { // Heads up! // Always normalize it to a function to simplify types [MOTION_DEFINITION]: typeof value === 'function' ? value : ()=>value }); }