Files
2025-03-07 19:22:02 +01:00

78 lines
3.2 KiB
JavaScript

import { useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } 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 { getChildElement } from '../utils/getChildElement';
import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
/**
* 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 = getChildElement(children);
const handleRef = useMotionImperativeRef(imperativeRef);
const elementRef = React.useRef();
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 = elementRef.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,
handleRef,
isReducedMotion,
onMotionFinish,
onMotionStart,
onMotionCancel
]);
return React.cloneElement(children, {
ref: useMergedRefs(elementRef, child.ref)
});
};
return Atom;
}