@fluentui/react-motion 9.6.9 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -2
- package/dist/index.d.ts +11 -30
- package/lib/factories/createPresenceComponent.js +27 -5
- package/lib/factories/createPresenceComponent.js.map +1 -1
- package/lib/factories/createPresenceComponentVariant.js +24 -18
- package/lib/factories/createPresenceComponentVariant.js.map +1 -1
- package/lib/hooks/useAnimateAtoms.js +37 -9
- package/lib/hooks/useAnimateAtoms.js.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/utils/isAnimationRunning.js +25 -0
- package/lib/utils/isAnimationRunning.js.map +1 -0
- package/lib-commonjs/factories/createPresenceComponent.js +27 -5
- package/lib-commonjs/factories/createPresenceComponent.js.map +1 -1
- package/lib-commonjs/factories/createPresenceComponentVariant.js +13 -20
- package/lib-commonjs/factories/createPresenceComponentVariant.js.map +1 -1
- package/lib-commonjs/hooks/useAnimateAtoms.js +37 -9
- package/lib-commonjs/hooks/useAnimateAtoms.js.map +1 -1
- package/lib-commonjs/utils/isAnimationRunning.js +35 -0
- package/lib-commonjs/utils/isAnimationRunning.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,12 +1,34 @@
|
|
1
1
|
# Change Log - @fluentui/react-motion
|
2
2
|
|
3
|
-
This log was last generated on Wed,
|
3
|
+
This log was last generated on Wed, 26 Mar 2025 21:46:55 GMT and should not be manually modified.
|
4
4
|
|
5
5
|
<!-- Start content -->
|
6
6
|
|
7
|
+
## [9.7.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.7.0)
|
8
|
+
|
9
|
+
Wed, 26 Mar 2025 21:46:55 GMT
|
10
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.6.10..@fluentui/react-motion_v9.7.0)
|
11
|
+
|
12
|
+
### Minor changes
|
13
|
+
|
14
|
+
- fix(motion): make createPresenceComponentVariant support motion arrays ([PR #33996](https://github.com/microsoft/fluentui/pull/33996) by robertpenner@microsoft.com)
|
15
|
+
|
16
|
+
### Patches
|
17
|
+
|
18
|
+
- fix: handle error cases in "element.animate()" ([PR #34096](https://github.com/microsoft/fluentui/pull/34096) by seanmonahan@microsoft.com)
|
19
|
+
|
20
|
+
## [9.6.10](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.6.10)
|
21
|
+
|
22
|
+
Thu, 20 Mar 2025 09:34:58 GMT
|
23
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.6.9..@fluentui/react-motion_v9.6.10)
|
24
|
+
|
25
|
+
### Patches
|
26
|
+
|
27
|
+
- feat: experimental createInterruptablePresence() ([PR #33994](https://github.com/microsoft/fluentui/pull/33994) by olfedias@microsoft.com)
|
28
|
+
|
7
29
|
## [9.6.9](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.6.9)
|
8
30
|
|
9
|
-
Wed, 19 Mar 2025 15:
|
31
|
+
Wed, 19 Mar 2025 15:40:43 GMT
|
10
32
|
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.6.8..@fluentui/react-motion_v9.6.9)
|
11
33
|
|
12
34
|
### Patches
|
package/dist/index.d.ts
CHANGED
@@ -30,7 +30,17 @@ export declare function createMotionComponent<MotionParams extends Record<string
|
|
30
30
|
|
31
31
|
export declare function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(value: PresenceMotion | PresenceMotionFn<MotionParams>): PresenceComponent<MotionParams>;
|
32
32
|
|
33
|
-
|
33
|
+
/**
|
34
|
+
* Create a new presence component based on another presence component,
|
35
|
+
* using the provided variant parameters as defaults.
|
36
|
+
*
|
37
|
+
* @param component - A component created by `createPresenceComponent`.
|
38
|
+
* @param variantParams - An object containing the variant parameters to be used as defaults.
|
39
|
+
* The variant parameters should match the type of the component's motion parameters.
|
40
|
+
* @returns A new presence component that uses the provided variant parameters as defaults.
|
41
|
+
* The new component can still accept runtime parameters that override the defaults.
|
42
|
+
*/
|
43
|
+
export declare function createPresenceComponentVariant<MotionParams extends Record<string, MotionParam> = {}>(component: PresenceComponent<MotionParams>, variantParams: Partial<MotionParams>): PresenceComponent<MotionParams>;
|
34
44
|
|
35
45
|
export declare const curves: {
|
36
46
|
readonly curveAccelerateMax: "cubic-bezier(0.9,0.1,1,0.2)";
|
@@ -242,33 +252,4 @@ export declare type PresenceMotionSlotProps<MotionParams extends Record<string,
|
|
242
252
|
*/
|
243
253
|
declare type PresenceMotionSlotRenderProps = Pick<PresenceComponentProps, 'appear' | 'onMotionFinish' | 'onMotionStart' | 'unmountOnExit' | 'visible'>;
|
244
254
|
|
245
|
-
/**
|
246
|
-
* @internal
|
247
|
-
*
|
248
|
-
* Override properties for presence transitions.
|
249
|
-
*
|
250
|
-
* @example <caption>Override duration for all transitions</caption>
|
251
|
-
* ```
|
252
|
-
* const override: PresenceOverride = {
|
253
|
-
* all: { duration: 1000 },
|
254
|
-
* };
|
255
|
-
* ```
|
256
|
-
*
|
257
|
-
* @example <caption>Override easing for exit transition</caption>
|
258
|
-
* ```
|
259
|
-
* const override: PresenceOverride = {
|
260
|
-
* exit: { easing: 'ease-out' },
|
261
|
-
* };
|
262
|
-
* ```
|
263
|
-
*/
|
264
|
-
declare type PresenceOverride = Partial<Record<PresenceDirection | 'all', Partial<PresenceOverrideFields>>>;
|
265
|
-
|
266
|
-
/**
|
267
|
-
* @internal
|
268
|
-
*/
|
269
|
-
declare type PresenceOverrideFields = {
|
270
|
-
duration: KeyframeEffectOptions['duration'];
|
271
|
-
easing: KeyframeEffectOptions['easing'];
|
272
|
-
};
|
273
|
-
|
274
255
|
export { }
|
@@ -10,6 +10,7 @@ import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
|
|
10
10
|
/**
|
11
11
|
* @internal A private symbol to store the motion definition on the component for variants.
|
12
12
|
*/ export const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');
|
13
|
+
const INTERRUPTABLE_MOTION_SYMBOL = Symbol.for('interruptablePresence');
|
13
14
|
export function createPresenceComponent(value) {
|
14
15
|
return Object.assign((props)=>{
|
15
16
|
'use no memo';
|
@@ -67,10 +68,33 @@ export function createPresenceComponent(value) {
|
|
67
68
|
if (!element) {
|
68
69
|
return;
|
69
70
|
}
|
71
|
+
let handle;
|
72
|
+
function cleanup() {
|
73
|
+
if (!handle) {
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
// Heads up!
|
77
|
+
//
|
78
|
+
// If the animation is interruptible & is running, we don't want to cancel it as it will be reversed in
|
79
|
+
// the next effect.
|
80
|
+
if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION && handle.isRunning()) {
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
handle.cancel();
|
84
|
+
handleRef.current = undefined;
|
85
|
+
}
|
70
86
|
const presenceMotion = typeof value === 'function' ? value({
|
71
87
|
element,
|
72
88
|
...optionsRef.current.params
|
73
89
|
}) : value;
|
90
|
+
const IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION = presenceMotion[INTERRUPTABLE_MOTION_SYMBOL];
|
91
|
+
if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION) {
|
92
|
+
handle = handleRef.current;
|
93
|
+
if (handle && handle.isRunning()) {
|
94
|
+
handle.reverse();
|
95
|
+
return cleanup;
|
96
|
+
}
|
97
|
+
}
|
74
98
|
const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
|
75
99
|
const direction = visible ? 'enter' : 'exit';
|
76
100
|
// Heads up!
|
@@ -80,23 +104,21 @@ export function createPresenceComponent(value) {
|
|
80
104
|
if (!applyInitialStyles) {
|
81
105
|
handleMotionStart(direction);
|
82
106
|
}
|
83
|
-
|
107
|
+
handle = animateAtoms(element, atoms, {
|
84
108
|
isReducedMotion: isReducedMotion()
|
85
109
|
});
|
86
110
|
if (applyInitialStyles) {
|
87
111
|
// Heads up!
|
88
112
|
// .finish() is used in this case to skip animation and apply animation styles immediately
|
89
113
|
handle.finish();
|
90
|
-
return;
|
114
|
+
return cleanup;
|
91
115
|
}
|
92
116
|
handleRef.current = handle;
|
93
117
|
handle.setMotionEndCallbacks(()=>handleMotionFinish(direction), ()=>handleMotionCancel(direction));
|
94
118
|
if (skipAnimationByConfig) {
|
95
119
|
handle.finish();
|
96
120
|
}
|
97
|
-
return
|
98
|
-
handle.cancel();
|
99
|
-
};
|
121
|
+
return cleanup;
|
100
122
|
}, // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders
|
101
123
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
102
124
|
[
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { getChildElement } from '../utils/getChildElement';\nimport type { MotionParam, PresenceMotion, MotionImperativeRef, PresenceMotionFn, PresenceDirection } from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * @internal A private symbol to store the motion definition on the component for variants.\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type PresenceComponentProps = {\n /**\n * By default, the child component won't execute the \"enter\" motion when it initially mounts, regardless of the value\n * of \"visible\". If you desire this behavior, ensure both \"appear\" and \"visible\" are set to \"true\".\n */\n appear?: boolean;\n\n /** A React element that will be cloned and will have motion effects applied to it. */\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled. When a motion is cancelled it does not\n * emit a finish event but a specific cancel event\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionCancel?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion starts.\n *\n * A motion definition can contain multiple animations and therefore multiple \"start\" events. The callback is\n * triggered when the first animation is started. There is no official \"start\" event with the Web Animations API.\n * so the callback is triggered with \"null\".\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionStart?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /** Defines whether a component is visible; triggers the \"enter\" or \"exit\" motions. */\n visible?: boolean;\n\n /**\n * By default, the child component remains mounted after it reaches the \"finished\" state. Set \"unmountOnExit\" if\n * you prefer to unmount the component after it finishes exiting.\n */\n unmountOnExit?: boolean;\n};\n\nexport type PresenceComponent<MotionParams extends Record<string, MotionParam> = {}> = {\n (props: PresenceComponentProps & MotionParams): React.ReactElement | null;\n [MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n};\n\nexport function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: PresenceMotion | PresenceMotionFn<MotionParams>,\n): PresenceComponent<MotionParams> {\n return Object.assign(\n (props: PresenceComponentProps & MotionParams) => {\n 'use no memo';\n\n const itemContext = React.useContext(PresenceGroupChildContext);\n const merged = { ...itemContext, ...props };\n const skipMotions = useMotionBehaviourContext() === 'skip';\n\n const {\n appear,\n children,\n imperativeRef,\n onExit,\n onMotionFinish,\n onMotionStart,\n onMotionCancel,\n visible,\n unmountOnExit,\n ..._rest\n } = merged;\n const params = _rest as Exclude<typeof merged, PresenceComponentProps | typeof itemContext>;\n\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef<{ appear?: boolean; params: MotionParams; skipMotions: boolean }>({\n appear,\n params,\n skipMotions,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n\n const handleMotionStart = useEventCallback((direction: PresenceDirection) => {\n onMotionStart?.(null, { direction });\n });\n const handleMotionFinish = useEventCallback((direction: PresenceDirection) => {\n onMotionFinish?.(null, { direction });\n\n if (direction === 'exit' && unmountOnExit) {\n setMounted(false);\n onExit?.();\n }\n });\n\n const handleMotionCancel = useEventCallback((direction: PresenceDirection) => {\n onMotionCancel?.(null, { direction });\n });\n\n useIsomorphicLayoutEffect(() => {\n // Heads up!\n // We store the params in a ref to avoid re-rendering the component when the params change.\n optionsRef.current = { appear, params, skipMotions };\n });\n\n useIsomorphicLayoutEffect(\n () => {\n const element = elementRef.current;\n\n if (!element) {\n return;\n }\n\n const presenceMotion =\n typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : (value as PresenceMotion);\n\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n const direction: PresenceDirection = visible ? 'enter' : 'exit';\n\n // Heads up!\n // Initial styles are applied when the component is mounted for the first time and \"appear\" is set to \"false\" (otherwise animations are triggered)\n const applyInitialStyles = !optionsRef.current.appear && isFirstMount;\n const skipAnimationByConfig = optionsRef.current.skipMotions;\n\n if (!applyInitialStyles) {\n handleMotionStart(direction);\n }\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n if (applyInitialStyles) {\n // Heads up!\n // .finish() is used in this case to skip animation and apply animation styles immediately\n handle.finish();\n return;\n }\n\n handleRef.current = handle;\n handle.setMotionEndCallbacks(\n () => handleMotionFinish(direction),\n () => handleMotionCancel(direction),\n );\n\n if (skipAnimationByConfig) {\n handle.finish();\n }\n\n return () => {\n handle.cancel();\n };\n },\n // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [animateAtoms, handleRef, isReducedMotion, handleMotionFinish, handleMotionStart, handleMotionCancel, visible],\n );\n\n if (mounted) {\n return React.cloneElement(child, { ref });\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n );\n}\n"],"names":["useEventCallback","useFirstMount","useIsomorphicLayoutEffect","useMergedRefs","React","PresenceGroupChildContext","useAnimateAtoms","useMotionImperativeRef","useMountedState","useIsReducedMotion","getChildElement","useMotionBehaviourContext","MOTION_DEFINITION","Symbol","createPresenceComponent","value","Object","assign","props","itemContext","useContext","merged","skipMotions","appear","children","imperativeRef","onExit","onMotionFinish","onMotionStart","onMotionCancel","visible","unmountOnExit","_rest","params","mounted","setMounted","child","handleRef","elementRef","useRef","ref","optionsRef","animateAtoms","isFirstMount","isReducedMotion","handleMotionStart","direction","handleMotionFinish","handleMotionCancel","current","element","presenceMotion","atoms","enter","exit","applyInitialStyles","skipAnimationByConfig","handle","finish","setMotionEndCallbacks","cancel","cloneElement"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
1
|
+
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { getChildElement } from '../utils/getChildElement';\nimport type {\n MotionParam,\n PresenceMotion,\n MotionImperativeRef,\n PresenceMotionFn,\n PresenceDirection,\n AnimationHandle,\n} from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * @internal A private symbol to store the motion definition on the component for variants.\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type PresenceComponentProps = {\n /**\n * By default, the child component won't execute the \"enter\" motion when it initially mounts, regardless of the value\n * of \"visible\". If you desire this behavior, ensure both \"appear\" and \"visible\" are set to \"true\".\n */\n appear?: boolean;\n\n /** A React element that will be cloned and will have motion effects applied to it. */\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled. When a motion is cancelled it does not\n * emit a finish event but a specific cancel event\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionCancel?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion starts.\n *\n * A motion definition can contain multiple animations and therefore multiple \"start\" events. The callback is\n * triggered when the first animation is started. There is no official \"start\" event with the Web Animations API.\n * so the callback is triggered with \"null\".\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionStart?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /** Defines whether a component is visible; triggers the \"enter\" or \"exit\" motions. */\n visible?: boolean;\n\n /**\n * By default, the child component remains mounted after it reaches the \"finished\" state. Set \"unmountOnExit\" if\n * you prefer to unmount the component after it finishes exiting.\n */\n unmountOnExit?: boolean;\n};\n\nexport type PresenceComponent<MotionParams extends Record<string, MotionParam> = {}> = {\n (props: PresenceComponentProps & MotionParams): React.ReactElement | null;\n [MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n};\n\nconst INTERRUPTABLE_MOTION_SYMBOL = Symbol.for('interruptablePresence');\n\nexport function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: PresenceMotion | PresenceMotionFn<MotionParams>,\n): PresenceComponent<MotionParams> {\n return Object.assign(\n (props: PresenceComponentProps & MotionParams) => {\n 'use no memo';\n\n const itemContext = React.useContext(PresenceGroupChildContext);\n const merged = { ...itemContext, ...props };\n const skipMotions = useMotionBehaviourContext() === 'skip';\n\n const {\n appear,\n children,\n imperativeRef,\n onExit,\n onMotionFinish,\n onMotionStart,\n onMotionCancel,\n visible,\n unmountOnExit,\n ..._rest\n } = merged;\n const params = _rest as Exclude<typeof merged, PresenceComponentProps | typeof itemContext>;\n\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef<{ appear?: boolean; params: MotionParams; skipMotions: boolean }>({\n appear,\n params,\n skipMotions,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n\n const handleMotionStart = useEventCallback((direction: PresenceDirection) => {\n onMotionStart?.(null, { direction });\n });\n const handleMotionFinish = useEventCallback((direction: PresenceDirection) => {\n onMotionFinish?.(null, { direction });\n\n if (direction === 'exit' && unmountOnExit) {\n setMounted(false);\n onExit?.();\n }\n });\n\n const handleMotionCancel = useEventCallback((direction: PresenceDirection) => {\n onMotionCancel?.(null, { direction });\n });\n\n useIsomorphicLayoutEffect(() => {\n // Heads up!\n // We store the params in a ref to avoid re-rendering the component when the params change.\n optionsRef.current = { appear, params, skipMotions };\n });\n\n useIsomorphicLayoutEffect(\n () => {\n const element = elementRef.current;\n\n if (!element) {\n return;\n }\n\n let handle: AnimationHandle | undefined;\n\n function cleanup() {\n if (!handle) {\n return;\n }\n\n // Heads up!\n //\n // If the animation is interruptible & is running, we don't want to cancel it as it will be reversed in\n // the next effect.\n if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION && handle.isRunning()) {\n return;\n }\n\n handle.cancel();\n handleRef.current = undefined;\n }\n\n const presenceMotion =\n typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : (value as PresenceMotion);\n const IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION = (\n presenceMotion as PresenceMotion & { [INTERRUPTABLE_MOTION_SYMBOL]?: boolean }\n )[INTERRUPTABLE_MOTION_SYMBOL];\n\n if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION) {\n handle = handleRef.current;\n\n if (handle && handle.isRunning()) {\n handle.reverse();\n\n return cleanup;\n }\n }\n\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n const direction: PresenceDirection = visible ? 'enter' : 'exit';\n\n // Heads up!\n // Initial styles are applied when the component is mounted for the first time and \"appear\" is set to \"false\" (otherwise animations are triggered)\n const applyInitialStyles = !optionsRef.current.appear && isFirstMount;\n const skipAnimationByConfig = optionsRef.current.skipMotions;\n\n if (!applyInitialStyles) {\n handleMotionStart(direction);\n }\n\n handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n if (applyInitialStyles) {\n // Heads up!\n // .finish() is used in this case to skip animation and apply animation styles immediately\n handle.finish();\n\n return cleanup;\n }\n\n handleRef.current = handle;\n handle.setMotionEndCallbacks(\n () => handleMotionFinish(direction),\n () => handleMotionCancel(direction),\n );\n\n if (skipAnimationByConfig) {\n handle.finish();\n }\n\n return cleanup;\n },\n // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [animateAtoms, handleRef, isReducedMotion, handleMotionFinish, handleMotionStart, handleMotionCancel, visible],\n );\n\n if (mounted) {\n return React.cloneElement(child, { ref });\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n );\n}\n"],"names":["useEventCallback","useFirstMount","useIsomorphicLayoutEffect","useMergedRefs","React","PresenceGroupChildContext","useAnimateAtoms","useMotionImperativeRef","useMountedState","useIsReducedMotion","getChildElement","useMotionBehaviourContext","MOTION_DEFINITION","Symbol","INTERRUPTABLE_MOTION_SYMBOL","for","createPresenceComponent","value","Object","assign","props","itemContext","useContext","merged","skipMotions","appear","children","imperativeRef","onExit","onMotionFinish","onMotionStart","onMotionCancel","visible","unmountOnExit","_rest","params","mounted","setMounted","child","handleRef","elementRef","useRef","ref","optionsRef","animateAtoms","isFirstMount","isReducedMotion","handleMotionStart","direction","handleMotionFinish","handleMotionCancel","current","element","handle","cleanup","IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION","isRunning","cancel","undefined","presenceMotion","reverse","atoms","enter","exit","applyInitialStyles","skipAnimationByConfig","finish","setMotionEndCallbacks","cloneElement"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
@@ -1,23 +1,29 @@
|
|
1
1
|
import { MOTION_DEFINITION, createPresenceComponent } from './createPresenceComponent';
|
2
2
|
/**
|
3
3
|
* @internal
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
...exit,
|
15
|
-
...override.all,
|
16
|
-
...override.exit
|
17
|
-
}
|
18
|
-
};
|
19
|
-
};
|
4
|
+
*
|
5
|
+
* Create a variant function that wraps a presence function to customize it.
|
6
|
+
* The new presence function has the supplied variant params as defaults,
|
7
|
+
* but these can still be overridden by runtime params when the new function is called.
|
8
|
+
*/ export function createPresenceFnVariant(presenceFn, variantParams) {
|
9
|
+
const variantFn = (runtimeParams)=>presenceFn({
|
10
|
+
...variantParams,
|
11
|
+
...runtimeParams
|
12
|
+
});
|
13
|
+
return variantFn;
|
20
14
|
}
|
21
|
-
|
22
|
-
|
15
|
+
/**
|
16
|
+
* Create a new presence component based on another presence component,
|
17
|
+
* using the provided variant parameters as defaults.
|
18
|
+
*
|
19
|
+
* @param component - A component created by `createPresenceComponent`.
|
20
|
+
* @param variantParams - An object containing the variant parameters to be used as defaults.
|
21
|
+
* The variant parameters should match the type of the component's motion parameters.
|
22
|
+
* @returns A new presence component that uses the provided variant parameters as defaults.
|
23
|
+
* The new component can still accept runtime parameters that override the defaults.
|
24
|
+
*/ export function createPresenceComponentVariant(component, variantParams) {
|
25
|
+
const originalFn = component[MOTION_DEFINITION];
|
26
|
+
// The variant params become new defaults, but they can still be be overridden by runtime params.
|
27
|
+
const variantFn = createPresenceFnVariant(originalFn, variantParams);
|
28
|
+
return createPresenceComponent(variantFn);
|
23
29
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/factories/createPresenceComponentVariant.ts"],"sourcesContent":["import type { MotionParam,
|
1
|
+
{"version":3,"sources":["../src/factories/createPresenceComponentVariant.ts"],"sourcesContent":["import type { MotionParam, PresenceMotionFn } from '../types';\nimport { MOTION_DEFINITION, createPresenceComponent, PresenceComponent } from './createPresenceComponent';\n\n/**\n * @internal\n *\n * Create a variant function that wraps a presence function to customize it.\n * The new presence function has the supplied variant params as defaults,\n * but these can still be overridden by runtime params when the new function is called.\n */\nexport function createPresenceFnVariant<MotionParams extends Record<string, MotionParam> = {}>(\n presenceFn: PresenceMotionFn<MotionParams>,\n variantParams: Partial<MotionParams>,\n): typeof presenceFn {\n const variantFn: typeof presenceFn = runtimeParams => presenceFn({ ...variantParams, ...runtimeParams });\n return variantFn;\n}\n\n/**\n * Create a new presence component based on another presence component,\n * using the provided variant parameters as defaults.\n *\n * @param component - A component created by `createPresenceComponent`.\n * @param variantParams - An object containing the variant parameters to be used as defaults.\n * The variant parameters should match the type of the component's motion parameters.\n * @returns A new presence component that uses the provided variant parameters as defaults.\n * The new component can still accept runtime parameters that override the defaults.\n */\nexport function createPresenceComponentVariant<MotionParams extends Record<string, MotionParam> = {}>(\n component: PresenceComponent<MotionParams>,\n variantParams: Partial<MotionParams>,\n): PresenceComponent<MotionParams> {\n const originalFn = component[MOTION_DEFINITION];\n // The variant params become new defaults, but they can still be be overridden by runtime params.\n const variantFn = createPresenceFnVariant(originalFn, variantParams);\n return createPresenceComponent(variantFn);\n}\n"],"names":["MOTION_DEFINITION","createPresenceComponent","createPresenceFnVariant","presenceFn","variantParams","variantFn","runtimeParams","createPresenceComponentVariant","component","originalFn"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AACA,SAASA,iBAAiB,EAAEC,uBAAuB,QAA2B,4BAA4B;AAE1G;;;;;;CAMC,GACD,OAAO,SAASC,wBACdC,UAA0C,EAC1CC,aAAoC;IAEpC,MAAMC,YAA+BC,CAAAA,gBAAiBH,WAAW;YAAE,GAAGC,aAAa;YAAE,GAAGE,aAAa;QAAC;IACtG,OAAOD;AACT;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASE,+BACdC,SAA0C,EAC1CJ,aAAoC;IAEpC,MAAMK,aAAaD,SAAS,CAACR,kBAAkB;IAC/C,iGAAiG;IACjG,MAAMK,YAAYH,wBAAwBO,YAAYL;IACtD,OAAOH,wBAAwBI;AACjC"}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import * as React from 'react';
|
2
|
+
import { isAnimationRunning } from '../utils/isAnimationRunning';
|
2
3
|
export const DEFAULT_ANIMATION_OPTIONS = {
|
3
4
|
fill: 'forwards'
|
4
5
|
};
|
@@ -28,16 +29,24 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
28
29
|
// Use reduced motion overrides (e.g. duration, easing) when reduced motion is enabled
|
29
30
|
...isReducedMotion && reducedMotionParams
|
30
31
|
};
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
try {
|
33
|
+
// Firefox can throw an error when calling `element.animate()`.
|
34
|
+
// See: https://github.com/microsoft/fluentui/issues/33902
|
35
|
+
const animation = element.animate(animationKeyframes, animationParams);
|
36
|
+
if (SUPPORTS_PERSIST) {
|
37
|
+
// Chromium browsers can return null when calling `element.animate()`.
|
38
|
+
// See: https://github.com/microsoft/fluentui/issues/33902
|
39
|
+
animation === null || animation === void 0 ? void 0 : animation.persist();
|
40
|
+
} else {
|
41
|
+
const resultKeyframe = animationKeyframes[animationKeyframes.length - 1];
|
42
|
+
var _element_style;
|
43
|
+
Object.assign((_element_style = element.style) !== null && _element_style !== void 0 ? _element_style : {}, resultKeyframe);
|
44
|
+
}
|
45
|
+
return animation;
|
46
|
+
} catch (e) {
|
47
|
+
return null;
|
38
48
|
}
|
39
|
-
|
40
|
-
});
|
49
|
+
}).filter((animation)=>!!animation);
|
41
50
|
return {
|
42
51
|
set playbackRate (rate){
|
43
52
|
animations.forEach((animation)=>{
|
@@ -60,6 +69,9 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
60
69
|
oncancel();
|
61
70
|
});
|
62
71
|
},
|
72
|
+
isRunning () {
|
73
|
+
return animations.some((animation)=>isAnimationRunning(animation));
|
74
|
+
},
|
63
75
|
cancel: ()=>{
|
64
76
|
animations.forEach((animation)=>{
|
65
77
|
animation.cancel();
|
@@ -79,6 +91,17 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
79
91
|
animations.forEach((animation)=>{
|
80
92
|
animation.finish();
|
81
93
|
});
|
94
|
+
},
|
95
|
+
reverse: ()=>{
|
96
|
+
// Heads up!
|
97
|
+
//
|
98
|
+
// This is used for the interruptible motion. If the animation is running, we need to reverse it.
|
99
|
+
//
|
100
|
+
// TODO: what do with animations that have "delay"?
|
101
|
+
// TODO: what do with animations that have different "durations"?
|
102
|
+
animations.forEach((animation)=>{
|
103
|
+
animation.reverse();
|
104
|
+
});
|
82
105
|
}
|
83
106
|
};
|
84
107
|
}, [
|
@@ -116,6 +139,9 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
116
139
|
},
|
117
140
|
set playbackRate (rate){
|
118
141
|
/* no-op */ },
|
142
|
+
isRunning () {
|
143
|
+
return false;
|
144
|
+
},
|
119
145
|
cancel () {
|
120
146
|
/* no-op */ },
|
121
147
|
pause () {
|
@@ -123,6 +149,8 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
123
149
|
play () {
|
124
150
|
/* no-op */ },
|
125
151
|
finish () {
|
152
|
+
/* no-op */ },
|
153
|
+
reverse () {
|
126
154
|
/* no-op */ }
|
127
155
|
};
|
128
156
|
}, [
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["import * as React from 'react';\nimport type { AnimationHandle, AtomMotion } from '../types';\n\nexport const DEFAULT_ANIMATION_OPTIONS: KeyframeEffectOptions = {\n fill: 'forwards',\n};\n\n// A motion atom's default reduced motion is a simple 1 ms duration.\n// But an atom can define a custom reduced motion, overriding keyframes and/or params like duration, easing, iterations, etc.\nconst DEFAULT_REDUCED_MOTION_ATOM: NonNullable<AtomMotion['reducedMotion']> = {\n duration: 1,\n};\n\nfunction useAnimateAtomsInSupportedEnvironment() {\n // eslint-disable-next-line @nx/workspace-no-restricted-globals\n const SUPPORTS_PERSIST = typeof window !== 'undefined' && typeof window.Animation?.prototype.persist === 'function';\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const atoms = Array.isArray(value) ? value : [value];\n const { isReducedMotion } = options;\n\n const animations = atoms.map(motion => {\n // Grab the custom reduced motion definition if it exists, or fall back to the default reduced motion.\n const { keyframes: motionKeyframes, reducedMotion = DEFAULT_REDUCED_MOTION_ATOM, ...params } = motion;\n // Grab the reduced motion keyframes if they exist, or fall back to the regular keyframes.\n const { keyframes: reducedMotionKeyframes = motionKeyframes, ...reducedMotionParams } = reducedMotion;\n\n const animationKeyframes: Keyframe[] = isReducedMotion ? reducedMotionKeyframes : motionKeyframes;\n const animationParams: KeyframeEffectOptions = {\n ...DEFAULT_ANIMATION_OPTIONS,\n ...params,\n\n // Use reduced motion overrides (e.g. duration, easing) when reduced motion is enabled\n ...(isReducedMotion && reducedMotionParams),\n };\n\n const animation = element.animate(animationKeyframes, animationParams);\n\n if (SUPPORTS_PERSIST) {\n animation.persist();\n } else {\n const resultKeyframe = animationKeyframes[animationKeyframes.length - 1];\n Object.assign(element.style ?? {}, resultKeyframe);\n }\n\n return animation;\n });\n\n return {\n set playbackRate(rate: number) {\n animations.forEach(animation => {\n animation.playbackRate = rate;\n });\n },\n setMotionEndCallbacks(onfinish: () => void, oncancel: () => void) {\n // Heads up!\n // This could use \"Animation:finished\", but it's causing a memory leak in Chromium.\n // See: https://issues.chromium.org/u/2/issues/383016426\n const promises = animations.map(animation => {\n return new Promise<void>((resolve, reject) => {\n animation.onfinish = () => resolve();\n animation.oncancel = () => reject();\n });\n });\n\n Promise.all(promises)\n .then(() => {\n onfinish();\n })\n .catch(() => {\n oncancel();\n });\n },\n\n cancel: () => {\n animations.forEach(animation => {\n animation.cancel();\n });\n },\n pause: () => {\n animations.forEach(animation => {\n animation.pause();\n });\n },\n play: () => {\n animations.forEach(animation => {\n animation.play();\n });\n },\n finish: () => {\n animations.forEach(animation => {\n animation.finish();\n });\n },\n };\n },\n [SUPPORTS_PERSIST],\n );\n}\n\n/**\n * In test environments, this hook is used to delay the execution of a callback until the next render. This is necessary\n * to ensure that the callback is not executed synchronously, which would cause the test to fail.\n *\n * @see https://github.com/microsoft/fluentui/issues/31701\n */\nfunction useAnimateAtomsInTestEnvironment() {\n const [count, setCount] = React.useState(0);\n const callbackRef = React.useRef<() => void>();\n\n const realAnimateAtoms = useAnimateAtomsInSupportedEnvironment();\n\n React.useEffect(() => {\n if (count > 0) {\n callbackRef.current?.();\n }\n }, [count]);\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const ELEMENT_SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n\n // Heads up!\n // If the environment supports Web Animations API, we can use the native implementation.\n if (ELEMENT_SUPPORTS_WEB_ANIMATIONS) {\n return realAnimateAtoms(element, value, options);\n }\n\n return {\n setMotionEndCallbacks(onfinish: () => void) {\n callbackRef.current = onfinish;\n setCount(v => v + 1);\n },\n\n set playbackRate(rate: number) {\n /* no-op */\n },\n cancel() {\n /* no-op */\n },\n pause() {\n /* no-op */\n },\n play() {\n /* no-op */\n },\n finish() {\n /* no-op */\n },\n };\n },\n [realAnimateAtoms],\n );\n}\n\n/**\n * @internal\n */\nexport function useAnimateAtoms() {\n 'use no memo';\n\n if (process.env.NODE_ENV === 'test') {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInTestEnvironment();\n }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInSupportedEnvironment();\n}\n"],"names":["React","DEFAULT_ANIMATION_OPTIONS","fill","DEFAULT_REDUCED_MOTION_ATOM","duration","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animation","animate","resultKeyframe","length","Object","assign","style","playbackRate","rate","forEach","setMotionEndCallbacks","onfinish","oncancel","promises","Promise","resolve","reject","all","then","catch","cancel","pause","play","finish","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","useAnimateAtoms","process","env","NODE_ENV"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
1
|
+
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { isAnimationRunning } from '../utils/isAnimationRunning';\nimport type { AnimationHandle, AtomMotion } from '../types';\n\nexport const DEFAULT_ANIMATION_OPTIONS: KeyframeEffectOptions = {\n fill: 'forwards',\n};\n\n// A motion atom's default reduced motion is a simple 1 ms duration.\n// But an atom can define a custom reduced motion, overriding keyframes and/or params like duration, easing, iterations, etc.\nconst DEFAULT_REDUCED_MOTION_ATOM: NonNullable<AtomMotion['reducedMotion']> = {\n duration: 1,\n};\n\nfunction useAnimateAtomsInSupportedEnvironment() {\n // eslint-disable-next-line @nx/workspace-no-restricted-globals\n const SUPPORTS_PERSIST = typeof window !== 'undefined' && typeof window.Animation?.prototype.persist === 'function';\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const atoms = Array.isArray(value) ? value : [value];\n const { isReducedMotion } = options;\n\n const animations = atoms\n .map(motion => {\n // Grab the custom reduced motion definition if it exists, or fall back to the default reduced motion.\n const { keyframes: motionKeyframes, reducedMotion = DEFAULT_REDUCED_MOTION_ATOM, ...params } = motion;\n // Grab the reduced motion keyframes if they exist, or fall back to the regular keyframes.\n const { keyframes: reducedMotionKeyframes = motionKeyframes, ...reducedMotionParams } = reducedMotion;\n\n const animationKeyframes: Keyframe[] = isReducedMotion ? reducedMotionKeyframes : motionKeyframes;\n const animationParams: KeyframeEffectOptions = {\n ...DEFAULT_ANIMATION_OPTIONS,\n ...params,\n\n // Use reduced motion overrides (e.g. duration, easing) when reduced motion is enabled\n ...(isReducedMotion && reducedMotionParams),\n };\n\n try {\n // Firefox can throw an error when calling `element.animate()`.\n // See: https://github.com/microsoft/fluentui/issues/33902\n const animation = element.animate(animationKeyframes, animationParams);\n\n if (SUPPORTS_PERSIST) {\n // Chromium browsers can return null when calling `element.animate()`.\n // See: https://github.com/microsoft/fluentui/issues/33902\n animation?.persist();\n } else {\n const resultKeyframe = animationKeyframes[animationKeyframes.length - 1];\n Object.assign(element.style ?? {}, resultKeyframe);\n }\n\n return animation;\n } catch (e) {\n return null;\n }\n })\n .filter(animation => !!animation) as Animation[];\n\n return {\n set playbackRate(rate: number) {\n animations.forEach(animation => {\n animation.playbackRate = rate;\n });\n },\n setMotionEndCallbacks(onfinish: () => void, oncancel: () => void) {\n // Heads up!\n // This could use \"Animation:finished\", but it's causing a memory leak in Chromium.\n // See: https://issues.chromium.org/u/2/issues/383016426\n const promises = animations.map(animation => {\n return new Promise<void>((resolve, reject) => {\n animation.onfinish = () => resolve();\n animation.oncancel = () => reject();\n });\n });\n\n Promise.all(promises)\n .then(() => {\n onfinish();\n })\n .catch(() => {\n oncancel();\n });\n },\n isRunning() {\n return animations.some(animation => isAnimationRunning(animation));\n },\n\n cancel: () => {\n animations.forEach(animation => {\n animation.cancel();\n });\n },\n pause: () => {\n animations.forEach(animation => {\n animation.pause();\n });\n },\n play: () => {\n animations.forEach(animation => {\n animation.play();\n });\n },\n finish: () => {\n animations.forEach(animation => {\n animation.finish();\n });\n },\n reverse: () => {\n // Heads up!\n //\n // This is used for the interruptible motion. If the animation is running, we need to reverse it.\n //\n // TODO: what do with animations that have \"delay\"?\n // TODO: what do with animations that have different \"durations\"?\n\n animations.forEach(animation => {\n animation.reverse();\n });\n },\n };\n },\n [SUPPORTS_PERSIST],\n );\n}\n\n/**\n * In test environments, this hook is used to delay the execution of a callback until the next render. This is necessary\n * to ensure that the callback is not executed synchronously, which would cause the test to fail.\n *\n * @see https://github.com/microsoft/fluentui/issues/31701\n */\nfunction useAnimateAtomsInTestEnvironment() {\n const [count, setCount] = React.useState(0);\n const callbackRef = React.useRef<() => void>();\n\n const realAnimateAtoms = useAnimateAtomsInSupportedEnvironment();\n\n React.useEffect(() => {\n if (count > 0) {\n callbackRef.current?.();\n }\n }, [count]);\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const ELEMENT_SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n\n // Heads up!\n // If the environment supports Web Animations API, we can use the native implementation.\n if (ELEMENT_SUPPORTS_WEB_ANIMATIONS) {\n return realAnimateAtoms(element, value, options);\n }\n\n return {\n setMotionEndCallbacks(onfinish: () => void) {\n callbackRef.current = onfinish;\n setCount(v => v + 1);\n },\n\n set playbackRate(rate: number) {\n /* no-op */\n },\n isRunning() {\n return false;\n },\n\n cancel() {\n /* no-op */\n },\n pause() {\n /* no-op */\n },\n play() {\n /* no-op */\n },\n finish() {\n /* no-op */\n },\n reverse() {\n /* no-op */\n },\n };\n },\n [realAnimateAtoms],\n );\n}\n\n/**\n * @internal\n */\nexport function useAnimateAtoms() {\n 'use no memo';\n\n if (process.env.NODE_ENV === 'test') {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInTestEnvironment();\n }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInSupportedEnvironment();\n}\n"],"names":["React","isAnimationRunning","DEFAULT_ANIMATION_OPTIONS","fill","DEFAULT_REDUCED_MOTION_ATOM","duration","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animation","animate","resultKeyframe","length","Object","assign","style","e","filter","playbackRate","rate","forEach","setMotionEndCallbacks","onfinish","oncancel","promises","Promise","resolve","reject","all","then","catch","isRunning","some","cancel","pause","play","finish","reverse","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","useAnimateAtoms","process","env","NODE_ENV"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
package/lib/types.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["type AtomCore = { keyframes: Keyframe[] } & KeyframeEffectOptions;\n\nexport type AtomMotion = AtomCore & {\n /**\n * Allows to specify a reduced motion version of the animation. If provided, the settings will be used when the\n * user has enabled the reduced motion setting in the operating system (i.e `prefers-reduced-motion` media query is\n * active). If not provided, the duration of the animation will be overridden to be 1ms.\n *\n * Note, if `keyframes` are provided, they will be used instead of the regular `keyframes`.\n */\n reducedMotion?: Partial<AtomCore>;\n};\n\nexport type PresenceDirection = 'enter' | 'exit';\n\nexport type PresenceMotion = Record<PresenceDirection, AtomMotion | AtomMotion[]>;\n\n/**\n * A motion param should be a primitive value that can be serialized to JSON and could be potentially used a plain\n * dependency for React hooks.\n */\nexport type MotionParam = boolean | number | string;\n\nexport type AtomMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (\n params: { element: HTMLElement } & MotionParams,\n) => AtomMotion | AtomMotion[];\n\nexport type PresenceMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (\n params: { element: HTMLElement } & MotionParams,\n) => PresenceMotion;\n\n// ---\n\nexport type AnimationHandle = Pick<Animation, 'cancel' | 'finish' | 'pause' | 'play' | 'playbackRate'> & {\n setMotionEndCallbacks: (onfinish: () => void, oncancel: () => void) => void;\n};\n\nexport type MotionImperativeRef = {\n /** Sets the playback rate of the animation, where 1 is normal speed. */\n setPlaybackRate: (rate: number) => void;\n\n /** Sets the state of the animation to running or paused. */\n setPlayState: (state: 'running' | 'paused') => void;\n};\n"],"names":[],"rangeMappings":"","mappings":"
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["type AtomCore = { keyframes: Keyframe[] } & KeyframeEffectOptions;\n\nexport type AtomMotion = AtomCore & {\n /**\n * Allows to specify a reduced motion version of the animation. If provided, the settings will be used when the\n * user has enabled the reduced motion setting in the operating system (i.e `prefers-reduced-motion` media query is\n * active). If not provided, the duration of the animation will be overridden to be 1ms.\n *\n * Note, if `keyframes` are provided, they will be used instead of the regular `keyframes`.\n */\n reducedMotion?: Partial<AtomCore>;\n};\n\nexport type PresenceDirection = 'enter' | 'exit';\n\nexport type PresenceMotion = Record<PresenceDirection, AtomMotion | AtomMotion[]>;\n\n/**\n * A motion param should be a primitive value that can be serialized to JSON and could be potentially used a plain\n * dependency for React hooks.\n */\nexport type MotionParam = boolean | number | string;\n\nexport type AtomMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (\n params: { element: HTMLElement } & MotionParams,\n) => AtomMotion | AtomMotion[];\n\nexport type PresenceMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (\n params: { element: HTMLElement } & MotionParams,\n) => PresenceMotion;\n\n// ---\n\nexport type AnimationHandle = Pick<Animation, 'cancel' | 'finish' | 'pause' | 'play' | 'playbackRate' | 'reverse'> & {\n setMotionEndCallbacks: (onfinish: () => void, oncancel: () => void) => void;\n isRunning: () => boolean;\n};\n\nexport type MotionImperativeRef = {\n /** Sets the playback rate of the animation, where 1 is normal speed. */\n setPlaybackRate: (rate: number) => void;\n\n /** Sets the state of the animation to running or paused. */\n setPlayState: (state: 'running' | 'paused') => void;\n};\n"],"names":[],"rangeMappings":"","mappings":"AAsCA,WAME"}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/**
|
2
|
+
* Checks if the animation is running at the moment.
|
3
|
+
*/ export function isAnimationRunning(animation) {
|
4
|
+
if (animation.playState === 'running') {
|
5
|
+
var _animation_effect;
|
6
|
+
// Heads up!
|
7
|
+
//
|
8
|
+
// There is an edge case where the animation is running, but the overall progress is 0 or 1. In this case, we
|
9
|
+
// consider the animation to be not running. If it will be reversed it will flip from 1 to 0, and we will observe a
|
10
|
+
// glitch.
|
11
|
+
// "overallProgress" is not supported in all browsers, so we need to check if it exists.
|
12
|
+
// We will fall back to the currentTime and duration if "overallProgress" is not supported.
|
13
|
+
if (animation.overallProgress !== undefined) {
|
14
|
+
var _animation_overallProgress;
|
15
|
+
const overallProgress = (_animation_overallProgress = animation.overallProgress) !== null && _animation_overallProgress !== void 0 ? _animation_overallProgress : 0;
|
16
|
+
return overallProgress > 0 && overallProgress < 1;
|
17
|
+
}
|
18
|
+
var _animation_currentTime;
|
19
|
+
const currentTime = Number((_animation_currentTime = animation.currentTime) !== null && _animation_currentTime !== void 0 ? _animation_currentTime : 0);
|
20
|
+
var _animation_effect_getTiming_duration;
|
21
|
+
const totalTime = Number((_animation_effect_getTiming_duration = (_animation_effect = animation.effect) === null || _animation_effect === void 0 ? void 0 : _animation_effect.getTiming().duration) !== null && _animation_effect_getTiming_duration !== void 0 ? _animation_effect_getTiming_duration : 0);
|
22
|
+
return currentTime > 0 && currentTime < totalTime;
|
23
|
+
}
|
24
|
+
return false;
|
25
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../src/utils/isAnimationRunning.ts"],"sourcesContent":["/**\n * Checks if the animation is running at the moment.\n */\nexport function isAnimationRunning(animation: Animation & { readonly overallProgress?: number | null }) {\n if (animation.playState === 'running') {\n // Heads up!\n //\n // There is an edge case where the animation is running, but the overall progress is 0 or 1. In this case, we\n // consider the animation to be not running. If it will be reversed it will flip from 1 to 0, and we will observe a\n // glitch.\n\n // \"overallProgress\" is not supported in all browsers, so we need to check if it exists.\n // We will fall back to the currentTime and duration if \"overallProgress\" is not supported.\n if (animation.overallProgress !== undefined) {\n const overallProgress = animation.overallProgress ?? 0;\n\n return overallProgress > 0 && overallProgress < 1;\n }\n\n const currentTime = Number(animation.currentTime ?? 0);\n const totalTime = Number(animation.effect?.getTiming().duration ?? 0);\n\n return currentTime > 0 && currentTime < totalTime;\n }\n\n return false;\n}\n"],"names":["isAnimationRunning","animation","playState","overallProgress","undefined","currentTime","Number","totalTime","effect","getTiming","duration"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA;;CAEC,GACD,OAAO,SAASA,mBAAmBC,SAAmE;IACpG,IAAIA,UAAUC,SAAS,KAAK,WAAW;YAgBZD;QAfzB,YAAY;QACZ,EAAE;QACF,6GAA6G;QAC7G,mHAAmH;QACnH,UAAU;QAEV,wFAAwF;QACxF,2FAA2F;QAC3F,IAAIA,UAAUE,eAAe,KAAKC,WAAW;gBACnBH;YAAxB,MAAME,kBAAkBF,CAAAA,6BAAAA,UAAUE,eAAe,cAAzBF,wCAAAA,6BAA6B;YAErD,OAAOE,kBAAkB,KAAKA,kBAAkB;QAClD;YAE2BF;QAA3B,MAAMI,cAAcC,OAAOL,CAAAA,yBAAAA,UAAUI,WAAW,cAArBJ,oCAAAA,yBAAyB;YAC3BA;QAAzB,MAAMM,YAAYD,OAAOL,CAAAA,wCAAAA,oBAAAA,UAAUO,MAAM,cAAhBP,wCAAAA,kBAAkBQ,SAAS,GAAGC,QAAQ,cAAtCT,kDAAAA,uCAA0C;QAEnE,OAAOI,cAAc,KAAKA,cAAcE;IAC1C;IAEA,OAAO;AACT"}
|
@@ -27,6 +27,7 @@ const _useIsReducedMotion = require("../hooks/useIsReducedMotion");
|
|
27
27
|
const _getChildElement = require("../utils/getChildElement");
|
28
28
|
const _MotionBehaviourContext = require("../contexts/MotionBehaviourContext");
|
29
29
|
const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');
|
30
|
+
const INTERRUPTABLE_MOTION_SYMBOL = Symbol.for('interruptablePresence');
|
30
31
|
function createPresenceComponent(value) {
|
31
32
|
return Object.assign((props)=>{
|
32
33
|
'use no memo';
|
@@ -84,10 +85,33 @@ function createPresenceComponent(value) {
|
|
84
85
|
if (!element) {
|
85
86
|
return;
|
86
87
|
}
|
88
|
+
let handle;
|
89
|
+
function cleanup() {
|
90
|
+
if (!handle) {
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
// Heads up!
|
94
|
+
//
|
95
|
+
// If the animation is interruptible & is running, we don't want to cancel it as it will be reversed in
|
96
|
+
// the next effect.
|
97
|
+
if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION && handle.isRunning()) {
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
handle.cancel();
|
101
|
+
handleRef.current = undefined;
|
102
|
+
}
|
87
103
|
const presenceMotion = typeof value === 'function' ? value({
|
88
104
|
element,
|
89
105
|
...optionsRef.current.params
|
90
106
|
}) : value;
|
107
|
+
const IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION = presenceMotion[INTERRUPTABLE_MOTION_SYMBOL];
|
108
|
+
if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION) {
|
109
|
+
handle = handleRef.current;
|
110
|
+
if (handle && handle.isRunning()) {
|
111
|
+
handle.reverse();
|
112
|
+
return cleanup;
|
113
|
+
}
|
114
|
+
}
|
91
115
|
const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
|
92
116
|
const direction = visible ? 'enter' : 'exit';
|
93
117
|
// Heads up!
|
@@ -97,23 +121,21 @@ function createPresenceComponent(value) {
|
|
97
121
|
if (!applyInitialStyles) {
|
98
122
|
handleMotionStart(direction);
|
99
123
|
}
|
100
|
-
|
124
|
+
handle = animateAtoms(element, atoms, {
|
101
125
|
isReducedMotion: isReducedMotion()
|
102
126
|
});
|
103
127
|
if (applyInitialStyles) {
|
104
128
|
// Heads up!
|
105
129
|
// .finish() is used in this case to skip animation and apply animation styles immediately
|
106
130
|
handle.finish();
|
107
|
-
return;
|
131
|
+
return cleanup;
|
108
132
|
}
|
109
133
|
handleRef.current = handle;
|
110
134
|
handle.setMotionEndCallbacks(()=>handleMotionFinish(direction), ()=>handleMotionCancel(direction));
|
111
135
|
if (skipAnimationByConfig) {
|
112
136
|
handle.finish();
|
113
137
|
}
|
114
|
-
return
|
115
|
-
handle.cancel();
|
116
|
-
};
|
138
|
+
return cleanup;
|
117
139
|
}, // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders
|
118
140
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
119
141
|
[
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { getChildElement } from '../utils/getChildElement';\nimport type { MotionParam, PresenceMotion, MotionImperativeRef, PresenceMotionFn, PresenceDirection } from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * @internal A private symbol to store the motion definition on the component for variants.\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type PresenceComponentProps = {\n /**\n * By default, the child component won't execute the \"enter\" motion when it initially mounts, regardless of the value\n * of \"visible\". If you desire this behavior, ensure both \"appear\" and \"visible\" are set to \"true\".\n */\n appear?: boolean;\n\n /** A React element that will be cloned and will have motion effects applied to it. */\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled. When a motion is cancelled it does not\n * emit a finish event but a specific cancel event\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionCancel?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion starts.\n *\n * A motion definition can contain multiple animations and therefore multiple \"start\" events. The callback is\n * triggered when the first animation is started. There is no official \"start\" event with the Web Animations API.\n * so the callback is triggered with \"null\".\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionStart?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /** Defines whether a component is visible; triggers the \"enter\" or \"exit\" motions. */\n visible?: boolean;\n\n /**\n * By default, the child component remains mounted after it reaches the \"finished\" state. Set \"unmountOnExit\" if\n * you prefer to unmount the component after it finishes exiting.\n */\n unmountOnExit?: boolean;\n};\n\nexport type PresenceComponent<MotionParams extends Record<string, MotionParam> = {}> = {\n (props: PresenceComponentProps & MotionParams): React.ReactElement | null;\n [MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n};\n\nexport function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: PresenceMotion | PresenceMotionFn<MotionParams>,\n): PresenceComponent<MotionParams> {\n return Object.assign(\n (props: PresenceComponentProps & MotionParams) => {\n 'use no memo';\n\n const itemContext = React.useContext(PresenceGroupChildContext);\n const merged = { ...itemContext, ...props };\n const skipMotions = useMotionBehaviourContext() === 'skip';\n\n const {\n appear,\n children,\n imperativeRef,\n onExit,\n onMotionFinish,\n onMotionStart,\n onMotionCancel,\n visible,\n unmountOnExit,\n ..._rest\n } = merged;\n const params = _rest as Exclude<typeof merged, PresenceComponentProps | typeof itemContext>;\n\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef<{ appear?: boolean; params: MotionParams; skipMotions: boolean }>({\n appear,\n params,\n skipMotions,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n\n const handleMotionStart = useEventCallback((direction: PresenceDirection) => {\n onMotionStart?.(null, { direction });\n });\n const handleMotionFinish = useEventCallback((direction: PresenceDirection) => {\n onMotionFinish?.(null, { direction });\n\n if (direction === 'exit' && unmountOnExit) {\n setMounted(false);\n onExit?.();\n }\n });\n\n const handleMotionCancel = useEventCallback((direction: PresenceDirection) => {\n onMotionCancel?.(null, { direction });\n });\n\n useIsomorphicLayoutEffect(() => {\n // Heads up!\n // We store the params in a ref to avoid re-rendering the component when the params change.\n optionsRef.current = { appear, params, skipMotions };\n });\n\n useIsomorphicLayoutEffect(\n () => {\n const element = elementRef.current;\n\n if (!element) {\n return;\n }\n\n const presenceMotion =\n typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : (value as PresenceMotion);\n\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n const direction: PresenceDirection = visible ? 'enter' : 'exit';\n\n // Heads up!\n // Initial styles are applied when the component is mounted for the first time and \"appear\" is set to \"false\" (otherwise animations are triggered)\n const applyInitialStyles = !optionsRef.current.appear && isFirstMount;\n const skipAnimationByConfig = optionsRef.current.skipMotions;\n\n if (!applyInitialStyles) {\n handleMotionStart(direction);\n }\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n if (applyInitialStyles) {\n // Heads up!\n // .finish() is used in this case to skip animation and apply animation styles immediately\n handle.finish();\n return;\n }\n\n handleRef.current = handle;\n handle.setMotionEndCallbacks(\n () => handleMotionFinish(direction),\n () => handleMotionCancel(direction),\n );\n\n if (skipAnimationByConfig) {\n handle.finish();\n }\n\n return () => {\n handle.cancel();\n };\n },\n // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [animateAtoms, handleRef, isReducedMotion, handleMotionFinish, handleMotionStart, handleMotionCancel, visible],\n );\n\n if (mounted) {\n return React.cloneElement(child, { ref });\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n );\n}\n"],"names":["MOTION_DEFINITION","createPresenceComponent","Symbol","value","Object","assign","props","itemContext","React","useContext","PresenceGroupChildContext","merged","skipMotions","useMotionBehaviourContext","appear","children","imperativeRef","onExit","onMotionFinish","onMotionStart","onMotionCancel","visible","unmountOnExit","_rest","params","mounted","setMounted","useMountedState","child","getChildElement","handleRef","useMotionImperativeRef","elementRef","useRef","ref","useMergedRefs","optionsRef","animateAtoms","useAnimateAtoms","isFirstMount","useFirstMount","isReducedMotion","useIsReducedMotion","handleMotionStart","useEventCallback","direction","handleMotionFinish","handleMotionCancel","useIsomorphicLayoutEffect","current","element","presenceMotion","atoms","enter","exit","applyInitialStyles","skipAnimationByConfig","handle","finish","setMotionEndCallbacks","cancel","cloneElement"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
1
|
+
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { getChildElement } from '../utils/getChildElement';\nimport type {\n MotionParam,\n PresenceMotion,\n MotionImperativeRef,\n PresenceMotionFn,\n PresenceDirection,\n AnimationHandle,\n} from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * @internal A private symbol to store the motion definition on the component for variants.\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type PresenceComponentProps = {\n /**\n * By default, the child component won't execute the \"enter\" motion when it initially mounts, regardless of the value\n * of \"visible\". If you desire this behavior, ensure both \"appear\" and \"visible\" are set to \"true\".\n */\n appear?: boolean;\n\n /** A React element that will be cloned and will have motion effects applied to it. */\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled. When a motion is cancelled it does not\n * emit a finish event but a specific cancel event\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionCancel?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /**\n * Callback that is called when the whole motion starts.\n *\n * A motion definition can contain multiple animations and therefore multiple \"start\" events. The callback is\n * triggered when the first animation is started. There is no official \"start\" event with the Web Animations API.\n * so the callback is triggered with \"null\".\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionStart?: (ev: null, data: { direction: PresenceDirection }) => void;\n\n /** Defines whether a component is visible; triggers the \"enter\" or \"exit\" motions. */\n visible?: boolean;\n\n /**\n * By default, the child component remains mounted after it reaches the \"finished\" state. Set \"unmountOnExit\" if\n * you prefer to unmount the component after it finishes exiting.\n */\n unmountOnExit?: boolean;\n};\n\nexport type PresenceComponent<MotionParams extends Record<string, MotionParam> = {}> = {\n (props: PresenceComponentProps & MotionParams): React.ReactElement | null;\n [MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n};\n\nconst INTERRUPTABLE_MOTION_SYMBOL = Symbol.for('interruptablePresence');\n\nexport function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: PresenceMotion | PresenceMotionFn<MotionParams>,\n): PresenceComponent<MotionParams> {\n return Object.assign(\n (props: PresenceComponentProps & MotionParams) => {\n 'use no memo';\n\n const itemContext = React.useContext(PresenceGroupChildContext);\n const merged = { ...itemContext, ...props };\n const skipMotions = useMotionBehaviourContext() === 'skip';\n\n const {\n appear,\n children,\n imperativeRef,\n onExit,\n onMotionFinish,\n onMotionStart,\n onMotionCancel,\n visible,\n unmountOnExit,\n ..._rest\n } = merged;\n const params = _rest as Exclude<typeof merged, PresenceComponentProps | typeof itemContext>;\n\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef<{ appear?: boolean; params: MotionParams; skipMotions: boolean }>({\n appear,\n params,\n skipMotions,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n\n const handleMotionStart = useEventCallback((direction: PresenceDirection) => {\n onMotionStart?.(null, { direction });\n });\n const handleMotionFinish = useEventCallback((direction: PresenceDirection) => {\n onMotionFinish?.(null, { direction });\n\n if (direction === 'exit' && unmountOnExit) {\n setMounted(false);\n onExit?.();\n }\n });\n\n const handleMotionCancel = useEventCallback((direction: PresenceDirection) => {\n onMotionCancel?.(null, { direction });\n });\n\n useIsomorphicLayoutEffect(() => {\n // Heads up!\n // We store the params in a ref to avoid re-rendering the component when the params change.\n optionsRef.current = { appear, params, skipMotions };\n });\n\n useIsomorphicLayoutEffect(\n () => {\n const element = elementRef.current;\n\n if (!element) {\n return;\n }\n\n let handle: AnimationHandle | undefined;\n\n function cleanup() {\n if (!handle) {\n return;\n }\n\n // Heads up!\n //\n // If the animation is interruptible & is running, we don't want to cancel it as it will be reversed in\n // the next effect.\n if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION && handle.isRunning()) {\n return;\n }\n\n handle.cancel();\n handleRef.current = undefined;\n }\n\n const presenceMotion =\n typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : (value as PresenceMotion);\n const IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION = (\n presenceMotion as PresenceMotion & { [INTERRUPTABLE_MOTION_SYMBOL]?: boolean }\n )[INTERRUPTABLE_MOTION_SYMBOL];\n\n if (IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION) {\n handle = handleRef.current;\n\n if (handle && handle.isRunning()) {\n handle.reverse();\n\n return cleanup;\n }\n }\n\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n const direction: PresenceDirection = visible ? 'enter' : 'exit';\n\n // Heads up!\n // Initial styles are applied when the component is mounted for the first time and \"appear\" is set to \"false\" (otherwise animations are triggered)\n const applyInitialStyles = !optionsRef.current.appear && isFirstMount;\n const skipAnimationByConfig = optionsRef.current.skipMotions;\n\n if (!applyInitialStyles) {\n handleMotionStart(direction);\n }\n\n handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n if (applyInitialStyles) {\n // Heads up!\n // .finish() is used in this case to skip animation and apply animation styles immediately\n handle.finish();\n\n return cleanup;\n }\n\n handleRef.current = handle;\n handle.setMotionEndCallbacks(\n () => handleMotionFinish(direction),\n () => handleMotionCancel(direction),\n );\n\n if (skipAnimationByConfig) {\n handle.finish();\n }\n\n return cleanup;\n },\n // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [animateAtoms, handleRef, isReducedMotion, handleMotionFinish, handleMotionStart, handleMotionCancel, visible],\n );\n\n if (mounted) {\n return React.cloneElement(child, { ref });\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n );\n}\n"],"names":["MOTION_DEFINITION","createPresenceComponent","Symbol","INTERRUPTABLE_MOTION_SYMBOL","for","value","Object","assign","props","itemContext","React","useContext","PresenceGroupChildContext","merged","skipMotions","useMotionBehaviourContext","appear","children","imperativeRef","onExit","onMotionFinish","onMotionStart","onMotionCancel","visible","unmountOnExit","_rest","params","mounted","setMounted","useMountedState","child","getChildElement","handleRef","useMotionImperativeRef","elementRef","useRef","ref","useMergedRefs","optionsRef","animateAtoms","useAnimateAtoms","isFirstMount","useFirstMount","isReducedMotion","useIsReducedMotion","handleMotionStart","useEventCallback","direction","handleMotionFinish","handleMotionCancel","useIsomorphicLayoutEffect","current","element","handle","cleanup","IS_EXPERIMENTAL_INTERRUPTIBLE_MOTION","isRunning","cancel","undefined","presenceMotion","reverse","atoms","enter","exit","applyInitialStyles","skipAnimationByConfig","finish","setMotionEndCallbacks","cloneElement"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
@@ -12,28 +12,21 @@ _export(exports, {
|
|
12
12
|
createPresenceComponentVariant: function() {
|
13
13
|
return createPresenceComponentVariant;
|
14
14
|
},
|
15
|
-
|
16
|
-
return
|
15
|
+
createPresenceFnVariant: function() {
|
16
|
+
return createPresenceFnVariant;
|
17
17
|
}
|
18
18
|
});
|
19
19
|
const _createPresenceComponent = require("./createPresenceComponent");
|
20
|
-
function
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
...override.all,
|
27
|
-
...override.enter
|
28
|
-
},
|
29
|
-
exit: {
|
30
|
-
...exit,
|
31
|
-
...override.all,
|
32
|
-
...override.exit
|
33
|
-
}
|
34
|
-
};
|
35
|
-
};
|
20
|
+
function createPresenceFnVariant(presenceFn, variantParams) {
|
21
|
+
const variantFn = (runtimeParams)=>presenceFn({
|
22
|
+
...variantParams,
|
23
|
+
...runtimeParams
|
24
|
+
});
|
25
|
+
return variantFn;
|
36
26
|
}
|
37
|
-
function createPresenceComponentVariant(component,
|
38
|
-
|
27
|
+
function createPresenceComponentVariant(component, variantParams) {
|
28
|
+
const originalFn = component[_createPresenceComponent.MOTION_DEFINITION];
|
29
|
+
// The variant params become new defaults, but they can still be be overridden by runtime params.
|
30
|
+
const variantFn = createPresenceFnVariant(originalFn, variantParams);
|
31
|
+
return (0, _createPresenceComponent.createPresenceComponent)(variantFn);
|
39
32
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/factories/createPresenceComponentVariant.ts"],"sourcesContent":["import type { MotionParam,
|
1
|
+
{"version":3,"sources":["../src/factories/createPresenceComponentVariant.ts"],"sourcesContent":["import type { MotionParam, PresenceMotionFn } from '../types';\nimport { MOTION_DEFINITION, createPresenceComponent, PresenceComponent } from './createPresenceComponent';\n\n/**\n * @internal\n *\n * Create a variant function that wraps a presence function to customize it.\n * The new presence function has the supplied variant params as defaults,\n * but these can still be overridden by runtime params when the new function is called.\n */\nexport function createPresenceFnVariant<MotionParams extends Record<string, MotionParam> = {}>(\n presenceFn: PresenceMotionFn<MotionParams>,\n variantParams: Partial<MotionParams>,\n): typeof presenceFn {\n const variantFn: typeof presenceFn = runtimeParams => presenceFn({ ...variantParams, ...runtimeParams });\n return variantFn;\n}\n\n/**\n * Create a new presence component based on another presence component,\n * using the provided variant parameters as defaults.\n *\n * @param component - A component created by `createPresenceComponent`.\n * @param variantParams - An object containing the variant parameters to be used as defaults.\n * The variant parameters should match the type of the component's motion parameters.\n * @returns A new presence component that uses the provided variant parameters as defaults.\n * The new component can still accept runtime parameters that override the defaults.\n */\nexport function createPresenceComponentVariant<MotionParams extends Record<string, MotionParam> = {}>(\n component: PresenceComponent<MotionParams>,\n variantParams: Partial<MotionParams>,\n): PresenceComponent<MotionParams> {\n const originalFn = component[MOTION_DEFINITION];\n // The variant params become new defaults, but they can still be be overridden by runtime params.\n const variantFn = createPresenceFnVariant(originalFn, variantParams);\n return createPresenceComponent(variantFn);\n}\n"],"names":["createPresenceComponentVariant","createPresenceFnVariant","presenceFn","variantParams","variantFn","runtimeParams","component","originalFn","MOTION_DEFINITION","createPresenceComponent"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":";;;;;;;;;;;IA4BgBA,8BAA8B;eAA9BA;;IAlBAC,uBAAuB;eAAvBA;;;yCAT8D;AASvE,SAASA,wBACdC,UAA0C,EAC1CC,aAAoC;IAEpC,MAAMC,YAA+BC,CAAAA,gBAAiBH,WAAW;YAAE,GAAGC,aAAa;YAAE,GAAGE,aAAa;QAAC;IACtG,OAAOD;AACT;AAYO,SAASJ,+BACdM,SAA0C,EAC1CH,aAAoC;IAEpC,MAAMI,aAAaD,SAAS,CAACE,0CAAiB,CAAC;IAC/C,iGAAiG;IACjG,MAAMJ,YAAYH,wBAAwBM,YAAYJ;IACtD,OAAOM,IAAAA,gDAAuB,EAACL;AACjC"}
|
@@ -18,6 +18,7 @@ _export(exports, {
|
|
18
18
|
});
|
19
19
|
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
20
20
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
21
|
+
const _isAnimationRunning = require("../utils/isAnimationRunning");
|
21
22
|
const DEFAULT_ANIMATION_OPTIONS = {
|
22
23
|
fill: 'forwards'
|
23
24
|
};
|
@@ -47,16 +48,24 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
47
48
|
// Use reduced motion overrides (e.g. duration, easing) when reduced motion is enabled
|
48
49
|
...isReducedMotion && reducedMotionParams
|
49
50
|
};
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
try {
|
52
|
+
// Firefox can throw an error when calling `element.animate()`.
|
53
|
+
// See: https://github.com/microsoft/fluentui/issues/33902
|
54
|
+
const animation = element.animate(animationKeyframes, animationParams);
|
55
|
+
if (SUPPORTS_PERSIST) {
|
56
|
+
// Chromium browsers can return null when calling `element.animate()`.
|
57
|
+
// See: https://github.com/microsoft/fluentui/issues/33902
|
58
|
+
animation === null || animation === void 0 ? void 0 : animation.persist();
|
59
|
+
} else {
|
60
|
+
const resultKeyframe = animationKeyframes[animationKeyframes.length - 1];
|
61
|
+
var _element_style;
|
62
|
+
Object.assign((_element_style = element.style) !== null && _element_style !== void 0 ? _element_style : {}, resultKeyframe);
|
63
|
+
}
|
64
|
+
return animation;
|
65
|
+
} catch (e) {
|
66
|
+
return null;
|
57
67
|
}
|
58
|
-
|
59
|
-
});
|
68
|
+
}).filter((animation)=>!!animation);
|
60
69
|
return {
|
61
70
|
set playbackRate (rate){
|
62
71
|
animations.forEach((animation)=>{
|
@@ -79,6 +88,9 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
79
88
|
oncancel();
|
80
89
|
});
|
81
90
|
},
|
91
|
+
isRunning () {
|
92
|
+
return animations.some((animation)=>(0, _isAnimationRunning.isAnimationRunning)(animation));
|
93
|
+
},
|
82
94
|
cancel: ()=>{
|
83
95
|
animations.forEach((animation)=>{
|
84
96
|
animation.cancel();
|
@@ -98,6 +110,17 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
98
110
|
animations.forEach((animation)=>{
|
99
111
|
animation.finish();
|
100
112
|
});
|
113
|
+
},
|
114
|
+
reverse: ()=>{
|
115
|
+
// Heads up!
|
116
|
+
//
|
117
|
+
// This is used for the interruptible motion. If the animation is running, we need to reverse it.
|
118
|
+
//
|
119
|
+
// TODO: what do with animations that have "delay"?
|
120
|
+
// TODO: what do with animations that have different "durations"?
|
121
|
+
animations.forEach((animation)=>{
|
122
|
+
animation.reverse();
|
123
|
+
});
|
101
124
|
}
|
102
125
|
};
|
103
126
|
}, [
|
@@ -135,6 +158,9 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
135
158
|
},
|
136
159
|
set playbackRate (rate){
|
137
160
|
/* no-op */ },
|
161
|
+
isRunning () {
|
162
|
+
return false;
|
163
|
+
},
|
138
164
|
cancel () {
|
139
165
|
/* no-op */ },
|
140
166
|
pause () {
|
@@ -142,6 +168,8 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
142
168
|
play () {
|
143
169
|
/* no-op */ },
|
144
170
|
finish () {
|
171
|
+
/* no-op */ },
|
172
|
+
reverse () {
|
145
173
|
/* no-op */ }
|
146
174
|
};
|
147
175
|
}, [
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["import * as React from 'react';\nimport type { AnimationHandle, AtomMotion } from '../types';\n\nexport const DEFAULT_ANIMATION_OPTIONS: KeyframeEffectOptions = {\n fill: 'forwards',\n};\n\n// A motion atom's default reduced motion is a simple 1 ms duration.\n// But an atom can define a custom reduced motion, overriding keyframes and/or params like duration, easing, iterations, etc.\nconst DEFAULT_REDUCED_MOTION_ATOM: NonNullable<AtomMotion['reducedMotion']> = {\n duration: 1,\n};\n\nfunction useAnimateAtomsInSupportedEnvironment() {\n // eslint-disable-next-line @nx/workspace-no-restricted-globals\n const SUPPORTS_PERSIST = typeof window !== 'undefined' && typeof window.Animation?.prototype.persist === 'function';\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const atoms = Array.isArray(value) ? value : [value];\n const { isReducedMotion } = options;\n\n const animations = atoms.map(motion => {\n // Grab the custom reduced motion definition if it exists, or fall back to the default reduced motion.\n const { keyframes: motionKeyframes, reducedMotion = DEFAULT_REDUCED_MOTION_ATOM, ...params } = motion;\n // Grab the reduced motion keyframes if they exist, or fall back to the regular keyframes.\n const { keyframes: reducedMotionKeyframes = motionKeyframes, ...reducedMotionParams } = reducedMotion;\n\n const animationKeyframes: Keyframe[] = isReducedMotion ? reducedMotionKeyframes : motionKeyframes;\n const animationParams: KeyframeEffectOptions = {\n ...DEFAULT_ANIMATION_OPTIONS,\n ...params,\n\n // Use reduced motion overrides (e.g. duration, easing) when reduced motion is enabled\n ...(isReducedMotion && reducedMotionParams),\n };\n\n const animation = element.animate(animationKeyframes, animationParams);\n\n if (SUPPORTS_PERSIST) {\n animation.persist();\n } else {\n const resultKeyframe = animationKeyframes[animationKeyframes.length - 1];\n Object.assign(element.style ?? {}, resultKeyframe);\n }\n\n return animation;\n });\n\n return {\n set playbackRate(rate: number) {\n animations.forEach(animation => {\n animation.playbackRate = rate;\n });\n },\n setMotionEndCallbacks(onfinish: () => void, oncancel: () => void) {\n // Heads up!\n // This could use \"Animation:finished\", but it's causing a memory leak in Chromium.\n // See: https://issues.chromium.org/u/2/issues/383016426\n const promises = animations.map(animation => {\n return new Promise<void>((resolve, reject) => {\n animation.onfinish = () => resolve();\n animation.oncancel = () => reject();\n });\n });\n\n Promise.all(promises)\n .then(() => {\n onfinish();\n })\n .catch(() => {\n oncancel();\n });\n },\n\n cancel: () => {\n animations.forEach(animation => {\n animation.cancel();\n });\n },\n pause: () => {\n animations.forEach(animation => {\n animation.pause();\n });\n },\n play: () => {\n animations.forEach(animation => {\n animation.play();\n });\n },\n finish: () => {\n animations.forEach(animation => {\n animation.finish();\n });\n },\n };\n },\n [SUPPORTS_PERSIST],\n );\n}\n\n/**\n * In test environments, this hook is used to delay the execution of a callback until the next render. This is necessary\n * to ensure that the callback is not executed synchronously, which would cause the test to fail.\n *\n * @see https://github.com/microsoft/fluentui/issues/31701\n */\nfunction useAnimateAtomsInTestEnvironment() {\n const [count, setCount] = React.useState(0);\n const callbackRef = React.useRef<() => void>();\n\n const realAnimateAtoms = useAnimateAtomsInSupportedEnvironment();\n\n React.useEffect(() => {\n if (count > 0) {\n callbackRef.current?.();\n }\n }, [count]);\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const ELEMENT_SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n\n // Heads up!\n // If the environment supports Web Animations API, we can use the native implementation.\n if (ELEMENT_SUPPORTS_WEB_ANIMATIONS) {\n return realAnimateAtoms(element, value, options);\n }\n\n return {\n setMotionEndCallbacks(onfinish: () => void) {\n callbackRef.current = onfinish;\n setCount(v => v + 1);\n },\n\n set playbackRate(rate: number) {\n /* no-op */\n },\n cancel() {\n /* no-op */\n },\n pause() {\n /* no-op */\n },\n play() {\n /* no-op */\n },\n finish() {\n /* no-op */\n },\n };\n },\n [realAnimateAtoms],\n );\n}\n\n/**\n * @internal\n */\nexport function useAnimateAtoms() {\n 'use no memo';\n\n if (process.env.NODE_ENV === 'test') {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInTestEnvironment();\n }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInSupportedEnvironment();\n}\n"],"names":["DEFAULT_ANIMATION_OPTIONS","useAnimateAtoms","fill","DEFAULT_REDUCED_MOTION_ATOM","duration","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","React","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animation","animate","resultKeyframe","length","Object","assign","style","playbackRate","rate","forEach","setMotionEndCallbacks","onfinish","oncancel","promises","Promise","resolve","reject","all","then","catch","cancel","pause","play","finish","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","process","env","NODE_ENV"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
1
|
+
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { isAnimationRunning } from '../utils/isAnimationRunning';\nimport type { AnimationHandle, AtomMotion } from '../types';\n\nexport const DEFAULT_ANIMATION_OPTIONS: KeyframeEffectOptions = {\n fill: 'forwards',\n};\n\n// A motion atom's default reduced motion is a simple 1 ms duration.\n// But an atom can define a custom reduced motion, overriding keyframes and/or params like duration, easing, iterations, etc.\nconst DEFAULT_REDUCED_MOTION_ATOM: NonNullable<AtomMotion['reducedMotion']> = {\n duration: 1,\n};\n\nfunction useAnimateAtomsInSupportedEnvironment() {\n // eslint-disable-next-line @nx/workspace-no-restricted-globals\n const SUPPORTS_PERSIST = typeof window !== 'undefined' && typeof window.Animation?.prototype.persist === 'function';\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const atoms = Array.isArray(value) ? value : [value];\n const { isReducedMotion } = options;\n\n const animations = atoms\n .map(motion => {\n // Grab the custom reduced motion definition if it exists, or fall back to the default reduced motion.\n const { keyframes: motionKeyframes, reducedMotion = DEFAULT_REDUCED_MOTION_ATOM, ...params } = motion;\n // Grab the reduced motion keyframes if they exist, or fall back to the regular keyframes.\n const { keyframes: reducedMotionKeyframes = motionKeyframes, ...reducedMotionParams } = reducedMotion;\n\n const animationKeyframes: Keyframe[] = isReducedMotion ? reducedMotionKeyframes : motionKeyframes;\n const animationParams: KeyframeEffectOptions = {\n ...DEFAULT_ANIMATION_OPTIONS,\n ...params,\n\n // Use reduced motion overrides (e.g. duration, easing) when reduced motion is enabled\n ...(isReducedMotion && reducedMotionParams),\n };\n\n try {\n // Firefox can throw an error when calling `element.animate()`.\n // See: https://github.com/microsoft/fluentui/issues/33902\n const animation = element.animate(animationKeyframes, animationParams);\n\n if (SUPPORTS_PERSIST) {\n // Chromium browsers can return null when calling `element.animate()`.\n // See: https://github.com/microsoft/fluentui/issues/33902\n animation?.persist();\n } else {\n const resultKeyframe = animationKeyframes[animationKeyframes.length - 1];\n Object.assign(element.style ?? {}, resultKeyframe);\n }\n\n return animation;\n } catch (e) {\n return null;\n }\n })\n .filter(animation => !!animation) as Animation[];\n\n return {\n set playbackRate(rate: number) {\n animations.forEach(animation => {\n animation.playbackRate = rate;\n });\n },\n setMotionEndCallbacks(onfinish: () => void, oncancel: () => void) {\n // Heads up!\n // This could use \"Animation:finished\", but it's causing a memory leak in Chromium.\n // See: https://issues.chromium.org/u/2/issues/383016426\n const promises = animations.map(animation => {\n return new Promise<void>((resolve, reject) => {\n animation.onfinish = () => resolve();\n animation.oncancel = () => reject();\n });\n });\n\n Promise.all(promises)\n .then(() => {\n onfinish();\n })\n .catch(() => {\n oncancel();\n });\n },\n isRunning() {\n return animations.some(animation => isAnimationRunning(animation));\n },\n\n cancel: () => {\n animations.forEach(animation => {\n animation.cancel();\n });\n },\n pause: () => {\n animations.forEach(animation => {\n animation.pause();\n });\n },\n play: () => {\n animations.forEach(animation => {\n animation.play();\n });\n },\n finish: () => {\n animations.forEach(animation => {\n animation.finish();\n });\n },\n reverse: () => {\n // Heads up!\n //\n // This is used for the interruptible motion. If the animation is running, we need to reverse it.\n //\n // TODO: what do with animations that have \"delay\"?\n // TODO: what do with animations that have different \"durations\"?\n\n animations.forEach(animation => {\n animation.reverse();\n });\n },\n };\n },\n [SUPPORTS_PERSIST],\n );\n}\n\n/**\n * In test environments, this hook is used to delay the execution of a callback until the next render. This is necessary\n * to ensure that the callback is not executed synchronously, which would cause the test to fail.\n *\n * @see https://github.com/microsoft/fluentui/issues/31701\n */\nfunction useAnimateAtomsInTestEnvironment() {\n const [count, setCount] = React.useState(0);\n const callbackRef = React.useRef<() => void>();\n\n const realAnimateAtoms = useAnimateAtomsInSupportedEnvironment();\n\n React.useEffect(() => {\n if (count > 0) {\n callbackRef.current?.();\n }\n }, [count]);\n\n return React.useCallback(\n (\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n ): AnimationHandle => {\n const ELEMENT_SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n\n // Heads up!\n // If the environment supports Web Animations API, we can use the native implementation.\n if (ELEMENT_SUPPORTS_WEB_ANIMATIONS) {\n return realAnimateAtoms(element, value, options);\n }\n\n return {\n setMotionEndCallbacks(onfinish: () => void) {\n callbackRef.current = onfinish;\n setCount(v => v + 1);\n },\n\n set playbackRate(rate: number) {\n /* no-op */\n },\n isRunning() {\n return false;\n },\n\n cancel() {\n /* no-op */\n },\n pause() {\n /* no-op */\n },\n play() {\n /* no-op */\n },\n finish() {\n /* no-op */\n },\n reverse() {\n /* no-op */\n },\n };\n },\n [realAnimateAtoms],\n );\n}\n\n/**\n * @internal\n */\nexport function useAnimateAtoms() {\n 'use no memo';\n\n if (process.env.NODE_ENV === 'test') {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInTestEnvironment();\n }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAnimateAtomsInSupportedEnvironment();\n}\n"],"names":["DEFAULT_ANIMATION_OPTIONS","useAnimateAtoms","fill","DEFAULT_REDUCED_MOTION_ATOM","duration","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","React","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animation","animate","resultKeyframe","length","Object","assign","style","e","filter","playbackRate","rate","forEach","setMotionEndCallbacks","onfinish","oncancel","promises","Promise","resolve","reject","all","then","catch","isRunning","some","isAnimationRunning","cancel","pause","play","finish","reverse","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","process","env","NODE_ENV"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/**
|
2
|
+
* Checks if the animation is running at the moment.
|
3
|
+
*/ "use strict";
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
5
|
+
value: true
|
6
|
+
});
|
7
|
+
Object.defineProperty(exports, "isAnimationRunning", {
|
8
|
+
enumerable: true,
|
9
|
+
get: function() {
|
10
|
+
return isAnimationRunning;
|
11
|
+
}
|
12
|
+
});
|
13
|
+
function isAnimationRunning(animation) {
|
14
|
+
if (animation.playState === 'running') {
|
15
|
+
var _animation_effect;
|
16
|
+
// Heads up!
|
17
|
+
//
|
18
|
+
// There is an edge case where the animation is running, but the overall progress is 0 or 1. In this case, we
|
19
|
+
// consider the animation to be not running. If it will be reversed it will flip from 1 to 0, and we will observe a
|
20
|
+
// glitch.
|
21
|
+
// "overallProgress" is not supported in all browsers, so we need to check if it exists.
|
22
|
+
// We will fall back to the currentTime and duration if "overallProgress" is not supported.
|
23
|
+
if (animation.overallProgress !== undefined) {
|
24
|
+
var _animation_overallProgress;
|
25
|
+
const overallProgress = (_animation_overallProgress = animation.overallProgress) !== null && _animation_overallProgress !== void 0 ? _animation_overallProgress : 0;
|
26
|
+
return overallProgress > 0 && overallProgress < 1;
|
27
|
+
}
|
28
|
+
var _animation_currentTime;
|
29
|
+
const currentTime = Number((_animation_currentTime = animation.currentTime) !== null && _animation_currentTime !== void 0 ? _animation_currentTime : 0);
|
30
|
+
var _animation_effect_getTiming_duration;
|
31
|
+
const totalTime = Number((_animation_effect_getTiming_duration = (_animation_effect = animation.effect) === null || _animation_effect === void 0 ? void 0 : _animation_effect.getTiming().duration) !== null && _animation_effect_getTiming_duration !== void 0 ? _animation_effect_getTiming_duration : 0);
|
32
|
+
return currentTime > 0 && currentTime < totalTime;
|
33
|
+
}
|
34
|
+
return false;
|
35
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../src/utils/isAnimationRunning.ts"],"sourcesContent":["/**\n * Checks if the animation is running at the moment.\n */\nexport function isAnimationRunning(animation: Animation & { readonly overallProgress?: number | null }) {\n if (animation.playState === 'running') {\n // Heads up!\n //\n // There is an edge case where the animation is running, but the overall progress is 0 or 1. In this case, we\n // consider the animation to be not running. If it will be reversed it will flip from 1 to 0, and we will observe a\n // glitch.\n\n // \"overallProgress\" is not supported in all browsers, so we need to check if it exists.\n // We will fall back to the currentTime and duration if \"overallProgress\" is not supported.\n if (animation.overallProgress !== undefined) {\n const overallProgress = animation.overallProgress ?? 0;\n\n return overallProgress > 0 && overallProgress < 1;\n }\n\n const currentTime = Number(animation.currentTime ?? 0);\n const totalTime = Number(animation.effect?.getTiming().duration ?? 0);\n\n return currentTime > 0 && currentTime < totalTime;\n }\n\n return false;\n}\n"],"names":["isAnimationRunning","animation","playState","overallProgress","undefined","currentTime","Number","totalTime","effect","getTiming","duration"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA;;CAEC;;;;+BACeA;;;eAAAA;;;AAAT,SAASA,mBAAmBC,SAAmE;IACpG,IAAIA,UAAUC,SAAS,KAAK,WAAW;YAgBZD;QAfzB,YAAY;QACZ,EAAE;QACF,6GAA6G;QAC7G,mHAAmH;QACnH,UAAU;QAEV,wFAAwF;QACxF,2FAA2F;QAC3F,IAAIA,UAAUE,eAAe,KAAKC,WAAW;gBACnBH;YAAxB,MAAME,kBAAkBF,CAAAA,6BAAAA,UAAUE,eAAe,cAAzBF,wCAAAA,6BAA6B;YAErD,OAAOE,kBAAkB,KAAKA,kBAAkB;QAClD;YAE2BF;QAA3B,MAAMI,cAAcC,OAAOL,CAAAA,yBAAAA,UAAUI,WAAW,cAArBJ,oCAAAA,yBAAyB;YAC3BA;QAAzB,MAAMM,YAAYD,OAAOL,CAAAA,wCAAAA,oBAAAA,UAAUO,MAAM,cAAhBP,wCAAAA,kBAAkBQ,SAAS,GAAGC,QAAQ,cAAtCT,kDAAAA,uCAA0C;QAEnE,OAAOI,cAAc,KAAKA,cAAcE;IAC1C;IAEA,OAAO;AACT"}
|