86 lines
3.4 KiB
JavaScript
86 lines
3.4 KiB
JavaScript
'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
|
|
});
|
|
}
|