Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
@@ -0,0 +1,77 @@
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;
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,123 @@
import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';
import * as React from 'react';
import { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';
import { useAnimateAtoms } from '../hooks/useAnimateAtoms';
import { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';
import { useMountedState } from '../hooks/useMountedState';
import { useIsReducedMotion } from '../hooks/useIsReducedMotion';
import { getChildElement } from '../utils/getChildElement';
import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
/**
* @internal A private symbol to store the motion definition on the component for variants.
*/ export const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');
function shouldSkipAnimation(appear, isFirstMount, visible) {
return !appear && isFirstMount && !!visible;
}
export function createPresenceComponent(value) {
return Object.assign((props)=>{
'use no memo';
const itemContext = React.useContext(PresenceGroupChildContext);
const merged = {
...itemContext,
...props
};
const skipMotions = useMotionBehaviourContext() === 'skip';
const { appear, children, imperativeRef, onExit, onMotionFinish, onMotionStart, onMotionCancel, visible, unmountOnExit, ..._rest } = merged;
const params = _rest;
const [mounted, setMounted] = useMountedState(visible, unmountOnExit);
const child = getChildElement(children);
const handleRef = useMotionImperativeRef(imperativeRef);
const elementRef = React.useRef();
const ref = useMergedRefs(elementRef, child.ref);
const optionsRef = React.useRef({
appear,
params,
skipMotions
});
const animateAtoms = useAnimateAtoms();
const isFirstMount = useFirstMount();
const isReducedMotion = useIsReducedMotion();
const handleMotionStart = useEventCallback((direction)=>{
onMotionStart === null || onMotionStart === void 0 ? void 0 : onMotionStart(null, {
direction
});
});
const handleMotionFinish = useEventCallback((direction)=>{
onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
direction
});
if (direction === 'exit' && unmountOnExit) {
setMounted(false);
onExit === null || onExit === void 0 ? void 0 : onExit();
}
});
const handleMotionCancel = useEventCallback((direction)=>{
onMotionCancel === null || onMotionCancel === void 0 ? void 0 : onMotionCancel(null, {
direction
});
});
useIsomorphicLayoutEffect(()=>{
// Heads up!
// We store the params in a ref to avoid re-rendering the component when the params change.
optionsRef.current = {
appear,
params,
skipMotions
};
});
useIsomorphicLayoutEffect(()=>{
const element = elementRef.current;
if (!element || shouldSkipAnimation(optionsRef.current.appear, isFirstMount, visible)) {
return;
}
const presenceMotion = typeof value === 'function' ? value({
element,
...optionsRef.current.params
}) : value;
const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
const direction = visible ? 'enter' : 'exit';
const applyInitialStyles = !visible && isFirstMount;
const skipAnimation = optionsRef.current.skipMotions;
if (!applyInitialStyles) {
handleMotionStart(direction);
}
const handle = animateAtoms(element, atoms, {
isReducedMotion: isReducedMotion()
});
if (applyInitialStyles) {
// Heads up!
// .finish() is used in this case to skip animation and apply animation styles immediately
handle.finish();
return;
}
handleRef.current = handle;
handle.setMotionEndCallbacks(()=>handleMotionFinish(direction), ()=>handleMotionCancel(direction));
if (skipAnimation) {
handle.finish();
}
return ()=>{
handle.cancel();
};
}, // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders
// eslint-disable-next-line react-hooks/exhaustive-deps
[
animateAtoms,
handleRef,
isReducedMotion,
handleMotionFinish,
handleMotionStart,
handleMotionCancel,
visible
]);
if (mounted) {
return React.cloneElement(child, {
ref
});
}
return null;
}, {
// Heads up!
// Always normalize it to a function to simplify types
[MOTION_DEFINITION]: typeof value === 'function' ? value : ()=>value
});
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,23 @@
import { MOTION_DEFINITION, createPresenceComponent } from './createPresenceComponent';
/**
* @internal
*/ export function overridePresenceMotion(presenceMotion, override) {
return (...args)=>{
const { enter, exit } = presenceMotion(...args);
return {
enter: {
...enter,
...override.all,
...override.enter
},
exit: {
...exit,
...override.all,
...override.exit
}
};
};
}
export function createPresenceComponentVariant(component, override) {
return createPresenceComponent(overridePresenceMotion(component[MOTION_DEFINITION], override));
}
@@ -0,0 +1 @@
{"version":3,"sources":["../src/factories/createPresenceComponentVariant.ts"],"sourcesContent":["import type { MotionParam, PresenceDirection, PresenceMotionFn } from '../types';\nimport { MOTION_DEFINITION, createPresenceComponent, PresenceComponent } from './createPresenceComponent';\n\n/**\n * @internal\n */\ntype PresenceOverrideFields = {\n duration: KeyframeEffectOptions['duration'];\n easing: KeyframeEffectOptions['easing'];\n};\n\n/**\n * @internal\n *\n * Override properties for presence transitions.\n *\n * @example <caption>Override duration for all transitions</caption>\n * ```\n * const override: PresenceOverride = {\n * all: { duration: 1000 },\n * };\n * ```\n *\n * @example <caption>Override easing for exit transition</caption>\n * ```\n * const override: PresenceOverride = {\n * exit: { easing: 'ease-out' },\n * };\n * ```\n */\ntype PresenceOverride = Partial<Record<PresenceDirection | 'all', Partial<PresenceOverrideFields>>>;\n\n/**\n * @internal\n */\nexport function overridePresenceMotion<MotionParams extends Record<string, MotionParam> = {}>(\n presenceMotion: PresenceMotionFn<MotionParams>,\n override: PresenceOverride,\n): PresenceMotionFn<MotionParams> {\n return (...args: Parameters<PresenceMotionFn<MotionParams>>) => {\n const { enter, exit } = presenceMotion(...args);\n\n return {\n enter: { ...enter, ...override.all, ...override.enter },\n exit: { ...exit, ...override.all, ...override.exit },\n };\n };\n}\n\nexport function createPresenceComponentVariant<MotionParams extends Record<string, MotionParam> = {}>(\n component: PresenceComponent<MotionParams>,\n override: PresenceOverride,\n): PresenceComponent<MotionParams> {\n return createPresenceComponent(overridePresenceMotion(component[MOTION_DEFINITION], override));\n}\n"],"names":["MOTION_DEFINITION","createPresenceComponent","overridePresenceMotion","presenceMotion","override","args","enter","exit","all","createPresenceComponentVariant","component"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;","mappings":"AACA,SAASA,iBAAiB,EAAEC,uBAAuB,QAA2B,4BAA4B;AA+B1G;;CAEC,GACD,OAAO,SAASC,uBACdC,cAA8C,EAC9CC,QAA0B;IAE1B,OAAO,CAAC,GAAGC;QACT,MAAM,EAAEC,KAAK,EAAEC,IAAI,EAAE,GAAGJ,kBAAkBE;QAE1C,OAAO;YACLC,OAAO;gBAAE,GAAGA,KAAK;gBAAE,GAAGF,SAASI,GAAG;gBAAE,GAAGJ,SAASE,KAAK;YAAC;YACtDC,MAAM;gBAAE,GAAGA,IAAI;gBAAE,GAAGH,SAASI,GAAG;gBAAE,GAAGJ,SAASG,IAAI;YAAC;QACrD;IACF;AACF;AAEA,OAAO,SAASE,+BACdC,SAA0C,EAC1CN,QAA0B;IAE1B,OAAOH,wBAAwBC,uBAAuBQ,SAAS,CAACV,kBAAkB,EAAEI;AACtF"}