@fluentui/react-motion 9.15.0 → 9.16.1
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 -4
- package/README.md +60 -2
- package/dist/index.d.ts +24 -0
- package/lib/factories/createMotionComponent.js +41 -10
- package/lib/factories/createMotionComponent.js.map +1 -1
- package/lib/factories/createPresenceComponent.js +0 -1
- package/lib/factories/createPresenceComponent.js.map +1 -1
- package/lib/hooks/useAnimateAtoms.js +0 -1
- package/lib/hooks/useAnimateAtoms.js.map +1 -1
- package/lib/hooks/useMountedState.js +1 -0
- package/lib/hooks/useMountedState.js.map +1 -1
- package/lib-commonjs/factories/createMotionComponent.js +41 -10
- package/lib-commonjs/factories/createMotionComponent.js.map +1 -1
- package/lib-commonjs/factories/createPresenceComponent.js +0 -1
- package/lib-commonjs/factories/createPresenceComponent.js.map +1 -1
- package/lib-commonjs/hooks/useAnimateAtoms.js +0 -1
- package/lib-commonjs/hooks/useAnimateAtoms.js.map +1 -1
- package/lib-commonjs/hooks/useMountedState.js +1 -0
- package/lib-commonjs/hooks/useMountedState.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
# Change Log - @fluentui/react-motion
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Wed, 24 Jun 2026 11:03:37 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## [9.16.1](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.16.1)
|
|
8
|
+
|
|
9
|
+
Wed, 24 Jun 2026 11:03:37 GMT
|
|
10
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.16.0..@fluentui/react-motion_v9.16.1)
|
|
11
|
+
|
|
12
|
+
### Patches
|
|
13
|
+
|
|
14
|
+
- fix: remove redundant use no memo directives, and add justification to valid ones ([PR #36224](https://github.com/microsoft/fluentui/pull/36224) by martinhochel@microsoft.com)
|
|
15
|
+
- Bump @fluentui/react-utilities to v9.26.5 ([commit](https://github.com/microsoft/fluentui/commit/a4b871ca80c1f16f35ab4229def4fe02be7f30ea) by beachball)
|
|
16
|
+
|
|
17
|
+
## [9.16.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.16.0)
|
|
18
|
+
|
|
19
|
+
Tue, 26 May 2026 09:39:25 GMT
|
|
20
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.15.0..@fluentui/react-motion_v9.16.0)
|
|
21
|
+
|
|
22
|
+
### Minor changes
|
|
23
|
+
|
|
24
|
+
- feat(react-motion): add replayKey prop to replay motion without remounting ([PR #36108](https://github.com/microsoft/fluentui/pull/36108) by robertpenner@microsoft.com)
|
|
25
|
+
- Bump @fluentui/react-utilities to v9.26.4 ([PR #36246](https://github.com/microsoft/fluentui/pull/36246) by beachball)
|
|
26
|
+
|
|
7
27
|
## [9.15.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.15.0)
|
|
8
28
|
|
|
9
|
-
Thu, 23 Apr 2026
|
|
29
|
+
Thu, 23 Apr 2026 14:21:02 GMT
|
|
10
30
|
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.14.0..@fluentui/react-motion_v9.15.0)
|
|
11
31
|
|
|
12
32
|
### Minor changes
|
|
13
33
|
|
|
14
|
-
- fix(react-motion): apply MotionComponent type to presence definition ([PR #35952](https://github.com/microsoft/fluentui/pull/35952) by robertpenner@microsoft.com)
|
|
15
34
|
- feat: expose motion params as direct props on motion slot types ([PR #36011](https://github.com/microsoft/fluentui/pull/36011) by robertpenner@microsoft.com)
|
|
16
|
-
-
|
|
35
|
+
- fix(react-motion): apply MotionComponent type to presence definition ([PR #35952](https://github.com/microsoft/fluentui/pull/35952) by robertpenner@microsoft.com)
|
|
36
|
+
- Bump @fluentui/react-utilities to v9.26.3 ([PR #36035](https://github.com/microsoft/fluentui/pull/36035) by beachball)
|
|
17
37
|
|
|
18
38
|
## [9.14.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.14.0)
|
|
19
39
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# @fluentui/react-motion
|
|
2
2
|
|
|
3
|
-
**React
|
|
3
|
+
**React Motion components for [Fluent UI React](https://react.fluentui.dev/)**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A lightweight, performant animation library for React that brings Fluent UI experiences to life using the Web Animations API (WAAPI).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- ⚡ **Performance** — Animations run on the compositor thread for smooth 60fps motion
|
|
10
|
+
- 📦 **Lightweight** — ~3KB gzipped, leverages native browser capabilities
|
|
11
|
+
- 🎯 **Simple by default** — Common UI animations with minimal code
|
|
12
|
+
- 🔧 **Powerful on demand** — Full customization with keyframes, timing, and callbacks
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @fluentui/react-motion
|
|
18
|
+
# or
|
|
19
|
+
yarn add @fluentui/react-motion
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { createPresenceComponent, motionTokens } from '@fluentui/react-motion';
|
|
26
|
+
|
|
27
|
+
// Create a custom fade presence component
|
|
28
|
+
const Fade = createPresenceComponent({
|
|
29
|
+
enter: {
|
|
30
|
+
keyframes: [{ opacity: 0 }, { opacity: 1 }],
|
|
31
|
+
duration: motionTokens.durationNormal,
|
|
32
|
+
},
|
|
33
|
+
exit: {
|
|
34
|
+
keyframes: [{ opacity: 1 }, { opacity: 0 }],
|
|
35
|
+
duration: motionTokens.durationFast,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Use it in your app
|
|
40
|
+
function App() {
|
|
41
|
+
const [visible, setVisible] = useState(true);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Fade visible={visible}>
|
|
45
|
+
<div>Animated content</div>
|
|
46
|
+
</Fade>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Documentation
|
|
52
|
+
|
|
53
|
+
📚 **[Full documentation](https://react.fluentui.dev/?path=/docs/motion-introduction--docs)**
|
|
54
|
+
|
|
55
|
+
- [Introduction](https://react.fluentui.dev/?path=/docs/motion-introduction--docs) — Overview and key concepts
|
|
56
|
+
- [createPresenceComponent](https://react.fluentui.dev/?path=/docs/motion-apis-createpresencecomponent--docs) — Two-way enter/exit animations
|
|
57
|
+
- [createMotionComponent](https://react.fluentui.dev/?path=/docs/motion-apis-createmotioncomponent--docs) — One-way animations
|
|
58
|
+
- [Motion Tokens](https://react.fluentui.dev/?path=/docs/motion-tokens--docs) — Duration and easing values
|
|
59
|
+
- [Migration Guide](https://react.fluentui.dev/?path=/docs/motion-migration--docs) — Coming from Framer Motion, GSAP, etc.
|
|
60
|
+
|
|
61
|
+
## Pre-built Components
|
|
62
|
+
|
|
63
|
+
For ready-to-use motion components (Fade, Scale, Slide, Collapse, etc.), see **[@fluentui/react-motion-components-preview](https://www.npmjs.com/package/@fluentui/react-motion-components-preview)**.
|
package/dist/index.d.ts
CHANGED
|
@@ -123,6 +123,30 @@ export declare type MotionComponentProps = {
|
|
|
123
123
|
* so the callback is triggered with "null".
|
|
124
124
|
*/
|
|
125
125
|
onMotionStart?: (ev: null) => void;
|
|
126
|
+
/**
|
|
127
|
+
* When this value changes, the animation replays from the start on the same DOM element,
|
|
128
|
+
* cancelling any in-progress animation, without remounting the component or its children.
|
|
129
|
+
*
|
|
130
|
+
* **Why not just use a React `key`?** Changing a React `key` forces a full unmount and
|
|
131
|
+
* remount of the subtree: DOM nodes are destroyed and recreated, focus is lost, and any
|
|
132
|
+
* child state is reset. `replayKey` avoids all of that — only the animation effect reruns
|
|
133
|
+
* while the DOM and component state remain intact.
|
|
134
|
+
*
|
|
135
|
+
* Use this when you want to retrigger a motion in response to a state change (e.g. a user
|
|
136
|
+
* action or a data update) while preserving DOM continuity. It is the declarative equivalent
|
|
137
|
+
* of calling `imperativeRef.current.play()` but driven by a prop rather than a ref call.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```tsx
|
|
141
|
+
* // Replay a Fade.In each time the user clicks "Refresh"
|
|
142
|
+
* const [replayKey, setReplayKey] = React.useState(0);
|
|
143
|
+
* <Fade.In replayKey={replayKey}>
|
|
144
|
+
* <div>Content</div>
|
|
145
|
+
* </Fade.In>
|
|
146
|
+
* <button onClick={() => setReplayKey(k => k + 1)}>Refresh</button>
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
replayKey?: string | number;
|
|
126
150
|
};
|
|
127
151
|
|
|
128
152
|
export declare type MotionImperativeRef = {
|
|
@@ -17,11 +17,11 @@ import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
|
|
|
17
17
|
* @param value - A motion definition.
|
|
18
18
|
*/ export function createMotionComponent(value) {
|
|
19
19
|
const Atom = (props)=>{
|
|
20
|
-
|
|
21
|
-
const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, ..._rest } = props;
|
|
20
|
+
const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, replayKey, ..._rest } = props;
|
|
22
21
|
const params = _rest;
|
|
23
22
|
const [child, childRef] = useChildElement(children);
|
|
24
23
|
const handleRef = useMotionImperativeRef(imperativeRef);
|
|
24
|
+
const isInitialRender = React.useRef(true);
|
|
25
25
|
const skipMotions = useMotionBehaviourContext() === 'skip';
|
|
26
26
|
const optionsRef = React.useRef({
|
|
27
27
|
skipMotions,
|
|
@@ -38,6 +38,21 @@ import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
|
|
|
38
38
|
const onMotionCancel = useEventCallback(()=>{
|
|
39
39
|
onMotionCancelProp === null || onMotionCancelProp === void 0 ? void 0 : onMotionCancelProp(null);
|
|
40
40
|
});
|
|
41
|
+
// Stable callback (all deps are refs or useEventCallback) that activates a handle for a new playback cycle.
|
|
42
|
+
//
|
|
43
|
+
// TODO: consider moving the cancel+play+rewire sequence into a handle.replay() method on AnimationHandle,
|
|
44
|
+
// keeping pure animation sequencing on the handle and React callbacks here in the component.
|
|
45
|
+
const activateAnimationHandle = React.useCallback((handle)=>{
|
|
46
|
+
onMotionStart();
|
|
47
|
+
handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);
|
|
48
|
+
if (optionsRef.current.skipMotions) {
|
|
49
|
+
handle.finish();
|
|
50
|
+
}
|
|
51
|
+
}, [
|
|
52
|
+
onMotionStart,
|
|
53
|
+
onMotionFinish,
|
|
54
|
+
onMotionCancel
|
|
55
|
+
]);
|
|
41
56
|
useIsomorphicLayoutEffect(()=>{
|
|
42
57
|
// Heads up!
|
|
43
58
|
// We store the params in a ref to avoid re-rendering the component when the params change.
|
|
@@ -53,15 +68,11 @@ import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
|
|
|
53
68
|
element,
|
|
54
69
|
...optionsRef.current.params
|
|
55
70
|
}) : value;
|
|
56
|
-
onMotionStart();
|
|
57
71
|
const handle = animateAtoms(element, atoms, {
|
|
58
72
|
isReducedMotion: isReducedMotion()
|
|
59
73
|
});
|
|
60
74
|
handleRef.current = handle;
|
|
61
|
-
handle
|
|
62
|
-
if (optionsRef.current.skipMotions) {
|
|
63
|
-
handle.finish();
|
|
64
|
-
}
|
|
75
|
+
activateAnimationHandle(handle);
|
|
65
76
|
return ()=>{
|
|
66
77
|
handle.cancel();
|
|
67
78
|
};
|
|
@@ -71,10 +82,30 @@ import { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';
|
|
|
71
82
|
childRef,
|
|
72
83
|
handleRef,
|
|
73
84
|
isReducedMotion,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
activateAnimationHandle
|
|
86
|
+
]);
|
|
87
|
+
// Skips initial mount; on replayKey changes, reuses existing Animation objects via cancel+play
|
|
88
|
+
// rather than recreating them, preserving DOM continuity.
|
|
89
|
+
useIsomorphicLayoutEffect(()=>{
|
|
90
|
+
if (isInitialRender.current) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const handle = handleRef.current;
|
|
94
|
+
if (handle) {
|
|
95
|
+
handle.cancel();
|
|
96
|
+
handle.play();
|
|
97
|
+
activateAnimationHandle(handle);
|
|
98
|
+
}
|
|
99
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- replayKey is intentionally the only trigger; other deps are stable refs/callbacks
|
|
100
|
+
}, [
|
|
101
|
+
replayKey
|
|
77
102
|
]);
|
|
103
|
+
useIsomorphicLayoutEffect(()=>{
|
|
104
|
+
isInitialRender.current = false;
|
|
105
|
+
return ()=>{
|
|
106
|
+
isInitialRender.current = true;
|
|
107
|
+
};
|
|
108
|
+
}, []);
|
|
78
109
|
return child;
|
|
79
110
|
};
|
|
80
111
|
return Object.assign(Atom, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/factories/createMotionComponent.ts"],"sourcesContent":["'use client';\n\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useChildElement } from '../utils/useChildElement';\nimport type { AtomMotion, AtomMotionFn, MotionParam, MotionImperativeRef } from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type MotionComponentProps = {\n children: JSXElement;\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) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled.\n *\n * A motion definition can contain multiple animations and therefore multiple \"cancel\" events. The callback is\n * triggered once all animations have been cancelled 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) => 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) => void;\n};\n\nexport type MotionComponent<MotionParams extends Record<string, MotionParam> = {}> = React.FC<\n MotionComponentProps & MotionParams\n> & {\n [MOTION_DEFINITION]: AtomMotionFn<MotionParams>;\n};\n\n/**\n * Creates a component that will animate the children using the provided motion.\n *\n * @param value - A motion definition.\n */\nexport function createMotionComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: AtomMotion | AtomMotion[] | AtomMotionFn<MotionParams>,\n): MotionComponent<MotionParams> {\n const Atom: React.FC<MotionComponentProps & MotionParams> = props => {\n 'use no memo';\n\n const {\n children,\n imperativeRef,\n onMotionFinish: onMotionFinishProp,\n onMotionStart: onMotionStartProp,\n onMotionCancel: onMotionCancelProp,\n ..._rest\n } = props;\n const params = _rest as Exclude<typeof props, MotionComponentProps>;\n const [child, childRef] = useChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const skipMotions = useMotionBehaviourContext() === 'skip';\n const optionsRef = React.useRef<{ skipMotions: boolean; params: MotionParams }>({\n skipMotions,\n params,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isReducedMotion = useIsReducedMotion();\n\n const onMotionStart = useEventCallback(() => {\n onMotionStartProp?.(null);\n });\n\n const onMotionFinish = useEventCallback(() => {\n onMotionFinishProp?.(null);\n });\n\n const onMotionCancel = useEventCallback(() => {\n onMotionCancelProp?.(null);\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 = { skipMotions, params };\n });\n\n useIsomorphicLayoutEffect(() => {\n const element = childRef.current;\n\n if (element) {\n const atoms = typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : value;\n\n onMotionStart();\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n handleRef.current = handle;\n handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);\n\n if (optionsRef.current.skipMotions) {\n handle.finish();\n }\n\n return () => {\n handle.cancel();\n };\n }\n }, [animateAtoms, childRef, handleRef, isReducedMotion, onMotionFinish, onMotionStart, onMotionCancel]);\n\n return child;\n };\n\n return Object.assign(Atom, {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n });\n}\n"],"names":["useEventCallback","useIsomorphicLayoutEffect","React","useAnimateAtoms","useMotionImperativeRef","useIsReducedMotion","useChildElement","useMotionBehaviourContext","MOTION_DEFINITION","Symbol","createMotionComponent","value","Atom","props","children","imperativeRef","onMotionFinish","onMotionFinishProp","onMotionStart","onMotionStartProp","onMotionCancel","onMotionCancelProp","_rest","params","child","childRef","handleRef","skipMotions","optionsRef","useRef","animateAtoms","isReducedMotion","current","element","atoms","handle","setMotionEndCallbacks","finish","cancel","Object","assign"],"mappings":"AAAA;AAGA,SAASA,gBAAgB,EAAEC,yBAAyB,QAAQ,4BAA4B;AACxF,YAAYC,WAAW,QAAQ;AAE/B,SAASC,eAAe,QAAQ,2BAA2B;AAC3D,SAASC,sBAAsB,QAAQ,kCAAkC;AACzE,SAASC,kBAAkB,QAAQ,8BAA8B;AACjE,SAASC,eAAe,QAAQ,2BAA2B;AAE3D,SAASC,yBAAyB,QAAQ,qCAAqC;AAE/E;;;;CAIC,GACD,OAAO,MAAMC,oBAAoBC,OAAO,qBAAqB;AA2C7D;;;;CAIC,GACD,OAAO,SAASC,sBACdC,KAA6D;IAE7D,MAAMC,OAAsDC,CAAAA;QAC1D;QAEA,MAAM,EACJC,QAAQ,EACRC,aAAa,EACbC,gBAAgBC,kBAAkB,EAClCC,eAAeC,iBAAiB,EAChCC,gBAAgBC,kBAAkB,EAClC,GAAGC,OACJ,GAAGT;QACJ,MAAMU,SAASD;QACf,MAAM,CAACE,OAAOC,SAAS,GAAGnB,gBAAgBQ;QAE1C,MAAMY,YAAYtB,uBAAuBW;QACzC,MAAMY,cAAcpB,gCAAgC;QACpD,MAAMqB,aAAa1B,MAAM2B,MAAM,CAAiD;YAC9EF;YACAJ;QACF;QAEA,MAAMO,eAAe3B;QACrB,MAAM4B,kBAAkB1B;QAExB,MAAMa,gBAAgBlB,iBAAiB;YACrCmB,8BAAAA,wCAAAA,kBAAoB;QACtB;QAEA,MAAMH,iBAAiBhB,iBAAiB;YACtCiB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEA,MAAMG,iBAAiBpB,iBAAiB;YACtCqB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEApB,0BAA0B;YACxB,YAAY;YACZ,2FAA2F;YAC3F2B,WAAWI,OAAO,GAAG;gBAAEL;gBAAaJ;YAAO;QAC7C;QAEAtB,0BAA0B;YACxB,MAAMgC,UAAUR,SAASO,OAAO;YAEhC,IAAIC,SAAS;gBACX,MAAMC,QAAQ,OAAOvB,UAAU,aAAaA,MAAM;oBAAEsB;oBAAS,GAAGL,WAAWI,OAAO,CAACT,MAAM;gBAAC,KAAKZ;gBAE/FO;gBACA,MAAMiB,SAASL,aAAaG,SAASC,OAAO;oBAAEH,iBAAiBA;gBAAkB;gBACjFL,UAAUM,OAAO,GAAGG;gBACpBA,OAAOC,qBAAqB,CAACpB,gBAAgBI;gBAE7C,IAAIQ,WAAWI,OAAO,CAACL,WAAW,EAAE;oBAClCQ,OAAOE,MAAM;gBACf;gBAEA,OAAO;oBACLF,OAAOG,MAAM;gBACf;YACF;QACF,GAAG;YAACR;YAAcL;YAAUC;YAAWK;YAAiBf;YAAgBE;YAAeE;SAAe;QAEtG,OAAOI;IACT;IAEA,OAAOe,OAAOC,MAAM,CAAC5B,MAAM;QACzB,YAAY;QACZ,sDAAsD;QACtD,CAACJ,kBAAkB,EAAE,OAAOG,UAAU,aAAaA,QAAQ,IAAMA;IACnE;AACF"}
|
|
1
|
+
{"version":3,"sources":["../src/factories/createMotionComponent.ts"],"sourcesContent":["'use client';\n\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useChildElement } from '../utils/useChildElement';\nimport type { AtomMotion, AtomMotionFn, MotionParam, MotionImperativeRef, AnimationHandle } from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type MotionComponentProps = {\n children: JSXElement;\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) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled.\n *\n * A motion definition can contain multiple animations and therefore multiple \"cancel\" events. The callback is\n * triggered once all animations have been cancelled 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) => 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) => void;\n\n /**\n * When this value changes, the animation replays from the start on the same DOM element,\n * cancelling any in-progress animation, without remounting the component or its children.\n *\n * **Why not just use a React `key`?** Changing a React `key` forces a full unmount and\n * remount of the subtree: DOM nodes are destroyed and recreated, focus is lost, and any\n * child state is reset. `replayKey` avoids all of that — only the animation effect reruns\n * while the DOM and component state remain intact.\n *\n * Use this when you want to retrigger a motion in response to a state change (e.g. a user\n * action or a data update) while preserving DOM continuity. It is the declarative equivalent\n * of calling `imperativeRef.current.play()` but driven by a prop rather than a ref call.\n *\n * @example\n * ```tsx\n * // Replay a Fade.In each time the user clicks \"Refresh\"\n * const [replayKey, setReplayKey] = React.useState(0);\n * <Fade.In replayKey={replayKey}>\n * <div>Content</div>\n * </Fade.In>\n * <button onClick={() => setReplayKey(k => k + 1)}>Refresh</button>\n * ```\n */\n replayKey?: string | number;\n};\n\nexport type MotionComponent<MotionParams extends Record<string, MotionParam> = {}> = React.FC<\n MotionComponentProps & MotionParams\n> & {\n [MOTION_DEFINITION]: AtomMotionFn<MotionParams>;\n};\n\n/**\n * Creates a component that will animate the children using the provided motion.\n *\n * @param value - A motion definition.\n */\nexport function createMotionComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: AtomMotion | AtomMotion[] | AtomMotionFn<MotionParams>,\n): MotionComponent<MotionParams> {\n const Atom: React.FC<MotionComponentProps & MotionParams> = props => {\n const {\n children,\n imperativeRef,\n onMotionFinish: onMotionFinishProp,\n onMotionStart: onMotionStartProp,\n onMotionCancel: onMotionCancelProp,\n replayKey,\n ..._rest\n } = props;\n const params = _rest as Exclude<typeof props, MotionComponentProps>;\n const [child, childRef] = useChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const isInitialRender = React.useRef(true);\n const skipMotions = useMotionBehaviourContext() === 'skip';\n const optionsRef = React.useRef<{ skipMotions: boolean; params: MotionParams }>({\n skipMotions,\n params,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isReducedMotion = useIsReducedMotion();\n\n const onMotionStart = useEventCallback(() => {\n onMotionStartProp?.(null);\n });\n\n const onMotionFinish = useEventCallback(() => {\n onMotionFinishProp?.(null);\n });\n\n const onMotionCancel = useEventCallback(() => {\n onMotionCancelProp?.(null);\n });\n\n // Stable callback (all deps are refs or useEventCallback) that activates a handle for a new playback cycle.\n //\n // TODO: consider moving the cancel+play+rewire sequence into a handle.replay() method on AnimationHandle,\n // keeping pure animation sequencing on the handle and React callbacks here in the component.\n const activateAnimationHandle = React.useCallback(\n (handle: AnimationHandle) => {\n onMotionStart();\n handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);\n if (optionsRef.current.skipMotions) {\n handle.finish();\n }\n },\n [onMotionStart, onMotionFinish, onMotionCancel],\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 = { skipMotions, params };\n });\n\n useIsomorphicLayoutEffect(() => {\n const element = childRef.current;\n\n if (element) {\n const atoms = typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : value;\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n handleRef.current = handle;\n activateAnimationHandle(handle);\n\n return () => {\n handle.cancel();\n };\n }\n }, [animateAtoms, childRef, handleRef, isReducedMotion, activateAnimationHandle]);\n\n // Skips initial mount; on replayKey changes, reuses existing Animation objects via cancel+play\n // rather than recreating them, preserving DOM continuity.\n useIsomorphicLayoutEffect(() => {\n if (isInitialRender.current) {\n return;\n }\n\n const handle = handleRef.current;\n if (handle) {\n handle.cancel();\n handle.play();\n activateAnimationHandle(handle);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- replayKey is intentionally the only trigger; other deps are stable refs/callbacks\n }, [replayKey]);\n\n useIsomorphicLayoutEffect(() => {\n isInitialRender.current = false;\n\n return () => {\n isInitialRender.current = true;\n };\n }, []);\n\n return child;\n };\n\n return Object.assign(Atom, {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n });\n}\n"],"names":["useEventCallback","useIsomorphicLayoutEffect","React","useAnimateAtoms","useMotionImperativeRef","useIsReducedMotion","useChildElement","useMotionBehaviourContext","MOTION_DEFINITION","Symbol","createMotionComponent","value","Atom","props","children","imperativeRef","onMotionFinish","onMotionFinishProp","onMotionStart","onMotionStartProp","onMotionCancel","onMotionCancelProp","replayKey","_rest","params","child","childRef","handleRef","isInitialRender","useRef","skipMotions","optionsRef","animateAtoms","isReducedMotion","activateAnimationHandle","useCallback","handle","setMotionEndCallbacks","current","finish","element","atoms","cancel","play","Object","assign"],"mappings":"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"}
|
|
@@ -17,7 +17,6 @@ import { createMotionComponent } from './createMotionComponent';
|
|
|
17
17
|
const INTERRUPTABLE_MOTION_SYMBOL = Symbol.for('interruptablePresence');
|
|
18
18
|
export function createPresenceComponent(value) {
|
|
19
19
|
return Object.assign((props)=>{
|
|
20
|
-
'use no memo';
|
|
21
20
|
const itemContext = React.useContext(PresenceGroupChildContext);
|
|
22
21
|
const merged = {
|
|
23
22
|
...itemContext,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["'use client';\n\nimport { useEventCallback, useFirstMount, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport type { JSXElement } 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 { useChildElement } from '../utils/useChildElement';\nimport type {\n MotionParam,\n PresenceMotion,\n MotionImperativeRef,\n PresenceMotionFn,\n PresenceDirection,\n AnimationHandle,\n} from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\nimport type { MotionComponent } from './createMotionComponent';\nimport { createMotionComponent } from './createMotionComponent';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const PRESENCE_MOTION_DEFINITION = Symbol('PRESENCE_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: JSXElement;\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> = {}> = React.FC<\n PresenceComponentProps & MotionParams\n> & {\n (props: PresenceComponentProps & MotionParams): JSXElement | null;\n [PRESENCE_MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n In: MotionComponent<MotionParams>;\n Out: MotionComponent<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, childRef] = useChildElement(children, mounted);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\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 = childRef.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 [\n animateAtoms,\n childRef,\n handleRef,\n isReducedMotion,\n handleMotionFinish,\n handleMotionStart,\n handleMotionCancel,\n visible,\n ],\n );\n\n React.useEffect(() => {\n // Heads up!\n //\n // Dispose the handle when unmounting the component to clean up retained references. Doing it in a separate\n // effect to ensure that the component is unmounted.\n\n if (unmountOnExit && !mounted) {\n handleRef.current?.dispose();\n }\n }, [handleRef, unmountOnExit, mounted]);\n\n if (mounted) {\n return child;\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [PRESENCE_MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n {\n // Wrap `enter` in its own motion component as a static method, e.g. <Fade.In>\n In: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `enter`.\n // Otherwise, pass the `enter` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).enter : value.enter,\n ),\n\n // Wrap `exit` in its own motion component as a static method, e.g. <Fade.Out>\n Out: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `exit`.\n // Otherwise, pass the `exit` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).exit : value.exit,\n ),\n },\n );\n}\n"],"names":["useEventCallback","useFirstMount","useIsomorphicLayoutEffect","React","PresenceGroupChildContext","useAnimateAtoms","useMotionImperativeRef","useMountedState","useIsReducedMotion","useChildElement","useMotionBehaviourContext","createMotionComponent","PRESENCE_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","childRef","handleRef","optionsRef","useRef","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","useEffect","dispose","In","args","Out"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["'use client';\n\nimport { useEventCallback, useFirstMount, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport type { JSXElement } 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 { useChildElement } from '../utils/useChildElement';\nimport type {\n MotionParam,\n PresenceMotion,\n MotionImperativeRef,\n PresenceMotionFn,\n PresenceDirection,\n AnimationHandle,\n} from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\nimport type { MotionComponent } from './createMotionComponent';\nimport { createMotionComponent } from './createMotionComponent';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const PRESENCE_MOTION_DEFINITION = Symbol('PRESENCE_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: JSXElement;\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> = {}> = React.FC<\n PresenceComponentProps & MotionParams\n> & {\n (props: PresenceComponentProps & MotionParams): JSXElement | null;\n [PRESENCE_MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n In: MotionComponent<MotionParams>;\n Out: MotionComponent<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 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, childRef] = useChildElement(children, mounted);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\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 = childRef.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 [\n animateAtoms,\n childRef,\n handleRef,\n isReducedMotion,\n handleMotionFinish,\n handleMotionStart,\n handleMotionCancel,\n visible,\n ],\n );\n\n React.useEffect(() => {\n // Heads up!\n //\n // Dispose the handle when unmounting the component to clean up retained references. Doing it in a separate\n // effect to ensure that the component is unmounted.\n\n if (unmountOnExit && !mounted) {\n handleRef.current?.dispose();\n }\n }, [handleRef, unmountOnExit, mounted]);\n\n if (mounted) {\n return child;\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [PRESENCE_MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n {\n // Wrap `enter` in its own motion component as a static method, e.g. <Fade.In>\n In: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `enter`.\n // Otherwise, pass the `enter` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).enter : value.enter,\n ),\n\n // Wrap `exit` in its own motion component as a static method, e.g. <Fade.Out>\n Out: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `exit`.\n // Otherwise, pass the `exit` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).exit : value.exit,\n ),\n },\n );\n}\n"],"names":["useEventCallback","useFirstMount","useIsomorphicLayoutEffect","React","PresenceGroupChildContext","useAnimateAtoms","useMotionImperativeRef","useMountedState","useIsReducedMotion","useChildElement","useMotionBehaviourContext","createMotionComponent","PRESENCE_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","childRef","handleRef","optionsRef","useRef","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","useEffect","dispose","In","args","Out"],"mappings":"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"}
|
|
@@ -174,7 +174,6 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
|
174
174
|
/**
|
|
175
175
|
* @internal
|
|
176
176
|
*/ export function useAnimateAtoms() {
|
|
177
|
-
'use no memo';
|
|
178
177
|
if (process.env.NODE_ENV === 'test') {
|
|
179
178
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
180
179
|
return useAnimateAtomsInTestEnvironment();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["'use client';\n\nimport * 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\n/**\n * Creates an animation handle that controls multiple animations.\n * Is used to avoid leaking \"element\" references from the hook.\n *\n * @param animations\n */\nfunction createHandle(animations: Animation[]): AnimationHandle {\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 dispose: () => {\n animations.length = 0;\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\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 createHandle(animations);\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>(undefined);\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 dispose() {\n /* no-op */\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 element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: { isReducedMotion: boolean },\n) => AnimationHandle {\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","createHandle","animations","playbackRate","rate","forEach","animation","setMotionEndCallbacks","onfinish","oncancel","promises","map","Promise","resolve","reject","all","then","catch","isRunning","some","dispose","length","cancel","pause","play","finish","reverse","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animate","resultKeyframe","Object","assign","style","e","filter","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","undefined","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","useAnimateAtoms","process","env","NODE_ENV"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["'use client';\n\nimport * 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\n/**\n * Creates an animation handle that controls multiple animations.\n * Is used to avoid leaking \"element\" references from the hook.\n *\n * @param animations\n */\nfunction createHandle(animations: Animation[]): AnimationHandle {\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 dispose: () => {\n animations.length = 0;\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\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 createHandle(animations);\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>(undefined);\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 dispose() {\n /* no-op */\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 element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: { isReducedMotion: boolean },\n) => AnimationHandle {\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","createHandle","animations","playbackRate","rate","forEach","animation","setMotionEndCallbacks","onfinish","oncancel","promises","map","Promise","resolve","reject","all","then","catch","isRunning","some","dispose","length","cancel","pause","play","finish","reverse","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animate","resultKeyframe","Object","assign","style","e","filter","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","undefined","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","useAnimateAtoms","process","env","NODE_ENV"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useMountedState.ts"],"sourcesContent":["'use client';\n\nimport { useForceUpdate } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\n/**\n * This hook manages the mounted state of a component, based on the \"visible\" and \"unmountOnExit\" props.\n * It simulates the behavior of getDerivedStateFromProps(), which is not available in functional components.\n */\nexport function useMountedState(\n visible: boolean = false,\n unmountOnExit: boolean = false,\n): [boolean, (value: boolean) => void] {\n const mountedRef = React.useRef<boolean>(unmountOnExit ? visible : true);\n const forceUpdate = useForceUpdate();\n\n const setMounted = React.useCallback(\n (newValue: boolean) => {\n if (mountedRef.current !== newValue) {\n mountedRef.current = newValue;\n forceUpdate();\n }\n },\n [forceUpdate],\n );\n\n React.useEffect(() => {\n if (visible) {\n mountedRef.current = visible;\n }\n });\n\n return [visible || mountedRef.current, setMounted];\n}\n"],"names":["useForceUpdate","React","useMountedState","visible","unmountOnExit","mountedRef","useRef","forceUpdate","setMounted","useCallback","newValue","current","useEffect"],"mappings":"AAAA;AAEA,SAASA,cAAc,QAAQ,4BAA4B;AAC3D,YAAYC,WAAW,QAAQ;AAE/B;;;CAGC,GACD,OAAO,SAASC,gBACdC,UAAmB,KAAK,EACxBC,gBAAyB,KAAK;IAE9B,MAAMC,aAAaJ,MAAMK,MAAM,CAAUF,gBAAgBD,UAAU;IACnE,MAAMI,cAAcP;IAEpB,MAAMQ,aAAaP,MAAMQ,WAAW,CAClC,CAACC;QACC,IAAIL,WAAWM,OAAO,KAAKD,UAAU;YACnCL,WAAWM,OAAO,GAAGD;YACrBH;QACF;IACF,GACA;QAACA;KAAY;IAGfN,MAAMW,SAAS,CAAC;QACd,IAAIT,SAAS;YACXE,WAAWM,OAAO,GAAGR;QACvB;IACF;IAEA,OAAO;QAACA,WAAWE,WAAWM,OAAO;QAAEH;KAAW;AACpD"}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useMountedState.ts"],"sourcesContent":["'use client';\n\nimport { useForceUpdate } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\n/**\n * This hook manages the mounted state of a component, based on the \"visible\" and \"unmountOnExit\" props.\n * It simulates the behavior of getDerivedStateFromProps(), which is not available in functional components.\n */\nexport function useMountedState(\n visible: boolean = false,\n unmountOnExit: boolean = false,\n): [boolean, (value: boolean) => void] {\n const mountedRef = React.useRef<boolean>(unmountOnExit ? visible : true);\n const forceUpdate = useForceUpdate();\n\n const setMounted = React.useCallback(\n (newValue: boolean) => {\n if (mountedRef.current !== newValue) {\n mountedRef.current = newValue;\n forceUpdate();\n }\n },\n [forceUpdate],\n );\n\n React.useEffect(() => {\n if (visible) {\n mountedRef.current = visible;\n }\n });\n\n // eslint-disable-next-line react-hooks/refs\n return [visible || mountedRef.current, setMounted];\n}\n"],"names":["useForceUpdate","React","useMountedState","visible","unmountOnExit","mountedRef","useRef","forceUpdate","setMounted","useCallback","newValue","current","useEffect"],"mappings":"AAAA;AAEA,SAASA,cAAc,QAAQ,4BAA4B;AAC3D,YAAYC,WAAW,QAAQ;AAE/B;;;CAGC,GACD,OAAO,SAASC,gBACdC,UAAmB,KAAK,EACxBC,gBAAyB,KAAK;IAE9B,MAAMC,aAAaJ,MAAMK,MAAM,CAAUF,gBAAgBD,UAAU;IACnE,MAAMI,cAAcP;IAEpB,MAAMQ,aAAaP,MAAMQ,WAAW,CAClC,CAACC;QACC,IAAIL,WAAWM,OAAO,KAAKD,UAAU;YACnCL,WAAWM,OAAO,GAAGD;YACrBH;QACF;IACF,GACA;QAACA;KAAY;IAGfN,MAAMW,SAAS,CAAC;QACd,IAAIT,SAAS;YACXE,WAAWM,OAAO,GAAGR;QACvB;IACF;IAEA,4CAA4C;IAC5C,OAAO;QAACA,WAAWE,WAAWM,OAAO;QAAEH;KAAW;AACpD"}
|
|
@@ -28,11 +28,11 @@ const _MotionBehaviourContext = require("../contexts/MotionBehaviourContext");
|
|
|
28
28
|
const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');
|
|
29
29
|
function createMotionComponent(value) {
|
|
30
30
|
const Atom = (props)=>{
|
|
31
|
-
|
|
32
|
-
const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, ..._rest } = props;
|
|
31
|
+
const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, replayKey, ..._rest } = props;
|
|
33
32
|
const params = _rest;
|
|
34
33
|
const [child, childRef] = (0, _useChildElement.useChildElement)(children);
|
|
35
34
|
const handleRef = (0, _useMotionImperativeRef.useMotionImperativeRef)(imperativeRef);
|
|
35
|
+
const isInitialRender = _react.useRef(true);
|
|
36
36
|
const skipMotions = (0, _MotionBehaviourContext.useMotionBehaviourContext)() === 'skip';
|
|
37
37
|
const optionsRef = _react.useRef({
|
|
38
38
|
skipMotions,
|
|
@@ -49,6 +49,21 @@ function createMotionComponent(value) {
|
|
|
49
49
|
const onMotionCancel = (0, _reactutilities.useEventCallback)(()=>{
|
|
50
50
|
onMotionCancelProp === null || onMotionCancelProp === void 0 ? void 0 : onMotionCancelProp(null);
|
|
51
51
|
});
|
|
52
|
+
// Stable callback (all deps are refs or useEventCallback) that activates a handle for a new playback cycle.
|
|
53
|
+
//
|
|
54
|
+
// TODO: consider moving the cancel+play+rewire sequence into a handle.replay() method on AnimationHandle,
|
|
55
|
+
// keeping pure animation sequencing on the handle and React callbacks here in the component.
|
|
56
|
+
const activateAnimationHandle = _react.useCallback((handle)=>{
|
|
57
|
+
onMotionStart();
|
|
58
|
+
handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);
|
|
59
|
+
if (optionsRef.current.skipMotions) {
|
|
60
|
+
handle.finish();
|
|
61
|
+
}
|
|
62
|
+
}, [
|
|
63
|
+
onMotionStart,
|
|
64
|
+
onMotionFinish,
|
|
65
|
+
onMotionCancel
|
|
66
|
+
]);
|
|
52
67
|
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
|
|
53
68
|
// Heads up!
|
|
54
69
|
// We store the params in a ref to avoid re-rendering the component when the params change.
|
|
@@ -64,15 +79,11 @@ function createMotionComponent(value) {
|
|
|
64
79
|
element,
|
|
65
80
|
...optionsRef.current.params
|
|
66
81
|
}) : value;
|
|
67
|
-
onMotionStart();
|
|
68
82
|
const handle = animateAtoms(element, atoms, {
|
|
69
83
|
isReducedMotion: isReducedMotion()
|
|
70
84
|
});
|
|
71
85
|
handleRef.current = handle;
|
|
72
|
-
handle
|
|
73
|
-
if (optionsRef.current.skipMotions) {
|
|
74
|
-
handle.finish();
|
|
75
|
-
}
|
|
86
|
+
activateAnimationHandle(handle);
|
|
76
87
|
return ()=>{
|
|
77
88
|
handle.cancel();
|
|
78
89
|
};
|
|
@@ -82,10 +93,30 @@ function createMotionComponent(value) {
|
|
|
82
93
|
childRef,
|
|
83
94
|
handleRef,
|
|
84
95
|
isReducedMotion,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
activateAnimationHandle
|
|
97
|
+
]);
|
|
98
|
+
// Skips initial mount; on replayKey changes, reuses existing Animation objects via cancel+play
|
|
99
|
+
// rather than recreating them, preserving DOM continuity.
|
|
100
|
+
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
|
|
101
|
+
if (isInitialRender.current) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const handle = handleRef.current;
|
|
105
|
+
if (handle) {
|
|
106
|
+
handle.cancel();
|
|
107
|
+
handle.play();
|
|
108
|
+
activateAnimationHandle(handle);
|
|
109
|
+
}
|
|
110
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- replayKey is intentionally the only trigger; other deps are stable refs/callbacks
|
|
111
|
+
}, [
|
|
112
|
+
replayKey
|
|
88
113
|
]);
|
|
114
|
+
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
|
|
115
|
+
isInitialRender.current = false;
|
|
116
|
+
return ()=>{
|
|
117
|
+
isInitialRender.current = true;
|
|
118
|
+
};
|
|
119
|
+
}, []);
|
|
89
120
|
return child;
|
|
90
121
|
};
|
|
91
122
|
return Object.assign(Atom, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/factories/createMotionComponent.ts"],"sourcesContent":["'use client';\n\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useChildElement } from '../utils/useChildElement';\nimport type { AtomMotion, AtomMotionFn, MotionParam, MotionImperativeRef } from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type MotionComponentProps = {\n children: JSXElement;\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) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled.\n *\n * A motion definition can contain multiple animations and therefore multiple \"cancel\" events. The callback is\n * triggered once all animations have been cancelled 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) => 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) => void;\n};\n\nexport type MotionComponent<MotionParams extends Record<string, MotionParam> = {}> = React.FC<\n MotionComponentProps & MotionParams\n> & {\n [MOTION_DEFINITION]: AtomMotionFn<MotionParams>;\n};\n\n/**\n * Creates a component that will animate the children using the provided motion.\n *\n * @param value - A motion definition.\n */\nexport function createMotionComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: AtomMotion | AtomMotion[] | AtomMotionFn<MotionParams>,\n): MotionComponent<MotionParams> {\n const Atom: React.FC<MotionComponentProps & MotionParams> = props => {\n 'use no memo';\n\n const {\n children,\n imperativeRef,\n onMotionFinish: onMotionFinishProp,\n onMotionStart: onMotionStartProp,\n onMotionCancel: onMotionCancelProp,\n ..._rest\n } = props;\n const params = _rest as Exclude<typeof props, MotionComponentProps>;\n const [child, childRef] = useChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const skipMotions = useMotionBehaviourContext() === 'skip';\n const optionsRef = React.useRef<{ skipMotions: boolean; params: MotionParams }>({\n skipMotions,\n params,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isReducedMotion = useIsReducedMotion();\n\n const onMotionStart = useEventCallback(() => {\n onMotionStartProp?.(null);\n });\n\n const onMotionFinish = useEventCallback(() => {\n onMotionFinishProp?.(null);\n });\n\n const onMotionCancel = useEventCallback(() => {\n onMotionCancelProp?.(null);\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 = { skipMotions, params };\n });\n\n useIsomorphicLayoutEffect(() => {\n const element = childRef.current;\n\n if (element) {\n const atoms = typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : value;\n\n onMotionStart();\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n handleRef.current = handle;\n handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);\n\n if (optionsRef.current.skipMotions) {\n handle.finish();\n }\n\n return () => {\n handle.cancel();\n };\n }\n }, [animateAtoms, childRef, handleRef, isReducedMotion, onMotionFinish, onMotionStart, onMotionCancel]);\n\n return child;\n };\n\n return Object.assign(Atom, {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n });\n}\n"],"names":["MOTION_DEFINITION","createMotionComponent","Symbol","value","Atom","props","children","imperativeRef","onMotionFinish","onMotionFinishProp","onMotionStart","onMotionStartProp","onMotionCancel","onMotionCancelProp","_rest","params","child","childRef","useChildElement","handleRef","useMotionImperativeRef","skipMotions","useMotionBehaviourContext","optionsRef","React","useRef","animateAtoms","useAnimateAtoms","isReducedMotion","useIsReducedMotion","useEventCallback","useIsomorphicLayoutEffect","current","element","atoms","handle","setMotionEndCallbacks","finish","cancel","Object","assign"],"mappings":"AAAA;;;;;;;;;;;;IAkBaA,iBAAiB;eAAjBA;;IAgDGC,qBAAqB;eAArBA;;;;gCA/D4C;iEACrC;iCAES;wCACO;oCACJ;iCACH;wCAEU;AAOnC,MAAMD,oBAAoBE,OAAO;AAgDjC,SAASD,sBACdE,KAA6D;IAE7D,MAAMC,OAAsDC,CAAAA;QAC1D;QAEA,MAAM,EACJC,QAAQ,EACRC,aAAa,EACbC,gBAAgBC,kBAAkB,EAClCC,eAAeC,iBAAiB,EAChCC,gBAAgBC,kBAAkB,EAClC,GAAGC,OACJ,GAAGT;QACJ,MAAMU,SAASD;QACf,MAAM,CAACE,OAAOC,SAAS,GAAGC,IAAAA,gCAAe,EAACZ;QAE1C,MAAMa,YAAYC,IAAAA,8CAAsB,EAACb;QACzC,MAAMc,cAAcC,IAAAA,iDAAyB,QAAO;QACpD,MAAMC,aAAaC,OAAMC,MAAM,CAAiD;YAC9EJ;YACAN;QACF;QAEA,MAAMW,eAAeC,IAAAA,gCAAe;QACpC,MAAMC,kBAAkBC,IAAAA,sCAAkB;QAE1C,MAAMnB,gBAAgBoB,IAAAA,gCAAgB,EAAC;YACrCnB,8BAAAA,wCAAAA,kBAAoB;QACtB;QAEA,MAAMH,iBAAiBsB,IAAAA,gCAAgB,EAAC;YACtCrB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEA,MAAMG,iBAAiBkB,IAAAA,gCAAgB,EAAC;YACtCjB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEAkB,IAAAA,yCAAyB,EAAC;YACxB,YAAY;YACZ,2FAA2F;YAC3FR,WAAWS,OAAO,GAAG;gBAAEX;gBAAaN;YAAO;QAC7C;QAEAgB,IAAAA,yCAAyB,EAAC;YACxB,MAAME,UAAUhB,SAASe,OAAO;YAEhC,IAAIC,SAAS;gBACX,MAAMC,QAAQ,OAAO/B,UAAU,aAAaA,MAAM;oBAAE8B;oBAAS,GAAGV,WAAWS,OAAO,CAACjB,MAAM;gBAAC,KAAKZ;gBAE/FO;gBACA,MAAMyB,SAAST,aAAaO,SAASC,OAAO;oBAAEN,iBAAiBA;gBAAkB;gBACjFT,UAAUa,OAAO,GAAGG;gBACpBA,OAAOC,qBAAqB,CAAC5B,gBAAgBI;gBAE7C,IAAIW,WAAWS,OAAO,CAACX,WAAW,EAAE;oBAClCc,OAAOE,MAAM;gBACf;gBAEA,OAAO;oBACLF,OAAOG,MAAM;gBACf;YACF;QACF,GAAG;YAACZ;YAAcT;YAAUE;YAAWS;YAAiBpB;YAAgBE;YAAeE;SAAe;QAEtG,OAAOI;IACT;IAEA,OAAOuB,OAAOC,MAAM,CAACpC,MAAM;QACzB,YAAY;QACZ,sDAAsD;QACtD,CAACJ,kBAAkB,EAAE,OAAOG,UAAU,aAAaA,QAAQ,IAAMA;IACnE;AACF"}
|
|
1
|
+
{"version":3,"sources":["../src/factories/createMotionComponent.ts"],"sourcesContent":["'use client';\n\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { useAnimateAtoms } from '../hooks/useAnimateAtoms';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useChildElement } from '../utils/useChildElement';\nimport type { AtomMotion, AtomMotionFn, MotionParam, MotionImperativeRef, AnimationHandle } from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const MOTION_DEFINITION = Symbol('MOTION_DEFINITION');\n\nexport type MotionComponentProps = {\n children: JSXElement;\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) => void;\n\n /**\n * Callback that is called when the whole motion is cancelled.\n *\n * A motion definition can contain multiple animations and therefore multiple \"cancel\" events. The callback is\n * triggered once all animations have been cancelled 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) => 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) => void;\n\n /**\n * When this value changes, the animation replays from the start on the same DOM element,\n * cancelling any in-progress animation, without remounting the component or its children.\n *\n * **Why not just use a React `key`?** Changing a React `key` forces a full unmount and\n * remount of the subtree: DOM nodes are destroyed and recreated, focus is lost, and any\n * child state is reset. `replayKey` avoids all of that — only the animation effect reruns\n * while the DOM and component state remain intact.\n *\n * Use this when you want to retrigger a motion in response to a state change (e.g. a user\n * action or a data update) while preserving DOM continuity. It is the declarative equivalent\n * of calling `imperativeRef.current.play()` but driven by a prop rather than a ref call.\n *\n * @example\n * ```tsx\n * // Replay a Fade.In each time the user clicks \"Refresh\"\n * const [replayKey, setReplayKey] = React.useState(0);\n * <Fade.In replayKey={replayKey}>\n * <div>Content</div>\n * </Fade.In>\n * <button onClick={() => setReplayKey(k => k + 1)}>Refresh</button>\n * ```\n */\n replayKey?: string | number;\n};\n\nexport type MotionComponent<MotionParams extends Record<string, MotionParam> = {}> = React.FC<\n MotionComponentProps & MotionParams\n> & {\n [MOTION_DEFINITION]: AtomMotionFn<MotionParams>;\n};\n\n/**\n * Creates a component that will animate the children using the provided motion.\n *\n * @param value - A motion definition.\n */\nexport function createMotionComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: AtomMotion | AtomMotion[] | AtomMotionFn<MotionParams>,\n): MotionComponent<MotionParams> {\n const Atom: React.FC<MotionComponentProps & MotionParams> = props => {\n const {\n children,\n imperativeRef,\n onMotionFinish: onMotionFinishProp,\n onMotionStart: onMotionStartProp,\n onMotionCancel: onMotionCancelProp,\n replayKey,\n ..._rest\n } = props;\n const params = _rest as Exclude<typeof props, MotionComponentProps>;\n const [child, childRef] = useChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const isInitialRender = React.useRef(true);\n const skipMotions = useMotionBehaviourContext() === 'skip';\n const optionsRef = React.useRef<{ skipMotions: boolean; params: MotionParams }>({\n skipMotions,\n params,\n });\n\n const animateAtoms = useAnimateAtoms();\n const isReducedMotion = useIsReducedMotion();\n\n const onMotionStart = useEventCallback(() => {\n onMotionStartProp?.(null);\n });\n\n const onMotionFinish = useEventCallback(() => {\n onMotionFinishProp?.(null);\n });\n\n const onMotionCancel = useEventCallback(() => {\n onMotionCancelProp?.(null);\n });\n\n // Stable callback (all deps are refs or useEventCallback) that activates a handle for a new playback cycle.\n //\n // TODO: consider moving the cancel+play+rewire sequence into a handle.replay() method on AnimationHandle,\n // keeping pure animation sequencing on the handle and React callbacks here in the component.\n const activateAnimationHandle = React.useCallback(\n (handle: AnimationHandle) => {\n onMotionStart();\n handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);\n if (optionsRef.current.skipMotions) {\n handle.finish();\n }\n },\n [onMotionStart, onMotionFinish, onMotionCancel],\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 = { skipMotions, params };\n });\n\n useIsomorphicLayoutEffect(() => {\n const element = childRef.current;\n\n if (element) {\n const atoms = typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : value;\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n handleRef.current = handle;\n activateAnimationHandle(handle);\n\n return () => {\n handle.cancel();\n };\n }\n }, [animateAtoms, childRef, handleRef, isReducedMotion, activateAnimationHandle]);\n\n // Skips initial mount; on replayKey changes, reuses existing Animation objects via cancel+play\n // rather than recreating them, preserving DOM continuity.\n useIsomorphicLayoutEffect(() => {\n if (isInitialRender.current) {\n return;\n }\n\n const handle = handleRef.current;\n if (handle) {\n handle.cancel();\n handle.play();\n activateAnimationHandle(handle);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- replayKey is intentionally the only trigger; other deps are stable refs/callbacks\n }, [replayKey]);\n\n useIsomorphicLayoutEffect(() => {\n isInitialRender.current = false;\n\n return () => {\n isInitialRender.current = true;\n };\n }, []);\n\n return child;\n };\n\n return Object.assign(Atom, {\n // Heads up!\n // Always normalize it to a function to simplify types\n [MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n });\n}\n"],"names":["MOTION_DEFINITION","createMotionComponent","Symbol","value","Atom","props","children","imperativeRef","onMotionFinish","onMotionFinishProp","onMotionStart","onMotionStartProp","onMotionCancel","onMotionCancelProp","replayKey","_rest","params","child","childRef","useChildElement","handleRef","useMotionImperativeRef","isInitialRender","React","useRef","skipMotions","useMotionBehaviourContext","optionsRef","animateAtoms","useAnimateAtoms","isReducedMotion","useIsReducedMotion","useEventCallback","activateAnimationHandle","useCallback","handle","setMotionEndCallbacks","current","finish","useIsomorphicLayoutEffect","element","atoms","cancel","play","Object","assign"],"mappings":"AAAA;;;;;;;;;;;;IAkBaA,iBAAiB;eAAjBA;;IAyEGC,qBAAqB;eAArBA;;;;gCAxF4C;iEACrC;iCAES;wCACO;oCACJ;iCACH;wCAEU;AAOnC,MAAMD,oBAAoBE,OAAO;AAyEjC,SAASD,sBACdE,KAA6D;IAE7D,MAAMC,OAAsDC,CAAAA;QAC1D,MAAM,EACJC,QAAQ,EACRC,aAAa,EACbC,gBAAgBC,kBAAkB,EAClCC,eAAeC,iBAAiB,EAChCC,gBAAgBC,kBAAkB,EAClCC,SAAS,EACT,GAAGC,OACJ,GAAGV;QACJ,MAAMW,SAASD;QACf,MAAM,CAACE,OAAOC,SAAS,GAAGC,IAAAA,gCAAe,EAACb;QAE1C,MAAMc,YAAYC,IAAAA,8CAAsB,EAACd;QACzC,MAAMe,kBAAkBC,OAAMC,MAAM,CAAC;QACrC,MAAMC,cAAcC,IAAAA,iDAAyB,QAAO;QACpD,MAAMC,aAAaJ,OAAMC,MAAM,CAAiD;YAC9EC;YACAT;QACF;QAEA,MAAMY,eAAeC,IAAAA,gCAAe;QACpC,MAAMC,kBAAkBC,IAAAA,sCAAkB;QAE1C,MAAMrB,gBAAgBsB,IAAAA,gCAAgB,EAAC;YACrCrB,8BAAAA,wCAAAA,kBAAoB;QACtB;QAEA,MAAMH,iBAAiBwB,IAAAA,gCAAgB,EAAC;YACtCvB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEA,MAAMG,iBAAiBoB,IAAAA,gCAAgB,EAAC;YACtCnB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEA,4GAA4G;QAC5G,EAAE;QACF,0GAA0G;QAC1G,6FAA6F;QAC7F,MAAMoB,0BAA0BV,OAAMW,WAAW,CAC/C,CAACC;YACCzB;YACAyB,OAAOC,qBAAqB,CAAC5B,gBAAgBI;YAC7C,IAAIe,WAAWU,OAAO,CAACZ,WAAW,EAAE;gBAClCU,OAAOG,MAAM;YACf;QACF,GACA;YAAC5B;YAAeF;YAAgBI;SAAe;QAGjD2B,IAAAA,yCAAyB,EAAC;YACxB,YAAY;YACZ,2FAA2F;YAC3FZ,WAAWU,OAAO,GAAG;gBAAEZ;gBAAaT;YAAO;QAC7C;QAEAuB,IAAAA,yCAAyB,EAAC;YACxB,MAAMC,UAAUtB,SAASmB,OAAO;YAEhC,IAAIG,SAAS;gBACX,MAAMC,QAAQ,OAAOtC,UAAU,aAAaA,MAAM;oBAAEqC;oBAAS,GAAGb,WAAWU,OAAO,CAACrB,MAAM;gBAAC,KAAKb;gBAE/F,MAAMgC,SAASP,aAAaY,SAASC,OAAO;oBAAEX,iBAAiBA;gBAAkB;gBACjFV,UAAUiB,OAAO,GAAGF;gBACpBF,wBAAwBE;gBAExB,OAAO;oBACLA,OAAOO,MAAM;gBACf;YACF;QACF,GAAG;YAACd;YAAcV;YAAUE;YAAWU;YAAiBG;SAAwB;QAEhF,+FAA+F;QAC/F,0DAA0D;QAC1DM,IAAAA,yCAAyB,EAAC;YACxB,IAAIjB,gBAAgBe,OAAO,EAAE;gBAC3B;YACF;YAEA,MAAMF,SAASf,UAAUiB,OAAO;YAChC,IAAIF,QAAQ;gBACVA,OAAOO,MAAM;gBACbP,OAAOQ,IAAI;gBACXV,wBAAwBE;YAC1B;QACA,4IAA4I;QAC9I,GAAG;YAACrB;SAAU;QAEdyB,IAAAA,yCAAyB,EAAC;YACxBjB,gBAAgBe,OAAO,GAAG;YAE1B,OAAO;gBACLf,gBAAgBe,OAAO,GAAG;YAC5B;QACF,GAAG,EAAE;QAEL,OAAOpB;IACT;IAEA,OAAO2B,OAAOC,MAAM,CAACzC,MAAM;QACzB,YAAY;QACZ,sDAAsD;QACtD,CAACJ,kBAAkB,EAAE,OAAOG,UAAU,aAAaA,QAAQ,IAAMA;IACnE;AACF"}
|
|
@@ -32,7 +32,6 @@ const PRESENCE_MOTION_DEFINITION = Symbol('PRESENCE_MOTION_DEFINITION');
|
|
|
32
32
|
const INTERRUPTABLE_MOTION_SYMBOL = Symbol.for('interruptablePresence');
|
|
33
33
|
function createPresenceComponent(value) {
|
|
34
34
|
return Object.assign((props)=>{
|
|
35
|
-
'use no memo';
|
|
36
35
|
const itemContext = _react.useContext(_PresenceGroupChildContext.PresenceGroupChildContext);
|
|
37
36
|
const merged = {
|
|
38
37
|
...itemContext,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["'use client';\n\nimport { useEventCallback, useFirstMount, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport type { JSXElement } 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 { useChildElement } from '../utils/useChildElement';\nimport type {\n MotionParam,\n PresenceMotion,\n MotionImperativeRef,\n PresenceMotionFn,\n PresenceDirection,\n AnimationHandle,\n} from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\nimport type { MotionComponent } from './createMotionComponent';\nimport { createMotionComponent } from './createMotionComponent';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const PRESENCE_MOTION_DEFINITION = Symbol('PRESENCE_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: JSXElement;\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> = {}> = React.FC<\n PresenceComponentProps & MotionParams\n> & {\n (props: PresenceComponentProps & MotionParams): JSXElement | null;\n [PRESENCE_MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n In: MotionComponent<MotionParams>;\n Out: MotionComponent<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, childRef] = useChildElement(children, mounted);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\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 = childRef.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 [\n animateAtoms,\n childRef,\n handleRef,\n isReducedMotion,\n handleMotionFinish,\n handleMotionStart,\n handleMotionCancel,\n visible,\n ],\n );\n\n React.useEffect(() => {\n // Heads up!\n //\n // Dispose the handle when unmounting the component to clean up retained references. Doing it in a separate\n // effect to ensure that the component is unmounted.\n\n if (unmountOnExit && !mounted) {\n handleRef.current?.dispose();\n }\n }, [handleRef, unmountOnExit, mounted]);\n\n if (mounted) {\n return child;\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [PRESENCE_MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n {\n // Wrap `enter` in its own motion component as a static method, e.g. <Fade.In>\n In: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `enter`.\n // Otherwise, pass the `enter` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).enter : value.enter,\n ),\n\n // Wrap `exit` in its own motion component as a static method, e.g. <Fade.Out>\n Out: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `exit`.\n // Otherwise, pass the `exit` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).exit : value.exit,\n ),\n },\n );\n}\n"],"names":["PRESENCE_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","childRef","useChildElement","handleRef","useMotionImperativeRef","optionsRef","useRef","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","useEffect","dispose","In","createMotionComponent","args","Out"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/factories/createPresenceComponent.ts"],"sourcesContent":["'use client';\n\nimport { useEventCallback, useFirstMount, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport type { JSXElement } 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 { useChildElement } from '../utils/useChildElement';\nimport type {\n MotionParam,\n PresenceMotion,\n MotionImperativeRef,\n PresenceMotionFn,\n PresenceDirection,\n AnimationHandle,\n} from '../types';\nimport { useMotionBehaviourContext } from '../contexts/MotionBehaviourContext';\nimport type { MotionComponent } from './createMotionComponent';\nimport { createMotionComponent } from './createMotionComponent';\n\n/**\n * A private symbol to store the motion definition on the component for variants.\n *\n * @internal\n */\nexport const PRESENCE_MOTION_DEFINITION = Symbol('PRESENCE_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: JSXElement;\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> = {}> = React.FC<\n PresenceComponentProps & MotionParams\n> & {\n (props: PresenceComponentProps & MotionParams): JSXElement | null;\n [PRESENCE_MOTION_DEFINITION]: PresenceMotionFn<MotionParams>;\n In: MotionComponent<MotionParams>;\n Out: MotionComponent<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 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, childRef] = useChildElement(children, mounted);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\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 = childRef.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 [\n animateAtoms,\n childRef,\n handleRef,\n isReducedMotion,\n handleMotionFinish,\n handleMotionStart,\n handleMotionCancel,\n visible,\n ],\n );\n\n React.useEffect(() => {\n // Heads up!\n //\n // Dispose the handle when unmounting the component to clean up retained references. Doing it in a separate\n // effect to ensure that the component is unmounted.\n\n if (unmountOnExit && !mounted) {\n handleRef.current?.dispose();\n }\n }, [handleRef, unmountOnExit, mounted]);\n\n if (mounted) {\n return child;\n }\n\n return null;\n },\n {\n // Heads up!\n // Always normalize it to a function to simplify types\n [PRESENCE_MOTION_DEFINITION]: typeof value === 'function' ? value : () => value,\n },\n {\n // Wrap `enter` in its own motion component as a static method, e.g. <Fade.In>\n In: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `enter`.\n // Otherwise, pass the `enter` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).enter : value.enter,\n ),\n\n // Wrap `exit` in its own motion component as a static method, e.g. <Fade.Out>\n Out: createMotionComponent(\n // If we have a motion function, wrap it to forward the runtime params and pick `exit`.\n // Otherwise, pass the `exit` motion object directly.\n typeof value === 'function' ? (...args: Parameters<typeof value>) => value(...args).exit : value.exit,\n ),\n },\n );\n}\n"],"names":["PRESENCE_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","childRef","useChildElement","handleRef","useMotionImperativeRef","optionsRef","useRef","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","useEffect","dispose","In","createMotionComponent","args","Out"],"mappings":"AAAA;;;;;;;;;;;;IA6BaA,0BAA0B;eAA1BA;;IAiEGC,uBAAuB;eAAvBA;;;;gCA5F2D;iEAEpD;2CAEmB;iCACV;wCACO;iCACP;oCACG;iCACH;wCASU;uCAEJ;AAO/B,MAAMD,6BAA6BE,OAAO;AA+DjD,MAAMC,8BAA8BD,OAAOE,GAAG,CAAC;AAExC,SAASH,wBACdI,KAAsD;IAEtD,OAAOC,OAAOC,MAAM,CAClB,CAACC;QACC,MAAMC,cAAcC,OAAMC,UAAU,CAACC,oDAAyB;QAC9D,MAAMC,SAAS;YAAE,GAAGJ,WAAW;YAAE,GAAGD,KAAK;QAAC;QAC1C,MAAMM,cAAcC,IAAAA,iDAAyB,QAAO;QAEpD,MAAM,EACJC,MAAM,EACNC,QAAQ,EACRC,aAAa,EACbC,MAAM,EACNC,cAAc,EACdC,aAAa,EACbC,cAAc,EACdC,OAAO,EACPC,aAAa,EACb,GAAGC,OACJ,GAAGZ;QACJ,MAAMa,SAASD;QAEf,MAAM,CAACE,SAASC,WAAW,GAAGC,IAAAA,gCAAe,EAACN,SAASC;QACvD,MAAM,CAACM,OAAOC,SAAS,GAAGC,IAAAA,gCAAe,EAACf,UAAUU;QAEpD,MAAMM,YAAYC,IAAAA,8CAAsB,EAAChB;QACzC,MAAMiB,aAAazB,OAAM0B,MAAM,CAAmE;YAChGpB;YACAU;YACAZ;QACF;QAEA,MAAMuB,eAAeC,IAAAA,gCAAe;QACpC,MAAMC,eAAeC,IAAAA,6BAAa;QAClC,MAAMC,kBAAkBC,IAAAA,sCAAkB;QAE1C,MAAMC,oBAAoBC,IAAAA,gCAAgB,EAAC,CAACC;YAC1CxB,0BAAAA,oCAAAA,cAAgB,MAAM;gBAAEwB;YAAU;QACpC;QACA,MAAMC,qBAAqBF,IAAAA,gCAAgB,EAAC,CAACC;YAC3CzB,2BAAAA,qCAAAA,eAAiB,MAAM;gBAAEyB;YAAU;YAEnC,IAAIA,cAAc,UAAUrB,eAAe;gBACzCI,WAAW;gBACXT,mBAAAA,6BAAAA;YACF;QACF;QAEA,MAAM4B,qBAAqBH,IAAAA,gCAAgB,EAAC,CAACC;YAC3CvB,2BAAAA,qCAAAA,eAAiB,MAAM;gBAAEuB;YAAU;QACrC;QAEAG,IAAAA,yCAAyB,EAAC;YACxB,YAAY;YACZ,2FAA2F;YAC3Fb,WAAWc,OAAO,GAAG;gBAAEjC;gBAAQU;gBAAQZ;YAAY;QACrD;QAEAkC,IAAAA,yCAAyB,EACvB;YACE,MAAME,UAAUnB,SAASkB,OAAO;YAEhC,IAAI,CAACC,SAAS;gBACZ;YACF;YAEA,IAAIC;YAEJ,SAASC;gBACP,IAAI,CAACD,QAAQ;oBACX;gBACF;gBAEA,YAAY;gBACZ,EAAE;gBACF,uGAAuG;gBACvG,mBAAmB;gBACnB,IAAIE,wCAAwCF,OAAOG,SAAS,IAAI;oBAC9D;gBACF;gBAEAH,OAAOI,MAAM;gBACbtB,UAAUgB,OAAO,GAAGO;YACtB;YAEA,MAAMC,iBACJ,OAAOpD,UAAU,aAAaA,MAAM;gBAAE6C;gBAAS,GAAGf,WAAWc,OAAO,CAACvB,MAAM;YAAC,KAAMrB;YACpF,MAAMgD,uCAAuC,AAC3CI,cACD,CAACtD,4BAA4B;YAE9B,IAAIkD,sCAAsC;gBACxCF,SAASlB,UAAUgB,OAAO;gBAE1B,IAAIE,UAAUA,OAAOG,SAAS,IAAI;oBAChCH,OAAOO,OAAO;oBAEd,OAAON;gBACT;YACF;YAEA,MAAMO,QAAQpC,UAAUkC,eAAeG,KAAK,GAAGH,eAAeI,IAAI;YAClE,MAAMhB,YAA+BtB,UAAU,UAAU;YAEzD,YAAY;YACZ,kJAAkJ;YAClJ,MAAMuC,qBAAqB,CAAC3B,WAAWc,OAAO,CAACjC,MAAM,IAAIuB;YACzD,MAAMwB,wBAAwB5B,WAAWc,OAAO,CAACnC,WAAW;YAE5D,IAAI,CAACgD,oBAAoB;gBACvBnB,kBAAkBE;YACpB;YAEAM,SAASd,aAAaa,SAASS,OAAO;gBAAElB,iBAAiBA;YAAkB;YAE3E,IAAIqB,oBAAoB;gBACtB,YAAY;gBACZ,0FAA0F;gBAC1FX,OAAOa,MAAM;gBAEb,OAAOZ;YACT;YAEAnB,UAAUgB,OAAO,GAAGE;YACpBA,OAAOc,qBAAqB,CAC1B,IAAMnB,mBAAmBD,YACzB,IAAME,mBAAmBF;YAG3B,IAAIkB,uBAAuB;gBACzBZ,OAAOa,MAAM;YACf;YAEA,OAAOZ;QACT,GACA,kGAAkG;QAClG,uDAAuD;QACvD;YACEf;YACAN;YACAE;YACAQ;YACAK;YACAH;YACAI;YACAxB;SACD;QAGHb,OAAMwD,SAAS,CAAC;YACd,YAAY;YACZ,EAAE;YACF,2GAA2G;YAC3G,oDAAoD;YAEpD,IAAI1C,iBAAiB,CAACG,SAAS;oBAC7BM;iBAAAA,qBAAAA,UAAUgB,OAAO,cAAjBhB,yCAAAA,mBAAmBkC,OAAO;YAC5B;QACF,GAAG;YAAClC;YAAWT;YAAeG;SAAQ;QAEtC,IAAIA,SAAS;YACX,OAAOG;QACT;QAEA,OAAO;IACT,GACA;QACE,YAAY;QACZ,sDAAsD;QACtD,CAAC9B,2BAA2B,EAAE,OAAOK,UAAU,aAAaA,QAAQ,IAAMA;IAC5E,GACA;QACE,8EAA8E;QAC9E+D,IAAIC,IAAAA,4CAAqB,EACvB,wFAAwF;QACxF,sDAAsD;QACtD,OAAOhE,UAAU,aAAa,CAAC,GAAGiE,OAAmCjE,SAASiE,MAAMV,KAAK,GAAGvD,MAAMuD,KAAK;QAGzG,8EAA8E;QAC9EW,KAAKF,IAAAA,4CAAqB,EACxB,uFAAuF;QACvF,qDAAqD;QACrD,OAAOhE,UAAU,aAAa,CAAC,GAAGiE,OAAmCjE,SAASiE,MAAMT,IAAI,GAAGxD,MAAMwD,IAAI;IAEzG;AAEJ"}
|
|
@@ -191,7 +191,6 @@ function useAnimateAtomsInSupportedEnvironment() {
|
|
|
191
191
|
]);
|
|
192
192
|
}
|
|
193
193
|
function useAnimateAtoms() {
|
|
194
|
-
'use no memo';
|
|
195
194
|
if (process.env.NODE_ENV === 'test') {
|
|
196
195
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
197
196
|
return useAnimateAtomsInTestEnvironment();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["'use client';\n\nimport * 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\n/**\n * Creates an animation handle that controls multiple animations.\n * Is used to avoid leaking \"element\" references from the hook.\n *\n * @param animations\n */\nfunction createHandle(animations: Animation[]): AnimationHandle {\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 dispose: () => {\n animations.length = 0;\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\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 createHandle(animations);\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>(undefined);\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 dispose() {\n /* no-op */\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 element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: { isReducedMotion: boolean },\n) => AnimationHandle {\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","createHandle","animations","playbackRate","rate","forEach","animation","setMotionEndCallbacks","onfinish","oncancel","promises","map","Promise","resolve","reject","all","then","catch","isRunning","some","isAnimationRunning","dispose","length","cancel","pause","play","finish","reverse","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","React","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animate","resultKeyframe","Object","assign","style","e","filter","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","undefined","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","process","env","NODE_ENV"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useAnimateAtoms.ts"],"sourcesContent":["'use client';\n\nimport * 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\n/**\n * Creates an animation handle that controls multiple animations.\n * Is used to avoid leaking \"element\" references from the hook.\n *\n * @param animations\n */\nfunction createHandle(animations: Animation[]): AnimationHandle {\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 dispose: () => {\n animations.length = 0;\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\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 createHandle(animations);\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>(undefined);\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 dispose() {\n /* no-op */\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 element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: { isReducedMotion: boolean },\n) => AnimationHandle {\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","createHandle","animations","playbackRate","rate","forEach","animation","setMotionEndCallbacks","onfinish","oncancel","promises","map","Promise","resolve","reject","all","then","catch","isRunning","some","isAnimationRunning","dispose","length","cancel","pause","play","finish","reverse","useAnimateAtomsInSupportedEnvironment","window","SUPPORTS_PERSIST","Animation","prototype","persist","React","useCallback","element","value","options","atoms","Array","isArray","isReducedMotion","motion","keyframes","motionKeyframes","reducedMotion","params","reducedMotionKeyframes","reducedMotionParams","animationKeyframes","animationParams","animate","resultKeyframe","Object","assign","style","e","filter","useAnimateAtomsInTestEnvironment","count","setCount","useState","callbackRef","useRef","undefined","realAnimateAtoms","useEffect","current","ELEMENT_SUPPORTS_WEB_ANIMATIONS","v","process","env","NODE_ENV"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useMountedState.ts"],"sourcesContent":["'use client';\n\nimport { useForceUpdate } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\n/**\n * This hook manages the mounted state of a component, based on the \"visible\" and \"unmountOnExit\" props.\n * It simulates the behavior of getDerivedStateFromProps(), which is not available in functional components.\n */\nexport function useMountedState(\n visible: boolean = false,\n unmountOnExit: boolean = false,\n): [boolean, (value: boolean) => void] {\n const mountedRef = React.useRef<boolean>(unmountOnExit ? visible : true);\n const forceUpdate = useForceUpdate();\n\n const setMounted = React.useCallback(\n (newValue: boolean) => {\n if (mountedRef.current !== newValue) {\n mountedRef.current = newValue;\n forceUpdate();\n }\n },\n [forceUpdate],\n );\n\n React.useEffect(() => {\n if (visible) {\n mountedRef.current = visible;\n }\n });\n\n return [visible || mountedRef.current, setMounted];\n}\n"],"names":["useMountedState","visible","unmountOnExit","mountedRef","React","useRef","forceUpdate","useForceUpdate","setMounted","useCallback","newValue","current","useEffect"],"mappings":"AAAA;;;;;+BASgBA;;;eAAAA;;;;gCAPe;iEACR;AAMhB,SAASA,gBACdC,UAAmB,KAAK,EACxBC,gBAAyB,KAAK;IAE9B,MAAMC,aAAaC,OAAMC,MAAM,CAAUH,gBAAgBD,UAAU;IACnE,MAAMK,cAAcC,IAAAA,8BAAc;IAElC,MAAMC,aAAaJ,OAAMK,WAAW,CAClC,CAACC;QACC,IAAIP,WAAWQ,OAAO,KAAKD,UAAU;YACnCP,WAAWQ,OAAO,GAAGD;YACrBJ;QACF;IACF,GACA;QAACA;KAAY;IAGfF,OAAMQ,SAAS,CAAC;QACd,IAAIX,SAAS;YACXE,WAAWQ,OAAO,GAAGV;QACvB;IACF;IAEA,OAAO;QAACA,WAAWE,WAAWQ,OAAO;QAAEH;KAAW;AACpD"}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useMountedState.ts"],"sourcesContent":["'use client';\n\nimport { useForceUpdate } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\n/**\n * This hook manages the mounted state of a component, based on the \"visible\" and \"unmountOnExit\" props.\n * It simulates the behavior of getDerivedStateFromProps(), which is not available in functional components.\n */\nexport function useMountedState(\n visible: boolean = false,\n unmountOnExit: boolean = false,\n): [boolean, (value: boolean) => void] {\n const mountedRef = React.useRef<boolean>(unmountOnExit ? visible : true);\n const forceUpdate = useForceUpdate();\n\n const setMounted = React.useCallback(\n (newValue: boolean) => {\n if (mountedRef.current !== newValue) {\n mountedRef.current = newValue;\n forceUpdate();\n }\n },\n [forceUpdate],\n );\n\n React.useEffect(() => {\n if (visible) {\n mountedRef.current = visible;\n }\n });\n\n // eslint-disable-next-line react-hooks/refs\n return [visible || mountedRef.current, setMounted];\n}\n"],"names":["useMountedState","visible","unmountOnExit","mountedRef","React","useRef","forceUpdate","useForceUpdate","setMounted","useCallback","newValue","current","useEffect"],"mappings":"AAAA;;;;;+BASgBA;;;eAAAA;;;;gCAPe;iEACR;AAMhB,SAASA,gBACdC,UAAmB,KAAK,EACxBC,gBAAyB,KAAK;IAE9B,MAAMC,aAAaC,OAAMC,MAAM,CAAUH,gBAAgBD,UAAU;IACnE,MAAMK,cAAcC,IAAAA,8BAAc;IAElC,MAAMC,aAAaJ,OAAMK,WAAW,CAClC,CAACC;QACC,IAAIP,WAAWQ,OAAO,KAAKD,UAAU;YACnCP,WAAWQ,OAAO,GAAGD;YACrBJ;QACF;IACF,GACA;QAACA;KAAY;IAGfF,OAAMQ,SAAS,CAAC;QACd,IAAIX,SAAS;YACXE,WAAWQ,OAAO,GAAGV;QACvB;IACF;IAEA,4CAA4C;IAC5C,OAAO;QAACA,WAAWE,WAAWQ,OAAO;QAAEH;KAAW;AACpD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluentui/react-motion",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.16.1",
|
|
4
4
|
"description": "A package with utilities & motion definitions using Web Animations API",
|
|
5
5
|
"main": "lib-commonjs/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@fluentui/react-shared-contexts": "^9.26.2",
|
|
22
|
-
"@fluentui/react-utilities": "^9.26.
|
|
22
|
+
"@fluentui/react-utilities": "^9.26.5",
|
|
23
23
|
"@swc/helpers": "^0.5.1"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|