@fluentui/react-motion 9.0.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,35 @@
1
1
  # Change Log - @fluentui/react-motion
2
2
 
3
- This log was last generated on Thu, 06 Jun 2024 15:22:17 GMT and should not be manually modified.
3
+ This log was last generated on Mon, 17 Jun 2024 07:31:07 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## [9.2.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.2.0)
8
+
9
+ Mon, 17 Jun 2024 07:31:07 GMT
10
+ [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.1.0..@fluentui/react-motion_v9.2.0)
11
+
12
+ ### Minor changes
13
+
14
+ - feat: Implement `onMotionCancel` callback handler ([PR #31698](https://github.com/microsoft/fluentui/pull/31698) by lingfangao@hotmail.com)
15
+
16
+ ## [9.1.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.1.0)
17
+
18
+ Wed, 12 Jun 2024 13:17:19 GMT
19
+ [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motion_v9.0.0..@fluentui/react-motion_v9.1.0)
20
+
21
+ ### Minor changes
22
+
23
+ - feat: add support for params ([PR #31566](https://github.com/microsoft/fluentui/pull/31566) by olfedias@microsoft.com)
24
+ - feat: Add consistent start and finish lifecycle callbacks ([PR #31644](https://github.com/microsoft/fluentui/pull/31644) by lingfangao@hotmail.com)
25
+
26
+ ### Patches
27
+
28
+ - fix(motions): improve compat for jsdom & jest ([PR #31602](https://github.com/microsoft/fluentui/pull/31602) by olfedias@microsoft.com)
29
+
7
30
  ## [9.0.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-motion_v9.0.0)
8
31
 
9
- Thu, 06 Jun 2024 15:22:17 GMT
32
+ Thu, 06 Jun 2024 15:26:35 GMT
10
33
  [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-motions-preview_v0.3.2..@fluentui/react-motion_v9.0.0)
11
34
 
12
35
  ### Minor changes
package/dist/index.d.ts CHANGED
@@ -4,18 +4,18 @@ export declare type AtomMotion = {
4
4
  keyframes: Keyframe[];
5
5
  } & KeyframeEffectOptions;
6
6
 
7
- export declare type AtomMotionFn = (params: {
7
+ export declare type AtomMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (params: {
8
8
  element: HTMLElement;
9
- }) => AtomMotion | AtomMotion[];
9
+ } & MotionParams) => AtomMotion | AtomMotion[];
10
10
 
11
11
  /**
12
12
  * Creates a component that will animate the children using the provided motion.
13
13
  *
14
14
  * @param value - A motion definition.
15
15
  */
16
- export declare function createMotionComponent(value: AtomMotion | AtomMotion[] | AtomMotionFn): React_2.FC<MotionComponentProps>;
16
+ export declare function createMotionComponent<MotionParams extends Record<string, MotionParam> = {}>(value: AtomMotion | AtomMotion[] | AtomMotionFn<MotionParams>): React_2.FC<MotionComponentProps & MotionParams>;
17
17
 
18
- export declare function createPresenceComponent(value: PresenceMotion | PresenceMotionFn): React_2.FC<PresenceComponentProps>;
18
+ export declare function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(value: PresenceMotion | PresenceMotionFn<MotionParams>): React_2.FC<PresenceComponentProps & MotionParams>;
19
19
 
20
20
  export declare const curves: {
21
21
  readonly curveAccelerateMax: "cubic-bezier(0.9,0.1,1,0.2)";
@@ -44,6 +44,28 @@ export declare type MotionComponentProps = {
44
44
  children: React_2.ReactElement;
45
45
  /** Provides imperative controls for the animation. */
46
46
  imperativeRef?: React_2.Ref<MotionImperativeRef | undefined>;
47
+ /**
48
+ * Callback that is called when the whole motion finishes.
49
+ *
50
+ * A motion definition can contain multiple animations and therefore multiple "finish" events. The callback is
51
+ * triggered once all animations have finished with "null" instead of an event object to avoid ambiguity.
52
+ */
53
+ onMotionFinish?: (ev: null) => void;
54
+ /**
55
+ * Callback that is called when the whole motion is cancelled.
56
+ *
57
+ * A motion definition can contain multiple animations and therefore multiple "cancel" events. The callback is
58
+ * triggered once all animations have been cancelled with "null" instead of an event object to avoid ambiguity.
59
+ */
60
+ onMotionCancel?: (ev: null) => void;
61
+ /**
62
+ * Callback that is called when the whole motion starts.
63
+ *
64
+ * A motion definition can contain multiple animations and therefore multiple "start" events. The callback is
65
+ * triggered when the first animation is started. There is no official "start" event with the Web Animations API.
66
+ * so the callback is triggered with "null".
67
+ */
68
+ onMotionStart?: (ev: null) => void;
47
69
  };
48
70
 
49
71
  export declare type MotionImperativeRef = {
@@ -53,6 +75,14 @@ export declare type MotionImperativeRef = {
53
75
  setPlayState: (state: 'running' | 'paused') => void;
54
76
  };
55
77
 
78
+ /**
79
+ * @internal
80
+ *
81
+ * A motion param should be a primitive value that can be serialized to JSON and could be potentially used a plain
82
+ * dependency for React hooks.
83
+ */
84
+ declare type MotionParam = boolean | number | string;
85
+
56
86
  export declare const motionTokens: {
57
87
  curveAccelerateMax: "cubic-bezier(0.9,0.1,1,0.2)";
58
88
  curveAccelerateMid: "cubic-bezier(1,0,1,1)";
@@ -92,6 +122,26 @@ export declare type PresenceComponentProps = {
92
122
  onMotionFinish?: (ev: null, data: {
93
123
  direction: 'enter' | 'exit';
94
124
  }) => void;
125
+ /**
126
+ * Callback that is called when the whole motion is cancelled. When a motion is cancelled it does not
127
+ * emit a finish event but a specific cancel event
128
+ *
129
+ * A motion definition can contain multiple animations and therefore multiple "finish" events. The callback is
130
+ * triggered once all animations have finished with "null" instead of an event object to avoid ambiguity.
131
+ */
132
+ onMotionCancel?: (ev: null, data: {
133
+ direction: 'enter' | 'exit';
134
+ }) => void;
135
+ /**
136
+ * Callback that is called when the whole motion starts.
137
+ *
138
+ * A motion definition can contain multiple animations and therefore multiple "start" events. The callback is
139
+ * triggered when the first animation is started. There is no official "start" event with the Web Animations API.
140
+ * so the callback is triggered with "null".
141
+ */
142
+ onMotionStart?: (ev: null, data: {
143
+ direction: 'enter' | 'exit';
144
+ }) => void;
95
145
  /** Defines whether a component is visible; triggers the "enter" or "exit" motions. */
96
146
  visible?: boolean;
97
147
  /**
@@ -137,8 +187,8 @@ export declare type PresenceMotion = {
137
187
  exit: AtomMotion | AtomMotion[];
138
188
  };
139
189
 
140
- export declare type PresenceMotionFn = (params: {
190
+ export declare type PresenceMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (params: {
141
191
  element: HTMLElement;
142
- }) => PresenceMotion;
192
+ } & MotionParams) => PresenceMotion;
143
193
 
144
194
  export { }
@@ -1,4 +1,4 @@
1
- import { useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';
1
+ import { useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';
2
2
  import * as React from 'react';
3
3
  import { useIsReducedMotion } from '../hooks/useIsReducedMotion';
4
4
  import { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';
@@ -10,20 +10,39 @@ import { getChildElement } from '../utils/getChildElement';
10
10
  * @param value - A motion definition.
11
11
  */ export function createMotionComponent(value) {
12
12
  const Atom = (props)=>{
13
- const { children, imperativeRef } = props;
13
+ const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, ..._rest } = props;
14
+ const params = _rest;
14
15
  const child = getChildElement(children);
15
16
  const handleRef = useMotionImperativeRef(imperativeRef);
16
17
  const elementRef = React.useRef();
18
+ const paramsRef = React.useRef(params);
17
19
  const isReducedMotion = useIsReducedMotion();
20
+ const onMotionStart = useEventCallback(()=>{
21
+ onMotionStartProp === null || onMotionStartProp === void 0 ? void 0 : onMotionStartProp(null);
22
+ });
23
+ const onMotionFinish = useEventCallback(()=>{
24
+ onMotionFinishProp === null || onMotionFinishProp === void 0 ? void 0 : onMotionFinishProp(null);
25
+ });
26
+ const onMotionCancel = useEventCallback(()=>{
27
+ onMotionCancelProp === null || onMotionCancelProp === void 0 ? void 0 : onMotionCancelProp(null);
28
+ });
29
+ useIsomorphicLayoutEffect(()=>{
30
+ // Heads up!
31
+ // We store the params in a ref to avoid re-rendering the component when the params change.
32
+ paramsRef.current = params;
33
+ });
18
34
  useIsomorphicLayoutEffect(()=>{
19
35
  const element = elementRef.current;
20
36
  if (element) {
21
37
  const atoms = typeof value === 'function' ? value({
22
- element
38
+ element,
39
+ ...paramsRef.current
23
40
  }) : value;
41
+ onMotionStart();
24
42
  const handle = animateAtoms(element, atoms, {
25
43
  isReducedMotion: isReducedMotion()
26
44
  });
45
+ handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);
27
46
  handleRef.current = handle;
28
47
  return ()=>{
29
48
  handle.cancel();
@@ -31,7 +50,10 @@ import { getChildElement } from '../utils/getChildElement';
31
50
  }
32
51
  }, [
33
52
  handleRef,
34
- isReducedMotion
53
+ isReducedMotion,
54
+ onMotionFinish,
55
+ onMotionStart,
56
+ onMotionCancel
35
57
  ]);
36
58
  return React.cloneElement(children, {
37
59
  ref: useMergedRefs(elementRef, child.ref)
@@ -1 +1 @@
1
- {"version":3,"sources":["createMotionComponent.ts"],"sourcesContent":["import { useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\nimport type { AtomMotion, AtomMotionFn, MotionImperativeRef } from '../types';\n\nexport type MotionComponentProps = {\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\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(value: AtomMotion | AtomMotion[] | AtomMotionFn) {\n const Atom: React.FC<MotionComponentProps> = props => {\n const { children, imperativeRef } = props;\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n\n const isReducedMotion = useIsReducedMotion();\n\n useIsomorphicLayoutEffect(() => {\n const element = elementRef.current;\n\n if (element) {\n const atoms = typeof value === 'function' ? value({ element }) : value;\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n handleRef.current = handle;\n\n return () => {\n handle.cancel();\n };\n }\n }, [handleRef, isReducedMotion]);\n\n return React.cloneElement(children, { ref: useMergedRefs(elementRef, child.ref) });\n };\n\n return Atom;\n}\n"],"names":["useIsomorphicLayoutEffect","useMergedRefs","React","useIsReducedMotion","useMotionImperativeRef","animateAtoms","getChildElement","createMotionComponent","value","Atom","props","children","imperativeRef","child","handleRef","elementRef","useRef","isReducedMotion","element","current","atoms","handle","cancel","cloneElement","ref"],"mappings":"AAAA,SAASA,yBAAyB,EAAEC,aAAa,QAAQ,4BAA4B;AACrF,YAAYC,WAAW,QAAQ;AAE/B,SAASC,kBAAkB,QAAQ,8BAA8B;AACjE,SAASC,sBAAsB,QAAQ,kCAAkC;AACzE,SAASC,YAAY,QAAQ,wBAAwB;AACrD,SAASC,eAAe,QAAQ,2BAA2B;AAU3D;;;;CAIC,GACD,OAAO,SAASC,sBAAsBC,KAA+C;IACnF,MAAMC,OAAuCC,CAAAA;QAC3C,MAAM,EAAEC,QAAQ,EAAEC,aAAa,EAAE,GAAGF;QACpC,MAAMG,QAAQP,gBAAgBK;QAE9B,MAAMG,YAAYV,uBAAuBQ;QACzC,MAAMG,aAAab,MAAMc,MAAM;QAE/B,MAAMC,kBAAkBd;QAExBH,0BAA0B;YACxB,MAAMkB,UAAUH,WAAWI,OAAO;YAElC,IAAID,SAAS;gBACX,MAAME,QAAQ,OAAOZ,UAAU,aAAaA,MAAM;oBAAEU;gBAAQ,KAAKV;gBACjE,MAAMa,SAAShB,aAAaa,SAASE,OAAO;oBAAEH,iBAAiBA;gBAAkB;gBAEjFH,UAAUK,OAAO,GAAGE;gBAEpB,OAAO;oBACLA,OAAOC,MAAM;gBACf;YACF;QACF,GAAG;YAACR;YAAWG;SAAgB;QAE/B,OAAOf,MAAMqB,YAAY,CAACZ,UAAU;YAAEa,KAAKvB,cAAcc,YAAYF,MAAMW,GAAG;QAAE;IAClF;IAEA,OAAOf;AACT"}
1
+ {"version":3,"sources":["createMotionComponent.ts"],"sourcesContent":["import { useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\nimport type { AtomMotion, AtomMotionFn, MotionParam, MotionImperativeRef } from '../types';\n\nexport type MotionComponentProps = {\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null) => 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/**\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) {\n const Atom: React.FC<MotionComponentProps & MotionParams> = props => {\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 = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const paramsRef = React.useRef<MotionParams>(params);\n\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 paramsRef.current = params;\n });\n\n useIsomorphicLayoutEffect(() => {\n const element = elementRef.current;\n\n if (element) {\n const atoms = typeof value === 'function' ? value({ element, ...paramsRef.current }) : value;\n onMotionStart();\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);\n handleRef.current = handle;\n\n return () => {\n handle.cancel();\n };\n }\n }, [handleRef, isReducedMotion, onMotionFinish, onMotionStart, onMotionCancel]);\n\n return React.cloneElement(children, { ref: useMergedRefs(elementRef, child.ref) });\n };\n\n return Atom;\n}\n"],"names":["useEventCallback","useIsomorphicLayoutEffect","useMergedRefs","React","useIsReducedMotion","useMotionImperativeRef","animateAtoms","getChildElement","createMotionComponent","value","Atom","props","children","imperativeRef","onMotionFinish","onMotionFinishProp","onMotionStart","onMotionStartProp","onMotionCancel","onMotionCancelProp","_rest","params","child","handleRef","elementRef","useRef","paramsRef","isReducedMotion","current","element","atoms","handle","setMotionEndCallbacks","cancel","cloneElement","ref"],"mappings":"AAAA,SAASA,gBAAgB,EAAEC,yBAAyB,EAAEC,aAAa,QAAQ,4BAA4B;AACvG,YAAYC,WAAW,QAAQ;AAE/B,SAASC,kBAAkB,QAAQ,8BAA8B;AACjE,SAASC,sBAAsB,QAAQ,kCAAkC;AACzE,SAASC,YAAY,QAAQ,wBAAwB;AACrD,SAASC,eAAe,QAAQ,2BAA2B;AAsC3D;;;;CAIC,GACD,OAAO,SAASC,sBACdC,KAA6D;IAE7D,MAAMC,OAAsDC,CAAAA;QAC1D,MAAM,EACJC,QAAQ,EACRC,aAAa,EACbC,gBAAgBC,kBAAkB,EAClCC,eAAeC,iBAAiB,EAChCC,gBAAgBC,kBAAkB,EAClC,GAAGC,OACJ,GAAGT;QACJ,MAAMU,SAASD;QACf,MAAME,QAAQf,gBAAgBK;QAE9B,MAAMW,YAAYlB,uBAAuBQ;QACzC,MAAMW,aAAarB,MAAMsB,MAAM;QAC/B,MAAMC,YAAYvB,MAAMsB,MAAM,CAAeJ;QAE7C,MAAMM,kBAAkBvB;QAExB,MAAMY,gBAAgBhB,iBAAiB;YACrCiB,8BAAAA,wCAAAA,kBAAoB;QACtB;QAEA,MAAMH,iBAAiBd,iBAAiB;YACtCe,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEA,MAAMG,iBAAiBlB,iBAAiB;YACtCmB,+BAAAA,yCAAAA,mBAAqB;QACvB;QAEAlB,0BAA0B;YACxB,YAAY;YACZ,2FAA2F;YAC3FyB,UAAUE,OAAO,GAAGP;QACtB;QAEApB,0BAA0B;YACxB,MAAM4B,UAAUL,WAAWI,OAAO;YAElC,IAAIC,SAAS;gBACX,MAAMC,QAAQ,OAAOrB,UAAU,aAAaA,MAAM;oBAAEoB;oBAAS,GAAGH,UAAUE,OAAO;gBAAC,KAAKnB;gBACvFO;gBAEA,MAAMe,SAASzB,aAAauB,SAASC,OAAO;oBAAEH,iBAAiBA;gBAAkB;gBAEjFI,OAAOC,qBAAqB,CAAClB,gBAAgBI;gBAC7CK,UAAUK,OAAO,GAAGG;gBAEpB,OAAO;oBACLA,OAAOE,MAAM;gBACf;YACF;QACF,GAAG;YAACV;YAAWI;YAAiBb;YAAgBE;YAAeE;SAAe;QAE9E,OAAOf,MAAM+B,YAAY,CAACtB,UAAU;YAAEuB,KAAKjC,cAAcsB,YAAYF,MAAMa,GAAG;QAAE;IAClF;IAEA,OAAOzB;AACT"}
@@ -7,40 +7,53 @@ import { useMountedState } from '../hooks/useMountedState';
7
7
  import { animateAtoms } from '../utils/animateAtoms';
8
8
  import { getChildElement } from '../utils/getChildElement';
9
9
  function shouldSkipAnimation(appear, isFirstMount, visible) {
10
- return !appear && isFirstMount && visible;
10
+ return !appear && isFirstMount && !!visible;
11
11
  }
12
12
  export function createPresenceComponent(value) {
13
13
  const Presence = (props)=>{
14
14
  const itemContext = React.useContext(PresenceGroupChildContext);
15
- const { appear, children, imperativeRef, onMotionFinish, visible, unmountOnExit } = {
15
+ const merged = {
16
16
  ...itemContext,
17
17
  ...props
18
18
  };
19
+ const { appear, children, imperativeRef, onExit, onMotionFinish, onMotionStart, onMotionCancel, visible, unmountOnExit, ..._rest } = merged;
20
+ const params = _rest;
19
21
  const [mounted, setMounted] = useMountedState(visible, unmountOnExit);
20
22
  const child = getChildElement(children);
21
23
  const handleRef = useMotionImperativeRef(imperativeRef);
22
24
  const elementRef = React.useRef();
23
25
  const ref = useMergedRefs(elementRef, child.ref);
24
- const optionsRef = React.useRef({});
26
+ const optionsRef = React.useRef({
27
+ appear,
28
+ params
29
+ });
25
30
  const isFirstMount = useFirstMount();
26
31
  const isReducedMotion = useIsReducedMotion();
27
- const onEnterFinish = useEventCallback(()=>{
28
- onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
29
- direction: 'enter'
32
+ const handleMotionStart = useEventCallback((direction)=>{
33
+ onMotionStart === null || onMotionStart === void 0 ? void 0 : onMotionStart(null, {
34
+ direction
30
35
  });
31
36
  });
32
- const onExitFinish = useEventCallback(()=>{
37
+ const handleMotionFinish = useEventCallback((direction)=>{
33
38
  onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
34
- direction: 'exit'
39
+ direction
35
40
  });
36
- if (unmountOnExit) {
41
+ if (direction === 'exit' && unmountOnExit) {
37
42
  setMounted(false);
38
- itemContext === null || itemContext === void 0 ? void 0 : itemContext.onExit();
43
+ onExit === null || onExit === void 0 ? void 0 : onExit();
39
44
  }
40
45
  });
46
+ const handleMotionCancel = useEventCallback((direction)=>{
47
+ onMotionCancel === null || onMotionCancel === void 0 ? void 0 : onMotionCancel(null, {
48
+ direction
49
+ });
50
+ });
41
51
  useIsomorphicLayoutEffect(()=>{
52
+ // Heads up!
53
+ // We store the params in a ref to avoid re-rendering the component when the params change.
42
54
  optionsRef.current = {
43
- appear
55
+ appear,
56
+ params
44
57
  };
45
58
  });
46
59
  useIsomorphicLayoutEffect(()=>{
@@ -49,20 +62,26 @@ export function createPresenceComponent(value) {
49
62
  return;
50
63
  }
51
64
  const presenceMotion = typeof value === 'function' ? value({
52
- element
65
+ element,
66
+ ...optionsRef.current.params
53
67
  }) : value;
54
68
  const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
69
+ const direction = visible ? 'enter' : 'exit';
70
+ const forceFinishMotion = !visible && isFirstMount;
71
+ if (!forceFinishMotion) {
72
+ handleMotionStart(direction);
73
+ }
55
74
  const handle = animateAtoms(element, atoms, {
56
75
  isReducedMotion: isReducedMotion()
57
76
  });
58
- if (!visible && isFirstMount) {
77
+ if (forceFinishMotion) {
59
78
  // Heads up!
60
79
  // .finish() is used there to skip animation on first mount, but apply animation styles immediately
61
80
  handle.finish();
62
81
  return;
63
82
  }
64
83
  handleRef.current = handle;
65
- handle.onfinish = visible ? onEnterFinish : onExitFinish;
84
+ handle.setMotionEndCallbacks(()=>handleMotionFinish(direction), ()=>handleMotionCancel(direction));
66
85
  return ()=>{
67
86
  handle.cancel();
68
87
  };
@@ -71,8 +90,9 @@ export function createPresenceComponent(value) {
71
90
  [
72
91
  handleRef,
73
92
  isReducedMotion,
74
- onEnterFinish,
75
- onExitFinish,
93
+ handleMotionFinish,
94
+ handleMotionStart,
95
+ handleMotionCancel,
76
96
  visible
77
97
  ]);
78
98
  if (mounted) {
@@ -1 +1 @@
1
- {"version":3,"sources":["createPresenceComponent.ts"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\nimport type { PresenceMotion, MotionImperativeRef, PresenceMotionFn } from '../types';\n\nexport type PresenceComponentProps = {\n /**\n * By default, the child component won't execute the \"enter\" motion when it initially mounts, regardless of the value\n * of \"visible\". If you desire this behavior, ensure both \"appear\" and \"visible\" are set to \"true\".\n */\n appear?: boolean;\n\n /** A React element that will be cloned and will have motion effects applied to it. */\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null, data: { direction: 'enter' | 'exit' }) => 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\nfunction shouldSkipAnimation(appear: boolean | undefined, isFirstMount: boolean, visible: boolean | undefined) {\n return !appear && isFirstMount && visible;\n}\n\nexport function createPresenceComponent(value: PresenceMotion | PresenceMotionFn) {\n const Presence: React.FC<PresenceComponentProps> = props => {\n const itemContext = React.useContext(PresenceGroupChildContext);\n const { appear, children, imperativeRef, onMotionFinish, visible, unmountOnExit } = { ...itemContext, ...props };\n\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef<{ appear?: boolean }>({});\n\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n\n const onEnterFinish = useEventCallback(() => {\n onMotionFinish?.(null, { direction: 'enter' });\n });\n const onExitFinish = useEventCallback(() => {\n onMotionFinish?.(null, { direction: 'exit' });\n\n if (unmountOnExit) {\n setMounted(false);\n itemContext?.onExit();\n }\n });\n\n useIsomorphicLayoutEffect(() => {\n optionsRef.current = { appear };\n });\n\n useIsomorphicLayoutEffect(\n () => {\n const element = elementRef.current;\n\n if (!element || shouldSkipAnimation(optionsRef.current.appear, isFirstMount, visible)) {\n return;\n }\n\n const presenceMotion = typeof value === 'function' ? value({ element }) : value;\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n if (!visible && isFirstMount) {\n // Heads up!\n // .finish() is used there to skip animation on first mount, but apply animation styles immediately\n handle.finish();\n return;\n }\n\n handleRef.current = handle;\n handle.onfinish = visible ? onEnterFinish : onExitFinish;\n\n return () => {\n handle.cancel();\n };\n },\n // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [handleRef, isReducedMotion, onEnterFinish, onExitFinish, visible],\n );\n\n if (mounted) {\n return React.cloneElement(child, { ref });\n }\n\n return null;\n };\n\n return Presence;\n}\n"],"names":["useEventCallback","useFirstMount","useIsomorphicLayoutEffect","useMergedRefs","React","PresenceGroupChildContext","useIsReducedMotion","useMotionImperativeRef","useMountedState","animateAtoms","getChildElement","shouldSkipAnimation","appear","isFirstMount","visible","createPresenceComponent","value","Presence","props","itemContext","useContext","children","imperativeRef","onMotionFinish","unmountOnExit","mounted","setMounted","child","handleRef","elementRef","useRef","ref","optionsRef","isReducedMotion","onEnterFinish","direction","onExitFinish","onExit","current","element","presenceMotion","atoms","enter","exit","handle","finish","onfinish","cancel","cloneElement"],"mappings":"AAAA,SAASA,gBAAgB,EAAEC,aAAa,EAAEC,yBAAyB,EAAEC,aAAa,QAAQ,4BAA4B;AACtH,YAAYC,WAAW,QAAQ;AAE/B,SAASC,yBAAyB,QAAQ,wCAAwC;AAClF,SAASC,kBAAkB,QAAQ,8BAA8B;AACjE,SAASC,sBAAsB,QAAQ,kCAAkC;AACzE,SAASC,eAAe,QAAQ,2BAA2B;AAC3D,SAASC,YAAY,QAAQ,wBAAwB;AACrD,SAASC,eAAe,QAAQ,2BAA2B;AAmC3D,SAASC,oBAAoBC,MAA2B,EAAEC,YAAqB,EAAEC,OAA4B;IAC3G,OAAO,CAACF,UAAUC,gBAAgBC;AACpC;AAEA,OAAO,SAASC,wBAAwBC,KAAwC;IAC9E,MAAMC,WAA6CC,CAAAA;QACjD,MAAMC,cAAcf,MAAMgB,UAAU,CAACf;QACrC,MAAM,EAAEO,MAAM,EAAES,QAAQ,EAAEC,aAAa,EAAEC,cAAc,EAAET,OAAO,EAAEU,aAAa,EAAE,GAAG;YAAE,GAAGL,WAAW;YAAE,GAAGD,KAAK;QAAC;QAE/G,MAAM,CAACO,SAASC,WAAW,GAAGlB,gBAAgBM,SAASU;QACvD,MAAMG,QAAQjB,gBAAgBW;QAE9B,MAAMO,YAAYrB,uBAAuBe;QACzC,MAAMO,aAAazB,MAAM0B,MAAM;QAC/B,MAAMC,MAAM5B,cAAc0B,YAAYF,MAAMI,GAAG;QAC/C,MAAMC,aAAa5B,MAAM0B,MAAM,CAAuB,CAAC;QAEvD,MAAMjB,eAAeZ;QACrB,MAAMgC,kBAAkB3B;QAExB,MAAM4B,gBAAgBlC,iBAAiB;YACrCuB,2BAAAA,qCAAAA,eAAiB,MAAM;gBAAEY,WAAW;YAAQ;QAC9C;QACA,MAAMC,eAAepC,iBAAiB;YACpCuB,2BAAAA,qCAAAA,eAAiB,MAAM;gBAAEY,WAAW;YAAO;YAE3C,IAAIX,eAAe;gBACjBE,WAAW;gBACXP,wBAAAA,kCAAAA,YAAakB,MAAM;YACrB;QACF;QAEAnC,0BAA0B;YACxB8B,WAAWM,OAAO,GAAG;gBAAE1B;YAAO;QAChC;QAEAV,0BACE;YACE,MAAMqC,UAAUV,WAAWS,OAAO;YAElC,IAAI,CAACC,WAAW5B,oBAAoBqB,WAAWM,OAAO,CAAC1B,MAAM,EAAEC,cAAcC,UAAU;gBACrF;YACF;YAEA,MAAM0B,iBAAiB,OAAOxB,UAAU,aAAaA,MAAM;gBAAEuB;YAAQ,KAAKvB;YAC1E,MAAMyB,QAAQ3B,UAAU0B,eAAeE,KAAK,GAAGF,eAAeG,IAAI;YAElE,MAAMC,SAASnC,aAAa8B,SAASE,OAAO;gBAAER,iBAAiBA;YAAkB;YAEjF,IAAI,CAACnB,WAAWD,cAAc;gBAC5B,YAAY;gBACZ,mGAAmG;gBACnG+B,OAAOC,MAAM;gBACb;YACF;YAEAjB,UAAUU,OAAO,GAAGM;YACpBA,OAAOE,QAAQ,GAAGhC,UAAUoB,gBAAgBE;YAE5C,OAAO;gBACLQ,OAAOG,MAAM;YACf;QACF,GACA,kGAAkG;QAClG,uDAAuD;QACvD;YAACnB;YAAWK;YAAiBC;YAAeE;YAActB;SAAQ;QAGpE,IAAIW,SAAS;YACX,OAAOrB,MAAM4C,YAAY,CAACrB,OAAO;gBAAEI;YAAI;QACzC;QAEA,OAAO;IACT;IAEA,OAAOd;AACT"}
1
+ {"version":3,"sources":["createPresenceComponent.ts"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\nimport type { MotionParam, PresenceMotion, MotionImperativeRef, PresenceMotionFn } from '../types';\n\nexport type PresenceComponentProps = {\n /**\n * By default, the child component won't execute the \"enter\" motion when it initially mounts, regardless of the value\n * of \"visible\". If you desire this behavior, ensure both \"appear\" and \"visible\" are set to \"true\".\n */\n appear?: boolean;\n\n /** A React element that will be cloned and will have motion effects applied to it. */\n children: React.ReactElement;\n\n /** Provides imperative controls for the animation. */\n imperativeRef?: React.Ref<MotionImperativeRef | undefined>;\n\n /**\n * Callback that is called when the whole motion finishes.\n *\n * A motion definition can contain multiple animations and therefore multiple \"finish\" events. The callback is\n * triggered once all animations have finished with \"null\" instead of an event object to avoid ambiguity.\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- EventHandler<T> does not support \"null\"\n onMotionFinish?: (ev: null, data: { direction: 'enter' | 'exit' }) => 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: 'enter' | 'exit' }) => 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: 'enter' | 'exit' }) => 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\nfunction shouldSkipAnimation(appear: boolean | undefined, isFirstMount: boolean, visible: boolean | undefined) {\n return !appear && isFirstMount && !!visible;\n}\n\nexport function createPresenceComponent<MotionParams extends Record<string, MotionParam> = {}>(\n value: PresenceMotion | PresenceMotionFn<MotionParams>,\n) {\n const Presence: React.FC<PresenceComponentProps & MotionParams> = props => {\n const itemContext = React.useContext(PresenceGroupChildContext);\n const merged = { ...itemContext, ...props };\n\n const {\n appear,\n children,\n imperativeRef,\n onExit,\n onMotionFinish,\n onMotionStart,\n onMotionCancel,\n visible,\n unmountOnExit,\n ..._rest\n } = merged;\n const params = _rest as Exclude<typeof merged, PresenceComponentProps | typeof itemContext>;\n\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef<HTMLElement>();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef<{ appear?: boolean; params: MotionParams }>({ appear, params });\n\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n\n const handleMotionStart = useEventCallback((direction: 'enter' | 'exit') => {\n onMotionStart?.(null, { direction });\n });\n const handleMotionFinish = useEventCallback((direction: 'enter' | 'exit') => {\n onMotionFinish?.(null, { direction });\n\n if (direction === 'exit' && unmountOnExit) {\n setMounted(false);\n onExit?.();\n }\n });\n\n const handleMotionCancel = useEventCallback((direction: 'enter' | 'exit') => {\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 };\n });\n\n useIsomorphicLayoutEffect(\n () => {\n const element = elementRef.current;\n\n if (!element || shouldSkipAnimation(optionsRef.current.appear, isFirstMount, visible)) {\n return;\n }\n\n const presenceMotion = typeof value === 'function' ? value({ element, ...optionsRef.current.params }) : value;\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n\n const direction = visible ? 'enter' : 'exit';\n const forceFinishMotion = !visible && isFirstMount;\n\n if (!forceFinishMotion) {\n handleMotionStart(direction);\n }\n\n const handle = animateAtoms(element, atoms, { isReducedMotion: isReducedMotion() });\n\n if (forceFinishMotion) {\n // Heads up!\n // .finish() is used there to skip animation on first mount, but apply animation styles immediately\n handle.finish();\n return;\n }\n\n handleRef.current = handle;\n handle.setMotionEndCallbacks(\n () => handleMotionFinish(direction),\n () => handleMotionCancel(direction),\n );\n\n return () => {\n handle.cancel();\n };\n },\n // Excluding `isFirstMount` from deps to prevent re-triggering the animation on subsequent renders\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [handleRef, isReducedMotion, handleMotionFinish, handleMotionStart, handleMotionCancel, visible],\n );\n\n if (mounted) {\n return React.cloneElement(child, { ref });\n }\n\n return null;\n };\n\n return Presence;\n}\n"],"names":["useEventCallback","useFirstMount","useIsomorphicLayoutEffect","useMergedRefs","React","PresenceGroupChildContext","useIsReducedMotion","useMotionImperativeRef","useMountedState","animateAtoms","getChildElement","shouldSkipAnimation","appear","isFirstMount","visible","createPresenceComponent","value","Presence","props","itemContext","useContext","merged","children","imperativeRef","onExit","onMotionFinish","onMotionStart","onMotionCancel","unmountOnExit","_rest","params","mounted","setMounted","child","handleRef","elementRef","useRef","ref","optionsRef","isReducedMotion","handleMotionStart","direction","handleMotionFinish","handleMotionCancel","current","element","presenceMotion","atoms","enter","exit","forceFinishMotion","handle","finish","setMotionEndCallbacks","cancel","cloneElement"],"mappings":"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"}
package/lib/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["types.ts"],"sourcesContent":["export type AtomMotion = { keyframes: Keyframe[] } & KeyframeEffectOptions;\n\nexport type PresenceMotion = {\n enter: AtomMotion | AtomMotion[];\n exit: AtomMotion | AtomMotion[];\n};\n\nexport type AtomMotionFn = (params: { element: HTMLElement }) => AtomMotion | AtomMotion[];\nexport type PresenceMotionFn = (params: { element: HTMLElement }) => PresenceMotion;\n\n// ---\n\nexport type AnimationHandle = Pick<Animation, 'cancel' | 'finish' | 'pause' | 'play' | 'playbackRate'> & {\n onfinish: () => void;\n};\n\nexport type MotionImperativeRef = {\n /** Sets the playback rate of the animation, where 1 is normal speed. */\n setPlaybackRate: (rate: number) => void;\n\n /** Sets the state of the animation to running or paused. */\n setPlayState: (state: 'running' | 'paused') => void;\n};\n"],"names":[],"mappings":"AAAA,WAsBE"}
1
+ {"version":3,"sources":["types.ts"],"sourcesContent":["export type AtomMotion = { keyframes: Keyframe[] } & KeyframeEffectOptions;\n\nexport type PresenceMotion = {\n enter: AtomMotion | AtomMotion[];\n exit: AtomMotion | AtomMotion[];\n};\n\n/**\n * @internal\n *\n * A motion param should be a primitive value that can be serialized to JSON and could be potentially used a plain\n * dependency for React hooks.\n */\nexport type MotionParam = boolean | number | string;\n\nexport type AtomMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (\n params: { element: HTMLElement } & MotionParams,\n) => AtomMotion | AtomMotion[];\nexport type PresenceMotionFn<MotionParams extends Record<string, MotionParam> = {}> = (\n params: { element: HTMLElement } & MotionParams,\n) => PresenceMotion;\n\n// ---\n\nexport type AnimationHandle = Pick<Animation, 'cancel' | 'finish' | 'pause' | 'play' | 'playbackRate'> & {\n setMotionEndCallbacks: (onfinish: () => void, oncancel: () => void) => void;\n};\n\nexport type MotionImperativeRef = {\n /** Sets the playback rate of the animation, where 1 is normal speed. */\n setPlaybackRate: (rate: number) => void;\n\n /** Sets the state of the animation to running or paused. */\n setPlayState: (state: 'running' | 'paused') => void;\n};\n"],"names":[],"mappings":"AAAA,WAkCE"}
@@ -25,14 +25,27 @@ export function animateAtoms(element, value, options) {
25
25
  animation.playbackRate = rate;
26
26
  });
27
27
  },
28
- set onfinish (callback){
28
+ setMotionEndCallbacks (onfinish, oncancel) {
29
+ // Heads up!
30
+ // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. This no-op is
31
+ // necessary to avoid errors in tests.
32
+ //
33
+ // See https://github.com/microsoft/fluentui/issues/31593
34
+ // See https://github.com/jsdom/jsdom/issues/3429
35
+ if (process.env.NODE_ENV === 'test') {
36
+ if (animations.length === 0) {
37
+ onfinish();
38
+ return;
39
+ }
40
+ }
29
41
  Promise.all(animations.map((animation)=>animation.finished)).then(()=>{
30
- callback();
42
+ onfinish();
31
43
  }).catch((err)=>{
32
44
  var _element_ownerDocument_defaultView;
33
45
  const DOMException = (_element_ownerDocument_defaultView = element.ownerDocument.defaultView) === null || _element_ownerDocument_defaultView === void 0 ? void 0 : _element_ownerDocument_defaultView.DOMException;
34
46
  // Ignores "DOMException: The user aborted a request" that appears if animations are cancelled
35
- if (DOMException && err instanceof DOMException) {
47
+ if (DOMException && err instanceof DOMException && err.name === 'AbortError') {
48
+ oncancel();
36
49
  return;
37
50
  }
38
51
  throw err;
@@ -1 +1 @@
1
- {"version":3,"sources":["animateAtoms.ts"],"sourcesContent":["import type { AnimationHandle, AtomMotion } from '../types';\n\nexport function animateAtoms(\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n): AnimationHandle {\n // Heads up!\n // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. The same is true for\n // older browsers that are out of browser support matrix. In these cases, the animation will be a no-op.\n const SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n\n const atoms = Array.isArray(value) ? value : [value];\n const { isReducedMotion } = options;\n\n const animations = SUPPORTS_WEB_ANIMATIONS\n ? atoms.map(motion => {\n const { keyframes, ...params } = motion;\n const animation = element.animate(keyframes, {\n fill: 'forwards',\n\n ...params,\n ...(isReducedMotion && { duration: 1 }),\n });\n\n animation.persist();\n\n return animation;\n })\n : [];\n\n return {\n set playbackRate(rate: number) {\n animations.forEach(animation => {\n animation.playbackRate = rate;\n });\n },\n set onfinish(callback: () => void) {\n Promise.all(animations.map(animation => animation.finished))\n .then(() => {\n callback();\n })\n .catch((err: unknown) => {\n const DOMException = element.ownerDocument.defaultView?.DOMException;\n\n // Ignores \"DOMException: The user aborted a request\" that appears if animations are cancelled\n if (DOMException && err instanceof DOMException) {\n return;\n }\n\n throw err;\n });\n },\n\n cancel: () => {\n animations.forEach(animation => {\n animation.cancel();\n });\n },\n pause: () => {\n animations.forEach(animation => {\n animation.pause();\n });\n },\n play: () => {\n animations.forEach(animation => {\n animation.play();\n });\n },\n finish: () => {\n animations.forEach(animation => {\n animation.finish();\n });\n },\n };\n}\n"],"names":["animateAtoms","element","value","options","SUPPORTS_WEB_ANIMATIONS","animate","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","params","animation","fill","duration","persist","playbackRate","rate","forEach","onfinish","callback","Promise","all","finished","then","catch","err","DOMException","ownerDocument","defaultView","cancel","pause","play","finish"],"mappings":"AAEA,OAAO,SAASA,aACdC,OAAoB,EACpBC,KAAgC,EAChCC,OAEC;IAED,YAAY;IACZ,iHAAiH;IACjH,wGAAwG;IACxG,MAAMC,0BAA0B,OAAOH,QAAQI,OAAO,KAAK;IAE3D,MAAMC,QAAQC,MAAMC,OAAO,CAACN,SAASA,QAAQ;QAACA;KAAM;IACpD,MAAM,EAAEO,eAAe,EAAE,GAAGN;IAE5B,MAAMO,aAAaN,0BACfE,MAAMK,GAAG,CAACC,CAAAA;QACR,MAAM,EAAEC,SAAS,EAAE,GAAGC,QAAQ,GAAGF;QACjC,MAAMG,YAAYd,QAAQI,OAAO,CAACQ,WAAW;YAC3CG,MAAM;YAEN,GAAGF,MAAM;YACT,GAAIL,mBAAmB;gBAAEQ,UAAU;YAAE,CAAC;QACxC;QAEAF,UAAUG,OAAO;QAEjB,OAAOH;IACT,KACA,EAAE;IAEN,OAAO;QACL,IAAII,cAAaC,KAAc;YAC7BV,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUI,YAAY,GAAGC;YAC3B;QACF;QACA,IAAIE,UAASC,SAAsB;YACjCC,QAAQC,GAAG,CAACf,WAAWC,GAAG,CAACI,CAAAA,YAAaA,UAAUW,QAAQ,GACvDC,IAAI,CAAC;gBACJJ;YACF,GACCK,KAAK,CAAC,CAACC;oBACe5B;gBAArB,MAAM6B,gBAAe7B,qCAAAA,QAAQ8B,aAAa,CAACC,WAAW,cAAjC/B,yDAAAA,mCAAmC6B,YAAY;gBAEpE,8FAA8F;gBAC9F,IAAIA,gBAAgBD,eAAeC,cAAc;oBAC/C;gBACF;gBAEA,MAAMD;YACR;QACJ;QAEAI,QAAQ;YACNvB,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUkB,MAAM;YAClB;QACF;QACAC,OAAO;YACLxB,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUmB,KAAK;YACjB;QACF;QACAC,MAAM;YACJzB,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUoB,IAAI;YAChB;QACF;QACAC,QAAQ;YACN1B,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUqB,MAAM;YAClB;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["animateAtoms.ts"],"sourcesContent":["import type { AnimationHandle, AtomMotion } from '../types';\n\nexport function animateAtoms(\n element: HTMLElement,\n value: AtomMotion | AtomMotion[],\n options: {\n isReducedMotion: boolean;\n },\n): AnimationHandle {\n // Heads up!\n // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. The same is true for\n // older browsers that are out of browser support matrix. In these cases, the animation will be a no-op.\n const SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n\n const atoms = Array.isArray(value) ? value : [value];\n const { isReducedMotion } = options;\n\n const animations = SUPPORTS_WEB_ANIMATIONS\n ? atoms.map(motion => {\n const { keyframes, ...params } = motion;\n const animation = element.animate(keyframes, {\n fill: 'forwards',\n\n ...params,\n ...(isReducedMotion && { duration: 1 }),\n });\n\n animation.persist();\n\n return animation;\n })\n : [];\n\n return {\n set playbackRate(rate: number) {\n animations.forEach(animation => {\n animation.playbackRate = rate;\n });\n },\n setMotionEndCallbacks(onfinish: () => void, oncancel: () => void) {\n // Heads up!\n // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. This no-op is\n // necessary to avoid errors in tests.\n //\n // See https://github.com/microsoft/fluentui/issues/31593\n // See https://github.com/jsdom/jsdom/issues/3429\n if (process.env.NODE_ENV === 'test') {\n if (animations.length === 0) {\n onfinish();\n return;\n }\n }\n\n Promise.all(animations.map(animation => animation.finished))\n .then(() => {\n onfinish();\n })\n .catch((err: unknown) => {\n const DOMException = element.ownerDocument.defaultView?.DOMException;\n\n // Ignores \"DOMException: The user aborted a request\" that appears if animations are cancelled\n if (DOMException && err instanceof DOMException && err.name === 'AbortError') {\n oncancel();\n return;\n }\n\n throw err;\n });\n },\n\n cancel: () => {\n animations.forEach(animation => {\n animation.cancel();\n });\n },\n pause: () => {\n animations.forEach(animation => {\n animation.pause();\n });\n },\n play: () => {\n animations.forEach(animation => {\n animation.play();\n });\n },\n finish: () => {\n animations.forEach(animation => {\n animation.finish();\n });\n },\n };\n}\n"],"names":["animateAtoms","element","value","options","SUPPORTS_WEB_ANIMATIONS","animate","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","params","animation","fill","duration","persist","playbackRate","rate","forEach","setMotionEndCallbacks","onfinish","oncancel","process","env","NODE_ENV","length","Promise","all","finished","then","catch","err","DOMException","ownerDocument","defaultView","name","cancel","pause","play","finish"],"mappings":"AAEA,OAAO,SAASA,aACdC,OAAoB,EACpBC,KAAgC,EAChCC,OAEC;IAED,YAAY;IACZ,iHAAiH;IACjH,wGAAwG;IACxG,MAAMC,0BAA0B,OAAOH,QAAQI,OAAO,KAAK;IAE3D,MAAMC,QAAQC,MAAMC,OAAO,CAACN,SAASA,QAAQ;QAACA;KAAM;IACpD,MAAM,EAAEO,eAAe,EAAE,GAAGN;IAE5B,MAAMO,aAAaN,0BACfE,MAAMK,GAAG,CAACC,CAAAA;QACR,MAAM,EAAEC,SAAS,EAAE,GAAGC,QAAQ,GAAGF;QACjC,MAAMG,YAAYd,QAAQI,OAAO,CAACQ,WAAW;YAC3CG,MAAM;YAEN,GAAGF,MAAM;YACT,GAAIL,mBAAmB;gBAAEQ,UAAU;YAAE,CAAC;QACxC;QAEAF,UAAUG,OAAO;QAEjB,OAAOH;IACT,KACA,EAAE;IAEN,OAAO;QACL,IAAII,cAAaC,KAAc;YAC7BV,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUI,YAAY,GAAGC;YAC3B;QACF;QACAE,uBAAsBC,QAAoB,EAAEC,QAAoB;YAC9D,YAAY;YACZ,0GAA0G;YAC1G,sCAAsC;YACtC,EAAE;YACF,yDAAyD;YACzD,iDAAiD;YACjD,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,QAAQ;gBACnC,IAAIjB,WAAWkB,MAAM,KAAK,GAAG;oBAC3BL;oBACA;gBACF;YACF;YAEAM,QAAQC,GAAG,CAACpB,WAAWC,GAAG,CAACI,CAAAA,YAAaA,UAAUgB,QAAQ,GACvDC,IAAI,CAAC;gBACJT;YACF,GACCU,KAAK,CAAC,CAACC;oBACejC;gBAArB,MAAMkC,gBAAelC,qCAAAA,QAAQmC,aAAa,CAACC,WAAW,cAAjCpC,yDAAAA,mCAAmCkC,YAAY;gBAEpE,8FAA8F;gBAC9F,IAAIA,gBAAgBD,eAAeC,gBAAgBD,IAAII,IAAI,KAAK,cAAc;oBAC5Ed;oBACA;gBACF;gBAEA,MAAMU;YACR;QACJ;QAEAK,QAAQ;YACN7B,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUwB,MAAM;YAClB;QACF;QACAC,OAAO;YACL9B,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAUyB,KAAK;YACjB;QACF;QACAC,MAAM;YACJ/B,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAU0B,IAAI;YAChB;QACF;QACAC,QAAQ;YACNhC,WAAWW,OAAO,CAACN,CAAAA;gBACjBA,UAAU2B,MAAM;YAClB;QACF;IACF;AACF"}
@@ -17,20 +17,39 @@ const _animateAtoms = require("../utils/animateAtoms");
17
17
  const _getChildElement = require("../utils/getChildElement");
18
18
  function createMotionComponent(value) {
19
19
  const Atom = (props)=>{
20
- const { children, imperativeRef } = props;
20
+ const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, ..._rest } = props;
21
+ const params = _rest;
21
22
  const child = (0, _getChildElement.getChildElement)(children);
22
23
  const handleRef = (0, _useMotionImperativeRef.useMotionImperativeRef)(imperativeRef);
23
24
  const elementRef = _react.useRef();
25
+ const paramsRef = _react.useRef(params);
24
26
  const isReducedMotion = (0, _useIsReducedMotion.useIsReducedMotion)();
27
+ const onMotionStart = (0, _reactutilities.useEventCallback)(()=>{
28
+ onMotionStartProp === null || onMotionStartProp === void 0 ? void 0 : onMotionStartProp(null);
29
+ });
30
+ const onMotionFinish = (0, _reactutilities.useEventCallback)(()=>{
31
+ onMotionFinishProp === null || onMotionFinishProp === void 0 ? void 0 : onMotionFinishProp(null);
32
+ });
33
+ const onMotionCancel = (0, _reactutilities.useEventCallback)(()=>{
34
+ onMotionCancelProp === null || onMotionCancelProp === void 0 ? void 0 : onMotionCancelProp(null);
35
+ });
36
+ (0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
37
+ // Heads up!
38
+ // We store the params in a ref to avoid re-rendering the component when the params change.
39
+ paramsRef.current = params;
40
+ });
25
41
  (0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
26
42
  const element = elementRef.current;
27
43
  if (element) {
28
44
  const atoms = typeof value === 'function' ? value({
29
- element
45
+ element,
46
+ ...paramsRef.current
30
47
  }) : value;
48
+ onMotionStart();
31
49
  const handle = (0, _animateAtoms.animateAtoms)(element, atoms, {
32
50
  isReducedMotion: isReducedMotion()
33
51
  });
52
+ handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);
34
53
  handleRef.current = handle;
35
54
  return ()=>{
36
55
  handle.cancel();
@@ -38,7 +57,10 @@ function createMotionComponent(value) {
38
57
  }
39
58
  }, [
40
59
  handleRef,
41
- isReducedMotion
60
+ isReducedMotion,
61
+ onMotionFinish,
62
+ onMotionStart,
63
+ onMotionCancel
42
64
  ]);
43
65
  return /*#__PURE__*/ _react.cloneElement(children, {
44
66
  ref: (0, _reactutilities.useMergedRefs)(elementRef, child.ref)
@@ -1 +1 @@
1
- {"version":3,"sources":["createMotionComponent.js"],"sourcesContent":["import { useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\n/**\n * Creates a component that will animate the children using the provided motion.\n *\n * @param value - A motion definition.\n */ export function createMotionComponent(value) {\n const Atom = (props)=>{\n const { children, imperativeRef } = props;\n const child = getChildElement(children);\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef();\n const isReducedMotion = useIsReducedMotion();\n useIsomorphicLayoutEffect(()=>{\n const element = elementRef.current;\n if (element) {\n const atoms = typeof value === 'function' ? value({\n element\n }) : value;\n const handle = animateAtoms(element, atoms, {\n isReducedMotion: isReducedMotion()\n });\n handleRef.current = handle;\n return ()=>{\n handle.cancel();\n };\n }\n }, [\n handleRef,\n isReducedMotion\n ]);\n return React.cloneElement(children, {\n ref: useMergedRefs(elementRef, child.ref)\n });\n };\n return Atom;\n}\n"],"names":["createMotionComponent","value","Atom","props","children","imperativeRef","child","getChildElement","handleRef","useMotionImperativeRef","elementRef","React","useRef","isReducedMotion","useIsReducedMotion","useIsomorphicLayoutEffect","element","current","atoms","handle","animateAtoms","cancel","cloneElement","ref","useMergedRefs"],"mappings":";;;;+BAUoBA;;;eAAAA;;;;gCAVqC;iEAClC;oCACY;wCACI;8BACV;iCACG;AAKrB,SAASA,sBAAsBC,KAAK;IAC3C,MAAMC,OAAO,CAACC;QACV,MAAM,EAAEC,QAAQ,EAAEC,aAAa,EAAE,GAAGF;QACpC,MAAMG,QAAQC,IAAAA,gCAAe,EAACH;QAC9B,MAAMI,YAAYC,IAAAA,8CAAsB,EAACJ;QACzC,MAAMK,aAAaC,OAAMC,MAAM;QAC/B,MAAMC,kBAAkBC,IAAAA,sCAAkB;QAC1CC,IAAAA,yCAAyB,EAAC;YACtB,MAAMC,UAAUN,WAAWO,OAAO;YAClC,IAAID,SAAS;gBACT,MAAME,QAAQ,OAAOjB,UAAU,aAAaA,MAAM;oBAC9Ce;gBACJ,KAAKf;gBACL,MAAMkB,SAASC,IAAAA,0BAAY,EAACJ,SAASE,OAAO;oBACxCL,iBAAiBA;gBACrB;gBACAL,UAAUS,OAAO,GAAGE;gBACpB,OAAO;oBACHA,OAAOE,MAAM;gBACjB;YACJ;QACJ,GAAG;YACCb;YACAK;SACH;QACD,qBAAOF,OAAMW,YAAY,CAAClB,UAAU;YAChCmB,KAAKC,IAAAA,6BAAa,EAACd,YAAYJ,MAAMiB,GAAG;QAC5C;IACJ;IACA,OAAOrB;AACX"}
1
+ {"version":3,"sources":["createMotionComponent.js"],"sourcesContent":["import { useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\n/**\n * Creates a component that will animate the children using the provided motion.\n *\n * @param value - A motion definition.\n */ export function createMotionComponent(value) {\n const Atom = (props)=>{\n const { children, imperativeRef, onMotionFinish: onMotionFinishProp, onMotionStart: onMotionStartProp, onMotionCancel: onMotionCancelProp, ..._rest } = props;\n const params = _rest;\n const child = getChildElement(children);\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef();\n const paramsRef = React.useRef(params);\n const isReducedMotion = useIsReducedMotion();\n const onMotionStart = useEventCallback(()=>{\n onMotionStartProp === null || onMotionStartProp === void 0 ? void 0 : onMotionStartProp(null);\n });\n const onMotionFinish = useEventCallback(()=>{\n onMotionFinishProp === null || onMotionFinishProp === void 0 ? void 0 : onMotionFinishProp(null);\n });\n const onMotionCancel = useEventCallback(()=>{\n onMotionCancelProp === null || onMotionCancelProp === void 0 ? void 0 : onMotionCancelProp(null);\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 paramsRef.current = params;\n });\n useIsomorphicLayoutEffect(()=>{\n const element = elementRef.current;\n if (element) {\n const atoms = typeof value === 'function' ? value({\n element,\n ...paramsRef.current\n }) : value;\n onMotionStart();\n const handle = animateAtoms(element, atoms, {\n isReducedMotion: isReducedMotion()\n });\n handle.setMotionEndCallbacks(onMotionFinish, onMotionCancel);\n handleRef.current = handle;\n return ()=>{\n handle.cancel();\n };\n }\n }, [\n handleRef,\n isReducedMotion,\n onMotionFinish,\n onMotionStart,\n onMotionCancel\n ]);\n return React.cloneElement(children, {\n ref: useMergedRefs(elementRef, child.ref)\n });\n };\n return Atom;\n}\n"],"names":["createMotionComponent","value","Atom","props","children","imperativeRef","onMotionFinish","onMotionFinishProp","onMotionStart","onMotionStartProp","onMotionCancel","onMotionCancelProp","_rest","params","child","getChildElement","handleRef","useMotionImperativeRef","elementRef","React","useRef","paramsRef","isReducedMotion","useIsReducedMotion","useEventCallback","useIsomorphicLayoutEffect","current","element","atoms","handle","animateAtoms","setMotionEndCallbacks","cancel","cloneElement","ref","useMergedRefs"],"mappings":";;;;+BAUoBA;;;eAAAA;;;;gCAVuD;iEACpD;oCACY;wCACI;8BACV;iCACG;AAKrB,SAASA,sBAAsBC,KAAK;IAC3C,MAAMC,OAAO,CAACC;QACV,MAAM,EAAEC,QAAQ,EAAEC,aAAa,EAAEC,gBAAgBC,kBAAkB,EAAEC,eAAeC,iBAAiB,EAAEC,gBAAgBC,kBAAkB,EAAE,GAAGC,OAAO,GAAGT;QACxJ,MAAMU,SAASD;QACf,MAAME,QAAQC,IAAAA,gCAAe,EAACX;QAC9B,MAAMY,YAAYC,IAAAA,8CAAsB,EAACZ;QACzC,MAAMa,aAAaC,OAAMC,MAAM;QAC/B,MAAMC,YAAYF,OAAMC,MAAM,CAACP;QAC/B,MAAMS,kBAAkBC,IAAAA,sCAAkB;QAC1C,MAAMf,gBAAgBgB,IAAAA,gCAAgB,EAAC;YACnCf,sBAAsB,QAAQA,sBAAsB,KAAK,IAAI,KAAK,IAAIA,kBAAkB;QAC5F;QACA,MAAMH,iBAAiBkB,IAAAA,gCAAgB,EAAC;YACpCjB,uBAAuB,QAAQA,uBAAuB,KAAK,IAAI,KAAK,IAAIA,mBAAmB;QAC/F;QACA,MAAMG,iBAAiBc,IAAAA,gCAAgB,EAAC;YACpCb,uBAAuB,QAAQA,uBAAuB,KAAK,IAAI,KAAK,IAAIA,mBAAmB;QAC/F;QACAc,IAAAA,yCAAyB,EAAC;YACtB,YAAY;YACZ,2FAA2F;YAC3FJ,UAAUK,OAAO,GAAGb;QACxB;QACAY,IAAAA,yCAAyB,EAAC;YACtB,MAAME,UAAUT,WAAWQ,OAAO;YAClC,IAAIC,SAAS;gBACT,MAAMC,QAAQ,OAAO3B,UAAU,aAAaA,MAAM;oBAC9C0B;oBACA,GAAGN,UAAUK,OAAO;gBACxB,KAAKzB;gBACLO;gBACA,MAAMqB,SAASC,IAAAA,0BAAY,EAACH,SAASC,OAAO;oBACxCN,iBAAiBA;gBACrB;gBACAO,OAAOE,qBAAqB,CAACzB,gBAAgBI;gBAC7CM,UAAUU,OAAO,GAAGG;gBACpB,OAAO;oBACHA,OAAOG,MAAM;gBACjB;YACJ;QACJ,GAAG;YACChB;YACAM;YACAhB;YACAE;YACAE;SACH;QACD,qBAAOS,OAAMc,YAAY,CAAC7B,UAAU;YAChC8B,KAAKC,IAAAA,6BAAa,EAACjB,YAAYJ,MAAMoB,GAAG;QAC5C;IACJ;IACA,OAAOhC;AACX"}
@@ -18,40 +18,53 @@ const _useMountedState = require("../hooks/useMountedState");
18
18
  const _animateAtoms = require("../utils/animateAtoms");
19
19
  const _getChildElement = require("../utils/getChildElement");
20
20
  function shouldSkipAnimation(appear, isFirstMount, visible) {
21
- return !appear && isFirstMount && visible;
21
+ return !appear && isFirstMount && !!visible;
22
22
  }
23
23
  function createPresenceComponent(value) {
24
24
  const Presence = (props)=>{
25
25
  const itemContext = _react.useContext(_PresenceGroupChildContext.PresenceGroupChildContext);
26
- const { appear, children, imperativeRef, onMotionFinish, visible, unmountOnExit } = {
26
+ const merged = {
27
27
  ...itemContext,
28
28
  ...props
29
29
  };
30
+ const { appear, children, imperativeRef, onExit, onMotionFinish, onMotionStart, onMotionCancel, visible, unmountOnExit, ..._rest } = merged;
31
+ const params = _rest;
30
32
  const [mounted, setMounted] = (0, _useMountedState.useMountedState)(visible, unmountOnExit);
31
33
  const child = (0, _getChildElement.getChildElement)(children);
32
34
  const handleRef = (0, _useMotionImperativeRef.useMotionImperativeRef)(imperativeRef);
33
35
  const elementRef = _react.useRef();
34
36
  const ref = (0, _reactutilities.useMergedRefs)(elementRef, child.ref);
35
- const optionsRef = _react.useRef({});
37
+ const optionsRef = _react.useRef({
38
+ appear,
39
+ params
40
+ });
36
41
  const isFirstMount = (0, _reactutilities.useFirstMount)();
37
42
  const isReducedMotion = (0, _useIsReducedMotion.useIsReducedMotion)();
38
- const onEnterFinish = (0, _reactutilities.useEventCallback)(()=>{
39
- onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
40
- direction: 'enter'
43
+ const handleMotionStart = (0, _reactutilities.useEventCallback)((direction)=>{
44
+ onMotionStart === null || onMotionStart === void 0 ? void 0 : onMotionStart(null, {
45
+ direction
41
46
  });
42
47
  });
43
- const onExitFinish = (0, _reactutilities.useEventCallback)(()=>{
48
+ const handleMotionFinish = (0, _reactutilities.useEventCallback)((direction)=>{
44
49
  onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {
45
- direction: 'exit'
50
+ direction
46
51
  });
47
- if (unmountOnExit) {
52
+ if (direction === 'exit' && unmountOnExit) {
48
53
  setMounted(false);
49
- itemContext === null || itemContext === void 0 ? void 0 : itemContext.onExit();
54
+ onExit === null || onExit === void 0 ? void 0 : onExit();
50
55
  }
51
56
  });
57
+ const handleMotionCancel = (0, _reactutilities.useEventCallback)((direction)=>{
58
+ onMotionCancel === null || onMotionCancel === void 0 ? void 0 : onMotionCancel(null, {
59
+ direction
60
+ });
61
+ });
52
62
  (0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
63
+ // Heads up!
64
+ // We store the params in a ref to avoid re-rendering the component when the params change.
53
65
  optionsRef.current = {
54
- appear
66
+ appear,
67
+ params
55
68
  };
56
69
  });
57
70
  (0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
@@ -60,20 +73,26 @@ function createPresenceComponent(value) {
60
73
  return;
61
74
  }
62
75
  const presenceMotion = typeof value === 'function' ? value({
63
- element
76
+ element,
77
+ ...optionsRef.current.params
64
78
  }) : value;
65
79
  const atoms = visible ? presenceMotion.enter : presenceMotion.exit;
80
+ const direction = visible ? 'enter' : 'exit';
81
+ const forceFinishMotion = !visible && isFirstMount;
82
+ if (!forceFinishMotion) {
83
+ handleMotionStart(direction);
84
+ }
66
85
  const handle = (0, _animateAtoms.animateAtoms)(element, atoms, {
67
86
  isReducedMotion: isReducedMotion()
68
87
  });
69
- if (!visible && isFirstMount) {
88
+ if (forceFinishMotion) {
70
89
  // Heads up!
71
90
  // .finish() is used there to skip animation on first mount, but apply animation styles immediately
72
91
  handle.finish();
73
92
  return;
74
93
  }
75
94
  handleRef.current = handle;
76
- handle.onfinish = visible ? onEnterFinish : onExitFinish;
95
+ handle.setMotionEndCallbacks(()=>handleMotionFinish(direction), ()=>handleMotionCancel(direction));
77
96
  return ()=>{
78
97
  handle.cancel();
79
98
  };
@@ -81,8 +100,9 @@ function createPresenceComponent(value) {
81
100
  [
82
101
  handleRef,
83
102
  isReducedMotion,
84
- onEnterFinish,
85
- onExitFinish,
103
+ handleMotionFinish,
104
+ handleMotionStart,
105
+ handleMotionCancel,
86
106
  visible
87
107
  ]);
88
108
  if (mounted) {
@@ -1 +1 @@
1
- {"version":3,"sources":["createPresenceComponent.js"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\nfunction shouldSkipAnimation(appear, isFirstMount, visible) {\n return !appear && isFirstMount && visible;\n}\nexport function createPresenceComponent(value) {\n const Presence = (props)=>{\n const itemContext = React.useContext(PresenceGroupChildContext);\n const { appear, children, imperativeRef, onMotionFinish, visible, unmountOnExit } = {\n ...itemContext,\n ...props\n };\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef({});\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n const onEnterFinish = useEventCallback(()=>{\n onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {\n direction: 'enter'\n });\n });\n const onExitFinish = useEventCallback(()=>{\n onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {\n direction: 'exit'\n });\n if (unmountOnExit) {\n setMounted(false);\n itemContext === null || itemContext === void 0 ? void 0 : itemContext.onExit();\n }\n });\n useIsomorphicLayoutEffect(()=>{\n optionsRef.current = {\n appear\n };\n });\n useIsomorphicLayoutEffect(()=>{\n const element = elementRef.current;\n if (!element || shouldSkipAnimation(optionsRef.current.appear, isFirstMount, visible)) {\n return;\n }\n const presenceMotion = typeof value === 'function' ? value({\n element\n }) : value;\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n const handle = animateAtoms(element, atoms, {\n isReducedMotion: isReducedMotion()\n });\n if (!visible && isFirstMount) {\n // Heads up!\n // .finish() is used there to skip animation on first mount, but apply animation styles immediately\n handle.finish();\n return;\n }\n handleRef.current = handle;\n handle.onfinish = visible ? onEnterFinish : onExitFinish;\n return ()=>{\n handle.cancel();\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 handleRef,\n isReducedMotion,\n onEnterFinish,\n onExitFinish,\n visible\n ]);\n if (mounted) {\n return React.cloneElement(child, {\n ref\n });\n }\n return null;\n };\n return Presence;\n}\n"],"names":["createPresenceComponent","shouldSkipAnimation","appear","isFirstMount","visible","value","Presence","props","itemContext","React","useContext","PresenceGroupChildContext","children","imperativeRef","onMotionFinish","unmountOnExit","mounted","setMounted","useMountedState","child","getChildElement","handleRef","useMotionImperativeRef","elementRef","useRef","ref","useMergedRefs","optionsRef","useFirstMount","isReducedMotion","useIsReducedMotion","onEnterFinish","useEventCallback","direction","onExitFinish","onExit","useIsomorphicLayoutEffect","current","element","presenceMotion","atoms","enter","exit","handle","animateAtoms","finish","onfinish","cancel","cloneElement"],"mappings":";;;;+BAWgBA;;;eAAAA;;;;gCAX0E;iEACnE;2CACmB;oCACP;wCACI;iCACP;8BACH;iCACG;AAChC,SAASC,oBAAoBC,MAAM,EAAEC,YAAY,EAAEC,OAAO;IACtD,OAAO,CAACF,UAAUC,gBAAgBC;AACtC;AACO,SAASJ,wBAAwBK,KAAK;IACzC,MAAMC,WAAW,CAACC;QACd,MAAMC,cAAcC,OAAMC,UAAU,CAACC,oDAAyB;QAC9D,MAAM,EAAET,MAAM,EAAEU,QAAQ,EAAEC,aAAa,EAAEC,cAAc,EAAEV,OAAO,EAAEW,aAAa,EAAE,GAAG;YAChF,GAAGP,WAAW;YACd,GAAGD,KAAK;QACZ;QACA,MAAM,CAACS,SAASC,WAAW,GAAGC,IAAAA,gCAAe,EAACd,SAASW;QACvD,MAAMI,QAAQC,IAAAA,gCAAe,EAACR;QAC9B,MAAMS,YAAYC,IAAAA,8CAAsB,EAACT;QACzC,MAAMU,aAAad,OAAMe,MAAM;QAC/B,MAAMC,MAAMC,IAAAA,6BAAa,EAACH,YAAYJ,MAAMM,GAAG;QAC/C,MAAME,aAAalB,OAAMe,MAAM,CAAC,CAAC;QACjC,MAAMrB,eAAeyB,IAAAA,6BAAa;QAClC,MAAMC,kBAAkBC,IAAAA,sCAAkB;QAC1C,MAAMC,gBAAgBC,IAAAA,gCAAgB,EAAC;YACnClB,mBAAmB,QAAQA,mBAAmB,KAAK,IAAI,KAAK,IAAIA,eAAe,MAAM;gBACjFmB,WAAW;YACf;QACJ;QACA,MAAMC,eAAeF,IAAAA,gCAAgB,EAAC;YAClClB,mBAAmB,QAAQA,mBAAmB,KAAK,IAAI,KAAK,IAAIA,eAAe,MAAM;gBACjFmB,WAAW;YACf;YACA,IAAIlB,eAAe;gBACfE,WAAW;gBACXT,gBAAgB,QAAQA,gBAAgB,KAAK,IAAI,KAAK,IAAIA,YAAY2B,MAAM;YAChF;QACJ;QACAC,IAAAA,yCAAyB,EAAC;YACtBT,WAAWU,OAAO,GAAG;gBACjBnC;YACJ;QACJ;QACAkC,IAAAA,yCAAyB,EAAC;YACtB,MAAME,UAAUf,WAAWc,OAAO;YAClC,IAAI,CAACC,WAAWrC,oBAAoB0B,WAAWU,OAAO,CAACnC,MAAM,EAAEC,cAAcC,UAAU;gBACnF;YACJ;YACA,MAAMmC,iBAAiB,OAAOlC,UAAU,aAAaA,MAAM;gBACvDiC;YACJ,KAAKjC;YACL,MAAMmC,QAAQpC,UAAUmC,eAAeE,KAAK,GAAGF,eAAeG,IAAI;YAClE,MAAMC,SAASC,IAAAA,0BAAY,EAACN,SAASE,OAAO;gBACxCX,iBAAiBA;YACrB;YACA,IAAI,CAACzB,WAAWD,cAAc;gBAC1B,YAAY;gBACZ,mGAAmG;gBACnGwC,OAAOE,MAAM;gBACb;YACJ;YACAxB,UAAUgB,OAAO,GAAGM;YACpBA,OAAOG,QAAQ,GAAG1C,UAAU2B,gBAAgBG;YAC5C,OAAO;gBACHS,OAAOI,MAAM;YACjB;QACJ,GACA,uDAAuD;QACvD;YACI1B;YACAQ;YACAE;YACAG;YACA9B;SACH;QACD,IAAIY,SAAS;YACT,qBAAOP,OAAMuC,YAAY,CAAC7B,OAAO;gBAC7BM;YACJ;QACJ;QACA,OAAO;IACX;IACA,OAAOnB;AACX"}
1
+ {"version":3,"sources":["createPresenceComponent.js"],"sourcesContent":["import { useEventCallback, useFirstMount, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { PresenceGroupChildContext } from '../contexts/PresenceGroupChildContext';\nimport { useIsReducedMotion } from '../hooks/useIsReducedMotion';\nimport { useMotionImperativeRef } from '../hooks/useMotionImperativeRef';\nimport { useMountedState } from '../hooks/useMountedState';\nimport { animateAtoms } from '../utils/animateAtoms';\nimport { getChildElement } from '../utils/getChildElement';\nfunction shouldSkipAnimation(appear, isFirstMount, visible) {\n return !appear && isFirstMount && !!visible;\n}\nexport function createPresenceComponent(value) {\n const Presence = (props)=>{\n const itemContext = React.useContext(PresenceGroupChildContext);\n const merged = {\n ...itemContext,\n ...props\n };\n const { appear, children, imperativeRef, onExit, onMotionFinish, onMotionStart, onMotionCancel, visible, unmountOnExit, ..._rest } = merged;\n const params = _rest;\n const [mounted, setMounted] = useMountedState(visible, unmountOnExit);\n const child = getChildElement(children);\n const handleRef = useMotionImperativeRef(imperativeRef);\n const elementRef = React.useRef();\n const ref = useMergedRefs(elementRef, child.ref);\n const optionsRef = React.useRef({\n appear,\n params\n });\n const isFirstMount = useFirstMount();\n const isReducedMotion = useIsReducedMotion();\n const handleMotionStart = useEventCallback((direction)=>{\n onMotionStart === null || onMotionStart === void 0 ? void 0 : onMotionStart(null, {\n direction\n });\n });\n const handleMotionFinish = useEventCallback((direction)=>{\n onMotionFinish === null || onMotionFinish === void 0 ? void 0 : onMotionFinish(null, {\n direction\n });\n if (direction === 'exit' && unmountOnExit) {\n setMounted(false);\n onExit === null || onExit === void 0 ? void 0 : onExit();\n }\n });\n const handleMotionCancel = useEventCallback((direction)=>{\n onMotionCancel === null || onMotionCancel === void 0 ? void 0 : onMotionCancel(null, {\n 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 = {\n appear,\n params\n };\n });\n useIsomorphicLayoutEffect(()=>{\n const element = elementRef.current;\n if (!element || shouldSkipAnimation(optionsRef.current.appear, isFirstMount, visible)) {\n return;\n }\n const presenceMotion = typeof value === 'function' ? value({\n element,\n ...optionsRef.current.params\n }) : value;\n const atoms = visible ? presenceMotion.enter : presenceMotion.exit;\n const direction = visible ? 'enter' : 'exit';\n const forceFinishMotion = !visible && isFirstMount;\n if (!forceFinishMotion) {\n handleMotionStart(direction);\n }\n const handle = animateAtoms(element, atoms, {\n isReducedMotion: isReducedMotion()\n });\n if (forceFinishMotion) {\n // Heads up!\n // .finish() is used there to skip animation on first mount, but apply animation styles immediately\n handle.finish();\n return;\n }\n handleRef.current = handle;\n handle.setMotionEndCallbacks(()=>handleMotionFinish(direction), ()=>handleMotionCancel(direction));\n return ()=>{\n handle.cancel();\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 handleRef,\n isReducedMotion,\n handleMotionFinish,\n handleMotionStart,\n handleMotionCancel,\n visible\n ]);\n if (mounted) {\n return React.cloneElement(child, {\n ref\n });\n }\n return null;\n };\n return Presence;\n}\n"],"names":["createPresenceComponent","shouldSkipAnimation","appear","isFirstMount","visible","value","Presence","props","itemContext","React","useContext","PresenceGroupChildContext","merged","children","imperativeRef","onExit","onMotionFinish","onMotionStart","onMotionCancel","unmountOnExit","_rest","params","mounted","setMounted","useMountedState","child","getChildElement","handleRef","useMotionImperativeRef","elementRef","useRef","ref","useMergedRefs","optionsRef","useFirstMount","isReducedMotion","useIsReducedMotion","handleMotionStart","useEventCallback","direction","handleMotionFinish","handleMotionCancel","useIsomorphicLayoutEffect","current","element","presenceMotion","atoms","enter","exit","forceFinishMotion","handle","animateAtoms","finish","setMotionEndCallbacks","cancel","cloneElement"],"mappings":"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"}
@@ -35,14 +35,27 @@ function animateAtoms(element, value, options) {
35
35
  animation.playbackRate = rate;
36
36
  });
37
37
  },
38
- set onfinish (callback){
38
+ setMotionEndCallbacks (onfinish, oncancel) {
39
+ // Heads up!
40
+ // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. This no-op is
41
+ // necessary to avoid errors in tests.
42
+ //
43
+ // See https://github.com/microsoft/fluentui/issues/31593
44
+ // See https://github.com/jsdom/jsdom/issues/3429
45
+ if (process.env.NODE_ENV === 'test') {
46
+ if (animations.length === 0) {
47
+ onfinish();
48
+ return;
49
+ }
50
+ }
39
51
  Promise.all(animations.map((animation)=>animation.finished)).then(()=>{
40
- callback();
52
+ onfinish();
41
53
  }).catch((err)=>{
42
54
  var _element_ownerDocument_defaultView;
43
55
  const DOMException = (_element_ownerDocument_defaultView = element.ownerDocument.defaultView) === null || _element_ownerDocument_defaultView === void 0 ? void 0 : _element_ownerDocument_defaultView.DOMException;
44
56
  // Ignores "DOMException: The user aborted a request" that appears if animations are cancelled
45
- if (DOMException && err instanceof DOMException) {
57
+ if (DOMException && err instanceof DOMException && err.name === 'AbortError') {
58
+ oncancel();
46
59
  return;
47
60
  }
48
61
  throw err;
@@ -1 +1 @@
1
- {"version":3,"sources":["animateAtoms.js"],"sourcesContent":["export function animateAtoms(element, value, options) {\n // Heads up!\n // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. The same is true for\n // older browsers that are out of browser support matrix. In these cases, the animation will be a no-op.\n const SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n const atoms = Array.isArray(value) ? value : [\n value\n ];\n const { isReducedMotion } = options;\n const animations = SUPPORTS_WEB_ANIMATIONS ? atoms.map((motion)=>{\n const { keyframes, ...params } = motion;\n const animation = element.animate(keyframes, {\n fill: 'forwards',\n ...params,\n ...isReducedMotion && {\n duration: 1\n }\n });\n animation.persist();\n return animation;\n }) : [];\n return {\n set playbackRate (rate){\n animations.forEach((animation)=>{\n animation.playbackRate = rate;\n });\n },\n set onfinish (callback){\n Promise.all(animations.map((animation)=>animation.finished)).then(()=>{\n callback();\n }).catch((err)=>{\n var _element_ownerDocument_defaultView;\n const DOMException = (_element_ownerDocument_defaultView = element.ownerDocument.defaultView) === null || _element_ownerDocument_defaultView === void 0 ? void 0 : _element_ownerDocument_defaultView.DOMException;\n // Ignores \"DOMException: The user aborted a request\" that appears if animations are cancelled\n if (DOMException && err instanceof DOMException) {\n return;\n }\n throw err;\n });\n },\n cancel: ()=>{\n animations.forEach((animation)=>{\n animation.cancel();\n });\n },\n pause: ()=>{\n animations.forEach((animation)=>{\n animation.pause();\n });\n },\n play: ()=>{\n animations.forEach((animation)=>{\n animation.play();\n });\n },\n finish: ()=>{\n animations.forEach((animation)=>{\n animation.finish();\n });\n }\n };\n}\n"],"names":["animateAtoms","element","value","options","SUPPORTS_WEB_ANIMATIONS","animate","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","params","animation","fill","duration","persist","playbackRate","rate","forEach","onfinish","callback","Promise","all","finished","then","catch","err","_element_ownerDocument_defaultView","DOMException","ownerDocument","defaultView","cancel","pause","play","finish"],"mappings":";;;;+BAAgBA;;;eAAAA;;;AAAT,SAASA,aAAaC,OAAO,EAAEC,KAAK,EAAEC,OAAO;IAChD,YAAY;IACZ,iHAAiH;IACjH,wGAAwG;IACxG,MAAMC,0BAA0B,OAAOH,QAAQI,OAAO,KAAK;IAC3D,MAAMC,QAAQC,MAAMC,OAAO,CAACN,SAASA,QAAQ;QACzCA;KACH;IACD,MAAM,EAAEO,eAAe,EAAE,GAAGN;IAC5B,MAAMO,aAAaN,0BAA0BE,MAAMK,GAAG,CAAC,CAACC;QACpD,MAAM,EAAEC,SAAS,EAAE,GAAGC,QAAQ,GAAGF;QACjC,MAAMG,YAAYd,QAAQI,OAAO,CAACQ,WAAW;YACzCG,MAAM;YACN,GAAGF,MAAM;YACT,GAAGL,mBAAmB;gBAClBQ,UAAU;YACd,CAAC;QACL;QACAF,UAAUG,OAAO;QACjB,OAAOH;IACX,KAAK,EAAE;IACP,OAAO;QACH,IAAII,cAAcC,KAAK;YACnBV,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUI,YAAY,GAAGC;YAC7B;QACJ;QACA,IAAIE,UAAUC,SAAS;YACnBC,QAAQC,GAAG,CAACf,WAAWC,GAAG,CAAC,CAACI,YAAYA,UAAUW,QAAQ,GAAGC,IAAI,CAAC;gBAC9DJ;YACJ,GAAGK,KAAK,CAAC,CAACC;gBACN,IAAIC;gBACJ,MAAMC,eAAe,AAACD,CAAAA,qCAAqC7B,QAAQ+B,aAAa,CAACC,WAAW,AAAD,MAAO,QAAQH,uCAAuC,KAAK,IAAI,KAAK,IAAIA,mCAAmCC,YAAY;gBAClN,8FAA8F;gBAC9F,IAAIA,gBAAgBF,eAAeE,cAAc;oBAC7C;gBACJ;gBACA,MAAMF;YACV;QACJ;QACAK,QAAQ;YACJxB,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUmB,MAAM;YACpB;QACJ;QACAC,OAAO;YACHzB,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUoB,KAAK;YACnB;QACJ;QACAC,MAAM;YACF1B,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUqB,IAAI;YAClB;QACJ;QACAC,QAAQ;YACJ3B,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUsB,MAAM;YACpB;QACJ;IACJ;AACJ"}
1
+ {"version":3,"sources":["animateAtoms.js"],"sourcesContent":["export function animateAtoms(element, value, options) {\n // Heads up!\n // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. The same is true for\n // older browsers that are out of browser support matrix. In these cases, the animation will be a no-op.\n const SUPPORTS_WEB_ANIMATIONS = typeof element.animate === 'function';\n const atoms = Array.isArray(value) ? value : [\n value\n ];\n const { isReducedMotion } = options;\n const animations = SUPPORTS_WEB_ANIMATIONS ? atoms.map((motion)=>{\n const { keyframes, ...params } = motion;\n const animation = element.animate(keyframes, {\n fill: 'forwards',\n ...params,\n ...isReducedMotion && {\n duration: 1\n }\n });\n animation.persist();\n return animation;\n }) : [];\n return {\n set playbackRate (rate){\n animations.forEach((animation)=>{\n animation.playbackRate = rate;\n });\n },\n setMotionEndCallbacks (onfinish, oncancel) {\n // Heads up!\n // Jest uses jsdom as the default environment, which doesn't support the Web Animations API. This no-op is\n // necessary to avoid errors in tests.\n //\n // See https://github.com/microsoft/fluentui/issues/31593\n // See https://github.com/jsdom/jsdom/issues/3429\n if (process.env.NODE_ENV === 'test') {\n if (animations.length === 0) {\n onfinish();\n return;\n }\n }\n Promise.all(animations.map((animation)=>animation.finished)).then(()=>{\n onfinish();\n }).catch((err)=>{\n var _element_ownerDocument_defaultView;\n const DOMException = (_element_ownerDocument_defaultView = element.ownerDocument.defaultView) === null || _element_ownerDocument_defaultView === void 0 ? void 0 : _element_ownerDocument_defaultView.DOMException;\n // Ignores \"DOMException: The user aborted a request\" that appears if animations are cancelled\n if (DOMException && err instanceof DOMException && err.name === 'AbortError') {\n oncancel();\n return;\n }\n throw err;\n });\n },\n cancel: ()=>{\n animations.forEach((animation)=>{\n animation.cancel();\n });\n },\n pause: ()=>{\n animations.forEach((animation)=>{\n animation.pause();\n });\n },\n play: ()=>{\n animations.forEach((animation)=>{\n animation.play();\n });\n },\n finish: ()=>{\n animations.forEach((animation)=>{\n animation.finish();\n });\n }\n };\n}\n"],"names":["animateAtoms","element","value","options","SUPPORTS_WEB_ANIMATIONS","animate","atoms","Array","isArray","isReducedMotion","animations","map","motion","keyframes","params","animation","fill","duration","persist","playbackRate","rate","forEach","setMotionEndCallbacks","onfinish","oncancel","process","env","NODE_ENV","length","Promise","all","finished","then","catch","err","_element_ownerDocument_defaultView","DOMException","ownerDocument","defaultView","name","cancel","pause","play","finish"],"mappings":";;;;+BAAgBA;;;eAAAA;;;AAAT,SAASA,aAAaC,OAAO,EAAEC,KAAK,EAAEC,OAAO;IAChD,YAAY;IACZ,iHAAiH;IACjH,wGAAwG;IACxG,MAAMC,0BAA0B,OAAOH,QAAQI,OAAO,KAAK;IAC3D,MAAMC,QAAQC,MAAMC,OAAO,CAACN,SAASA,QAAQ;QACzCA;KACH;IACD,MAAM,EAAEO,eAAe,EAAE,GAAGN;IAC5B,MAAMO,aAAaN,0BAA0BE,MAAMK,GAAG,CAAC,CAACC;QACpD,MAAM,EAAEC,SAAS,EAAE,GAAGC,QAAQ,GAAGF;QACjC,MAAMG,YAAYd,QAAQI,OAAO,CAACQ,WAAW;YACzCG,MAAM;YACN,GAAGF,MAAM;YACT,GAAGL,mBAAmB;gBAClBQ,UAAU;YACd,CAAC;QACL;QACAF,UAAUG,OAAO;QACjB,OAAOH;IACX,KAAK,EAAE;IACP,OAAO;QACH,IAAII,cAAcC,KAAK;YACnBV,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUI,YAAY,GAAGC;YAC7B;QACJ;QACAE,uBAAuBC,QAAQ,EAAEC,QAAQ;YACrC,YAAY;YACZ,0GAA0G;YAC1G,sCAAsC;YACtC,EAAE;YACF,yDAAyD;YACzD,iDAAiD;YACjD,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,QAAQ;gBACjC,IAAIjB,WAAWkB,MAAM,KAAK,GAAG;oBACzBL;oBACA;gBACJ;YACJ;YACAM,QAAQC,GAAG,CAACpB,WAAWC,GAAG,CAAC,CAACI,YAAYA,UAAUgB,QAAQ,GAAGC,IAAI,CAAC;gBAC9DT;YACJ,GAAGU,KAAK,CAAC,CAACC;gBACN,IAAIC;gBACJ,MAAMC,eAAe,AAACD,CAAAA,qCAAqClC,QAAQoC,aAAa,CAACC,WAAW,AAAD,MAAO,QAAQH,uCAAuC,KAAK,IAAI,KAAK,IAAIA,mCAAmCC,YAAY;gBAClN,8FAA8F;gBAC9F,IAAIA,gBAAgBF,eAAeE,gBAAgBF,IAAIK,IAAI,KAAK,cAAc;oBAC1Ef;oBACA;gBACJ;gBACA,MAAMU;YACV;QACJ;QACAM,QAAQ;YACJ9B,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAUyB,MAAM;YACpB;QACJ;QACAC,OAAO;YACH/B,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAU0B,KAAK;YACnB;QACJ;QACAC,MAAM;YACFhC,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAU2B,IAAI;YAClB;QACJ;QACAC,QAAQ;YACJjC,WAAWW,OAAO,CAAC,CAACN;gBAChBA,UAAU4B,MAAM;YACpB;QACJ;IACJ;AACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluentui/react-motion",
3
- "version": "9.0.0",
3
+ "version": "9.2.0",
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",
@@ -23,17 +23,19 @@
23
23
  "generate-api": "just-scripts generate-api",
24
24
  "lint": "just-scripts lint",
25
25
  "start": "yarn storybook",
26
- "storybook": "start-storybook",
26
+ "storybook": "yarn --cwd ../stories storybook",
27
27
  "test": "jest --passWithNoTests",
28
- "test-ssr": "test-ssr \"./stories/**/*.stories.tsx\"",
29
- "type-check": "tsc -b tsconfig.json"
28
+ "type-check": "just-scripts type-check",
29
+ "e2e": "cypress run --component",
30
+ "e2e:local": "cypress open --component"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@fluentui/eslint-plugin": "*",
33
34
  "@fluentui/react-conformance": "*",
34
35
  "@fluentui/react-conformance-griffel": "*",
35
36
  "@fluentui/scripts-api-extractor": "*",
36
- "@fluentui/scripts-tasks": "*"
37
+ "@fluentui/scripts-tasks": "*",
38
+ "@fluentui/scripts-cypress": "*"
37
39
  },
38
40
  "dependencies": {
39
41
  "@fluentui/react-shared-contexts": "^9.19.0",