@tamagui/animations-motion 2.0.0-rc.9 → 2.0.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.
Files changed (40) hide show
  1. package/dist/cjs/createAnimations.cjs +559 -299
  2. package/dist/cjs/createAnimations.native.js +653 -318
  3. package/dist/cjs/createAnimations.native.js.map +1 -1
  4. package/dist/cjs/index.cjs +7 -5
  5. package/dist/cjs/index.native.js +21 -13
  6. package/dist/cjs/index.native.js.map +1 -1
  7. package/dist/cjs/polyfill.cjs +3 -1
  8. package/dist/cjs/polyfill.native.js +3 -1
  9. package/dist/cjs/polyfill.native.js.map +1 -1
  10. package/dist/esm/createAnimations.mjs +529 -272
  11. package/dist/esm/createAnimations.mjs.map +1 -1
  12. package/dist/esm/createAnimations.native.js +623 -291
  13. package/dist/esm/createAnimations.native.js.map +1 -1
  14. package/dist/esm/index.js +1 -2
  15. package/dist/esm/index.js.map +1 -6
  16. package/dist/esm/index.mjs +0 -1
  17. package/dist/esm/index.mjs.map +1 -1
  18. package/dist/esm/index.native.js +9 -3
  19. package/dist/esm/index.native.js.map +1 -1
  20. package/dist/esm/polyfill.mjs +3 -1
  21. package/dist/esm/polyfill.mjs.map +1 -1
  22. package/dist/esm/polyfill.native.js +3 -1
  23. package/dist/esm/polyfill.native.js.map +1 -1
  24. package/package.json +8 -7
  25. package/src/createAnimations.tsx +469 -351
  26. package/types/createAnimations.d.ts +1 -0
  27. package/types/createAnimations.d.ts.map +4 -4
  28. package/types/index.d.ts.map +2 -2
  29. package/types/index.native.d.ts.map +2 -2
  30. package/types/polyfill.d.ts.map +2 -2
  31. package/dist/cjs/createAnimations.js +0 -412
  32. package/dist/cjs/createAnimations.js.map +0 -6
  33. package/dist/cjs/index.js +0 -16
  34. package/dist/cjs/index.js.map +0 -6
  35. package/dist/cjs/polyfill.js +0 -2
  36. package/dist/cjs/polyfill.js.map +0 -6
  37. package/dist/esm/createAnimations.js +0 -416
  38. package/dist/esm/createAnimations.js.map +0 -6
  39. package/dist/esm/polyfill.js +0 -2
  40. package/dist/esm/polyfill.js.map +0 -6
@@ -2,199 +2,305 @@ import { getEffectiveAnimation, normalizeTransition } from "@tamagui/animation-h
2
2
  import { ResetPresence, usePresence } from "@tamagui/use-presence";
3
3
  import { fixStyles, getConfig, getSplitStyles, hooks, styleToCSS, Text, useComposedRefs, useIsomorphicLayoutEffect, useThemeWithState, View } from "@tamagui/web";
4
4
  import { useAnimate, useMotionValue, useMotionValueEvent } from "motion/react";
5
- import React, { forwardRef, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
5
+ import React, { forwardRef, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
- const MotionValueStrategy = /* @__PURE__ */new WeakMap(),
8
- nonPositionTransformRe = /scale|rotate|skew|matrix|perspective/;
7
+ const isServer = typeof window === "undefined";
8
+ function useAnimateSSRSafe() {
9
+ if (isServer) {
10
+ return [useRef(null), () => {}];
11
+ }
12
+ return useAnimate();
13
+ }
14
+ const MotionValueStrategy = /* @__PURE__ */new WeakMap();
15
+ const PendingMotionOnFinish = /* @__PURE__ */new WeakMap();
16
+ function settlePendingMotionOnFinish(mv, controls) {
17
+ const onFinish = PendingMotionOnFinish.get(mv);
18
+ if (!onFinish) return;
19
+ PendingMotionOnFinish.delete(mv);
20
+ controls.then(() => onFinish()).catch(() => onFinish());
21
+ }
9
22
  function createAnimations(animations) {
10
23
  let isHydratingGlobal;
11
24
  const hydratingComponents = /* @__PURE__ */new Set();
12
25
  return {
13
- // this is only used by Sheet basically for now to pass result of useAnimatedStyle to
14
26
  View: MotionView,
15
27
  Text: MotionText,
16
- isReactNative: !1,
17
- supportsCSS: !0,
28
+ isReactNative: false,
18
29
  inputStyle: "css",
19
30
  outputStyle: "inline",
20
- needsWebStyles: !0,
21
- avoidReRenders: !0,
31
+ avoidReRenders: true,
22
32
  animations,
23
33
  usePresence,
24
34
  ResetPresence,
25
35
  onMount() {
26
- isHydratingGlobal = !1, hydratingComponents.forEach(cb => cb());
36
+ isHydratingGlobal = false;
37
+ hydratingComponents.forEach(cb => cb());
27
38
  },
28
39
  useAnimations: animationProps => {
29
- isHydratingGlobal === void 0 && !getConfig().settings.disableSSR && (isHydratingGlobal = !0);
40
+ if (isHydratingGlobal === void 0 && !getConfig().settings.disableSSR) {
41
+ isHydratingGlobal = true;
42
+ }
30
43
  const {
31
- props,
32
- style,
33
- componentState,
34
- stateRef,
35
- useStyleEmitter,
36
- presence
37
- } = animationProps,
38
- animationKey = Array.isArray(props.transition) ? props.transition[0] : props.transition,
39
- isComponentHydrating = componentState.unmounted === !0,
40
- isMounting = componentState.unmounted === "should-enter",
41
- isEntering = !!componentState.unmounted,
42
- isExiting = presence?.[0] === !1,
43
- sendExitComplete = presence?.[1],
44
- wasEnteringRef = useRef(isEntering),
45
- justFinishedEntering = wasEnteringRef.current && !isEntering;
44
+ props,
45
+ style,
46
+ componentState,
47
+ stateRef,
48
+ useStyleEmitter,
49
+ presence
50
+ } = animationProps;
51
+ const animationKey = Array.isArray(props.transition) ? props.transition[0] : props.transition;
52
+ const isComponentHydrating = componentState.unmounted === true;
53
+ const isMounting = componentState.unmounted === "should-enter";
54
+ const isEntering = !!componentState.unmounted;
55
+ const isExiting = presence?.[0] === false;
56
+ const sendExitComplete = presence?.[1];
57
+ const refs = useRef(null);
58
+ if (!refs.current) {
59
+ refs.current = {
60
+ isFirstRender: true,
61
+ lastDoAnimate: null,
62
+ lastDontAnimate: null,
63
+ controls: null,
64
+ lastAnimateAt: 0,
65
+ disposed: false,
66
+ wasExiting: false,
67
+ isExiting: false,
68
+ sendExitComplete: void 0,
69
+ animationState: "default",
70
+ frozenExitTarget: null,
71
+ exitCompleteScheduled: false,
72
+ wasEntering: false,
73
+ wasDisabled: false
74
+ };
75
+ }
76
+ const justFinishedEntering = refs.current.wasEntering && !isEntering;
46
77
  useEffect(() => {
47
- wasEnteringRef.current = isEntering;
78
+ refs.current.wasEntering = isEntering;
48
79
  });
49
- const animationState = isExiting ? "exit" : isMounting || justFinishedEntering ? "enter" : "default",
50
- disableAnimation = isComponentHydrating || isMounting || !animationKey,
51
- isFirstRender = useRef(!0),
52
- [scope, animate] = useAnimate(),
53
- lastDoAnimate = useRef(null),
54
- controls = useRef(null),
55
- styleKey = JSON.stringify(style),
56
- shouldDebug =
57
- // process.env.NODE_ENV === 'development' &&
58
- props.debug && props.debug !== "profile",
59
- {
60
- dontAnimate = {},
61
- doAnimate,
62
- animationOptions
63
- } = useMemo(() => getMotionAnimatedProps(props, style, disableAnimation, animationState), [isExiting, animationKey, styleKey, animationState, disableAnimation]),
64
- id = useId(),
65
- debugId = process.env.NODE_ENV === "development" ? id : "",
66
- lastAnimateAt = useRef(0),
67
- disposed = useRef(!1),
68
- [firstRenderStyle] = useState(style),
69
- lastDontAnimate = useRef(firstRenderStyle),
70
- [isHydrating, setIsHydrating] = useState(isHydratingGlobal);
71
- useLayoutEffect(() => (isHydratingGlobal && hydratingComponents.add(() => {
72
- setIsHydrating(!1);
73
- }), () => {
74
- disposed.current = !0;
75
- }), []);
80
+ const animationState = isExiting ? "exit" : isMounting || justFinishedEntering ? "enter" : "default";
81
+ const disableAnimation = isComponentHydrating || isMounting || !animationKey;
82
+ const [scope, animate] = useAnimateSSRSafe();
83
+ refs.current.isExiting = isExiting;
84
+ refs.current.sendExitComplete = sendExitComplete;
85
+ refs.current.animationState = animationState;
86
+ const justStartedExiting = isExiting && !refs.current.wasExiting;
87
+ const justStoppedExiting = !isExiting && refs.current.wasExiting;
88
+ if (justStartedExiting || justStoppedExiting) {
89
+ refs.current.frozenExitTarget = null;
90
+ refs.current.exitCompleteScheduled = false;
91
+ }
92
+ useEffect(() => {
93
+ refs.current.wasExiting = isExiting;
94
+ });
95
+ const {
96
+ dontAnimate = {},
97
+ doAnimate,
98
+ animationOptions
99
+ } = getMotionAnimatedProps(props, style, disableAnimation, animationState);
100
+ const [firstRenderStyle] = useState(style);
101
+ if (refs.current.isFirstRender) {
102
+ refs.current.lastDontAnimate = firstRenderStyle;
103
+ }
104
+ const [isHydrating, setIsHydrating] = useState(isHydratingGlobal);
105
+ useLayoutEffect(() => {
106
+ if (isHydratingGlobal) {
107
+ hydratingComponents.add(() => {
108
+ setIsHydrating(false);
109
+ });
110
+ }
111
+ return () => {
112
+ refs.current.disposed = true;
113
+ };
114
+ }, []);
76
115
  const flushAnimation = ({
77
- doAnimate: doAnimate2 = {},
78
- animationOptions: animationOptions2 = {},
116
+ doAnimate: doAnimateRaw = {},
117
+ animationOptions: passedOptions = {},
79
118
  dontAnimate: dontAnimate2
80
119
  }) => {
120
+ let startedControls = null;
121
+ const isCurrentlyExiting = refs.current.isExiting;
122
+ const currentSendExitComplete = refs.current.sendExitComplete;
123
+ let doAnimate2 = doAnimateRaw;
124
+ if (isCurrentlyExiting && refs.current.frozenExitTarget) {
125
+ doAnimate2 = refs.current.frozenExitTarget;
126
+ }
127
+ const animationOptions2 = isCurrentlyExiting && currentSendExitComplete ? getAnimationOptions(props.transition ?? null, "exit") : passedOptions;
81
128
  try {
82
129
  const node = stateRef.current.host;
83
- if (isFirstRender.current && (lastDontAnimate.current = null, lastDoAnimate.current = null), shouldDebug && (console.groupCollapsed(`[motion] ${debugId} \u{1F30A} animate (${JSON.stringify(getDiff(lastDoAnimate.current, doAnimate2), null, 2)})`), console.info({
84
- props,
85
- componentState,
86
- doAnimate: doAnimate2,
87
- dontAnimate: dontAnimate2,
88
- animationOptions: animationOptions2,
89
- animationProps,
90
- lastDoAnimate: {
91
- ...lastDoAnimate.current
92
- },
93
- lastDontAnimate: {
94
- ...lastDontAnimate.current
95
- },
96
- isExiting,
97
- style,
98
- node
99
- }), console.groupCollapsed("trace >"), console.trace(), console.groupEnd(), console.groupEnd()), !(node instanceof HTMLElement)) return;
100
- const prevDont = lastDontAnimate.current;
101
- if (dontAnimate2) if (prevDont) {
102
- removeRemovedStyles(prevDont, dontAnimate2, node, doAnimate2);
103
- const changed = getDiff(prevDont, dontAnimate2);
104
- changed && Object.assign(node.style, changed);
105
- } else Object.assign(node.style, dontAnimate2);
130
+ if (refs.current.isFirstRender) {
131
+ refs.current.lastDontAnimate = null;
132
+ refs.current.lastDoAnimate = null;
133
+ }
134
+ if (process.env.NODE_ENV === "development") {
135
+ if (props["debug"] && props["debug"] !== "profile") {
136
+ console.groupCollapsed(`[motion] animate (${JSON.stringify(getDiff(refs.current.lastDoAnimate, doAnimate2), null, 2)})`);
137
+ console.info({
138
+ props,
139
+ componentState,
140
+ doAnimate: doAnimate2,
141
+ dontAnimate: dontAnimate2,
142
+ animationOptions: animationOptions2,
143
+ animationProps,
144
+ lastDoAnimate: {
145
+ ...refs.current.lastDoAnimate
146
+ },
147
+ lastDontAnimate: {
148
+ ...refs.current.lastDontAnimate
149
+ },
150
+ isExiting,
151
+ style,
152
+ node
153
+ });
154
+ console.groupCollapsed(`trace >`);
155
+ console.trace();
156
+ console.groupEnd();
157
+ console.groupEnd();
158
+ }
159
+ }
160
+ if (!(node instanceof HTMLElement)) {
161
+ return;
162
+ }
163
+ const prevDont = refs.current.lastDontAnimate;
164
+ if (dontAnimate2) {
165
+ if (prevDont) {
166
+ removeRemovedStyles(prevDont, dontAnimate2, node, doAnimate2);
167
+ const changed = getDiff(prevDont, dontAnimate2);
168
+ if (changed) {
169
+ Object.assign(node.style, changed);
170
+ }
171
+ } else {
172
+ Object.assign(node.style, dontAnimate2);
173
+ }
174
+ }
106
175
  if (doAnimate2) {
107
176
  if (prevDont) {
108
- const movedToAnimate = {};
109
- for (const key in prevDont) key in doAnimate2 && (node.style[key] = prevDont[key], movedToAnimate[key] = prevDont[key], lastDoAnimate.current && (lastDoAnimate.current[key] = prevDont[key]));
110
- Object.keys(movedToAnimate).length > 0 && animate(scope.current, {
111
- ...movedToAnimate
112
- }, {
113
- duration: 0
114
- });
177
+ for (const key in prevDont) {
178
+ if (key in doAnimate2) {
179
+ node.style[key] = prevDont[key];
180
+ if (refs.current.lastDoAnimate) {
181
+ refs.current.lastDoAnimate[key] = prevDont[key];
182
+ }
183
+ }
184
+ }
185
+ }
186
+ const lastAnimated = refs.current.lastDoAnimate;
187
+ if (lastAnimated) {
188
+ removeRemovedStyles(lastAnimated, doAnimate2, node, dontAnimate2);
115
189
  }
116
- const lastAnimated = lastDoAnimate.current;
117
- lastAnimated && removeRemovedStyles(lastAnimated, doAnimate2, node, dontAnimate2);
118
- const diff = getDiff(lastDoAnimate.current, doAnimate2);
190
+ const diff = getDiff(refs.current.lastDoAnimate, doAnimate2);
119
191
  if (diff) {
120
- const isRunning =
121
- /**
122
- * TypeError: Cannot read properties of undefined (reading 'state')
123
- * at GroupAnimationWithThen.getAll (http://localhost:8081/node_modules/.vite/deps/@tamagui_config_v5-motion.js?v=d717d926:2374:30)
124
- * at get state (http://localhost:8081/node_modules/.vite/deps/@tamagui_config_v5-motion.js?v=d717d926:2403:17)
125
- * at flushAnimation (http://localhost:8081/node_modules/.vite/deps/@tamagui_config_v5-motion.js?v=d717d926:9686:49)
126
- **/
127
- // @ts-expect-error it is there, and for some crazy reason in ~/chat pretty often i get errors ^
128
- controls.current?.animations?.length === 0 ? !1 : controls.current?.state === "running",
129
- targetTransform = typeof diff.transform == "string" ? diff.transform : null,
130
- isPositionOnlyTransform = targetTransform && targetTransform.includes("translate") && !nonPositionTransformRe.test(targetTransform),
131
- isPopperElement = node.hasAttribute("data-popper-animate-position"),
132
- isEnteringPresenceChild = presence && justFinishedEntering;
133
- if (isRunning && controls.current && isPositionOnlyTransform && (isPopperElement || isEnteringPresenceChild)) {
134
- const currentTransform = getComputedStyle(node).transform;
135
- if (currentTransform && currentTransform !== "none") {
136
- const matrixMatch = currentTransform.match(/matrix\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/);
137
- if (matrixMatch) {
138
- controls.current.stop(), node.style.transform = currentTransform;
139
- const currentX = Number.parseFloat(matrixMatch[1]),
140
- currentY = Number.parseFloat(matrixMatch[2]),
141
- keyframeDiff = {
142
- ...diff,
143
- transform: [`translateX(${currentX}px) translateY(${currentY}px)`, targetTransform]
144
- };
145
- controls.current = animate(scope.current, keyframeDiff, animationOptions2), lastAnimateAt.current = Date.now(), lastDontAnimate.current = dontAnimate2 ? {
146
- ...dontAnimate2
147
- } : {}, lastDoAnimate.current = doAnimate2 ? {
148
- ...doAnimate2
149
- } : {};
150
- return;
192
+ if (isCurrentlyExiting && !refs.current.frozenExitTarget) {
193
+ refs.current.frozenExitTarget = {
194
+ ...doAnimate2
195
+ };
196
+ }
197
+ const isPopperPosition = node.hasAttribute("data-popper-animate-position");
198
+ let midFlightValues = null;
199
+ if (refs.current.controls) {
200
+ try {
201
+ const computed = getComputedStyle(node);
202
+ midFlightValues = {};
203
+ for (const key in diff) {
204
+ const val = computed[key];
205
+ if (val !== void 0 && val !== "") {
206
+ midFlightValues[key] = val;
207
+ }
208
+ }
209
+ } catch {}
210
+ if (isCurrentlyExiting) {
211
+ refs.current.controls.stop();
212
+ }
213
+ if (midFlightValues) {
214
+ for (const key in midFlightValues) {
215
+ ;
216
+ node.style[key] = midFlightValues[key];
217
+ }
218
+ }
219
+ if (isPopperPosition) {
220
+ const anims = node.getAnimations();
221
+ for (const anim of anims) {
222
+ anim.cancel();
151
223
  }
152
224
  }
153
225
  }
154
- const fixedDiff = fixTransparentColors({
155
- ...diff
156
- }, lastDoAnimate.current);
157
- controls.current = animate(scope.current, fixedDiff, animationOptions2), lastAnimateAt.current = Date.now();
226
+ const fixedDiff = fixTransparentColors(diff, refs.current.lastDoAnimate, doAnimate2);
227
+ if (midFlightValues?.transform && fixedDiff.transform) {
228
+ fixedDiff.transform = [midFlightValues.transform, fixedDiff.transform];
229
+ }
230
+ startedControls = animate(scope.current, fixedDiff, animationOptions2);
231
+ refs.current.controls = startedControls;
232
+ refs.current.lastAnimateAt = Date.now();
158
233
  }
159
234
  }
160
- lastDontAnimate.current = dontAnimate2 ? {
235
+ refs.current.lastDontAnimate = dontAnimate2 ? {
161
236
  ...dontAnimate2
162
- } : {}, lastDoAnimate.current = doAnimate2 ? {
237
+ } : {};
238
+ refs.current.lastDoAnimate = doAnimate2 ? {
163
239
  ...doAnimate2
164
240
  } : {};
165
241
  } finally {
166
- isExiting && (controls.current ? controls.current.finished.then(() => {
167
- sendExitComplete?.();
168
- }) : sendExitComplete?.());
242
+ if (isCurrentlyExiting && currentSendExitComplete) {
243
+ if (startedControls) {
244
+ refs.current.exitCompleteScheduled = true;
245
+ startedControls.finished.then(() => {
246
+ if (refs.current.isExiting) {
247
+ currentSendExitComplete();
248
+ }
249
+ }).catch(() => {
250
+ if (refs.current.isExiting) {
251
+ currentSendExitComplete();
252
+ }
253
+ });
254
+ } else if (!refs.current.exitCompleteScheduled) {
255
+ currentSendExitComplete();
256
+ }
257
+ }
169
258
  }
170
259
  };
171
- return useStyleEmitter?.(nextStyle => {
172
- const animationProps2 = getMotionAnimatedProps(props, nextStyle, disableAnimation, animationState);
260
+ useStyleEmitter?.((nextStyle, effectiveTransition) => {
261
+ const animationProps2 = getMotionAnimatedProps(props, nextStyle, disableAnimation, refs.current.animationState, effectiveTransition);
173
262
  flushAnimation(animationProps2);
174
- }), useIsomorphicLayoutEffect(() => {
175
- if (isFirstRender.current) {
176
- if (isFirstRender.current = !1, isHydrating) {
177
- const node = stateRef.current.host;
178
- node instanceof HTMLElement && (dontAnimate && (Object.assign(node.style, dontAnimate), animate(scope.current, {
179
- ...dontAnimate
180
- }, {
181
- duration: 0
182
- })), doAnimate && Object.keys(doAnimate).length > 0 ? (lastDoAnimate.current = {
183
- ...doAnimate
184
- }, animate(scope.current, {
185
- ...doAnimate
186
- }, {
187
- duration: 0
188
- })) : lastDoAnimate.current = dontAnimate ? {
189
- ...dontAnimate
190
- } : {}), lastDontAnimate.current = dontAnimate ? {
263
+ });
264
+ useIsomorphicLayoutEffect(() => {
265
+ if (refs.current.isFirstRender) {
266
+ refs.current.isFirstRender = false;
267
+ refs.current.wasDisabled = disableAnimation;
268
+ if (isHydrating) {
269
+ if (doAnimate && Object.keys(doAnimate).length > 0) {
270
+ refs.current.lastDoAnimate = {
271
+ ...doAnimate
272
+ };
273
+ } else {
274
+ refs.current.lastDoAnimate = dontAnimate ? {
275
+ ...dontAnimate
276
+ } : {};
277
+ }
278
+ refs.current.lastDontAnimate = dontAnimate ? {
191
279
  ...dontAnimate
192
- } : {}, lastAnimateAt.current = Date.now();
280
+ } : {};
281
+ refs.current.lastAnimateAt = Date.now();
193
282
  return;
194
283
  }
195
- lastDontAnimate.current = dontAnimate ? {
284
+ refs.current.lastDontAnimate = dontAnimate ? {
196
285
  ...dontAnimate
197
- } : {}, lastDoAnimate.current = doAnimate ? {
286
+ } : {};
287
+ refs.current.lastDoAnimate = doAnimate ? {
288
+ ...doAnimate
289
+ } : {};
290
+ return;
291
+ }
292
+ const justEnabled = refs.current.wasDisabled && !disableAnimation;
293
+ refs.current.wasDisabled = disableAnimation;
294
+ if (justEnabled && animationState !== "enter") {
295
+ const node = stateRef.current.host;
296
+ if (node instanceof HTMLElement) {
297
+ if (dontAnimate) Object.assign(node.style, dontAnimate);
298
+ if (doAnimate) Object.assign(node.style, doAnimate);
299
+ }
300
+ refs.current.lastDontAnimate = dontAnimate ? {
301
+ ...dontAnimate
302
+ } : {};
303
+ refs.current.lastDoAnimate = doAnimate ? {
198
304
  ...doAnimate
199
305
  } : {};
200
306
  return;
@@ -204,18 +310,24 @@ function createAnimations(animations) {
204
310
  dontAnimate,
205
311
  animationOptions
206
312
  });
207
- }, [styleKey, isExiting, disableAnimation]), shouldDebug && (console.groupCollapsed("[motion] \u{1F30A} render"), console.info({
208
- style,
209
- doAnimate,
210
- dontAnimate,
211
- styleKey,
212
- scope,
213
- animationOptions,
214
- isExiting,
215
- isFirstRender: isFirstRender.current,
216
- animationProps
217
- }), console.groupEnd()), {
218
- // we never change this, after first render on
313
+ }, [style, isExiting, disableAnimation]);
314
+ if (process.env.NODE_ENV === "development") {
315
+ if (props["debug"] && props["debug"] !== "profile") {
316
+ console.groupCollapsed(`[motion] render`);
317
+ console.info({
318
+ style,
319
+ doAnimate,
320
+ dontAnimate,
321
+ scope,
322
+ animationOptions,
323
+ isExiting,
324
+ isFirstRender: refs.current.isFirstRender,
325
+ animationProps
326
+ });
327
+ console.groupEnd();
328
+ }
329
+ }
330
+ return {
219
331
  style: firstRenderStyle,
220
332
  ref: scope,
221
333
  render: "div"
@@ -233,16 +345,24 @@ function createAnimations(animations) {
233
345
  setValue(next, config = {
234
346
  type: "spring"
235
347
  }, onFinish) {
236
- if (config.type === "direct") MotionValueStrategy.set(motionValue, {
237
- type: "direct"
238
- }), motionValue.set(next), onFinish?.();else {
239
- if (MotionValueStrategy.set(motionValue, config), onFinish) {
240
- const unsubscribe = motionValue.on("change", value => {
241
- Math.abs(value - next) < 0.01 && (unsubscribe(), onFinish());
242
- });
243
- }
348
+ if (config.type === "direct") {
349
+ MotionValueStrategy.set(motionValue, {
350
+ type: "direct"
351
+ });
244
352
  motionValue.set(next);
353
+ onFinish?.();
354
+ return;
355
+ }
356
+ MotionValueStrategy.set(motionValue, config);
357
+ if (onFinish) {
358
+ const prior = PendingMotionOnFinish.get(motionValue);
359
+ if (prior) {
360
+ PendingMotionOnFinish.delete(motionValue);
361
+ prior();
362
+ }
363
+ PendingMotionOnFinish.set(motionValue, onFinish);
245
364
  }
365
+ motionValue.set(next);
246
366
  },
247
367
  stop() {
248
368
  motionValue.stop();
@@ -256,24 +376,49 @@ function createAnimations(animations) {
256
376
  useMotionValueEvent(instance, "change", onValue);
257
377
  },
258
378
  useAnimatedNumberStyle(val, getStyleProp) {
259
- const motionValue = val.getInstance(),
260
- getStyleRef = useRef(getStyleProp);
261
- return getStyleRef.current = getStyleProp, useMemo(() => ({
262
- getStyle: cur => getStyleRef.current(cur),
263
- motionValue
264
- }), []);
379
+ const motionValue = val.getInstance();
380
+ const getStyleRef = useRef(getStyleProp);
381
+ getStyleRef.current = getStyleProp;
382
+ return useMemo(() => {
383
+ return {
384
+ getStyle: cur => {
385
+ return getStyleRef.current(cur);
386
+ },
387
+ motionValue
388
+ };
389
+ }, []);
390
+ },
391
+ useAnimatedNumbersStyle(vals, getStyleProp) {
392
+ const motionValues = vals.map(v => v.getInstance());
393
+ const getStyleRef = useRef(getStyleProp);
394
+ getStyleRef.current = getStyleProp;
395
+ return useMemo(() => {
396
+ return {
397
+ getStyle: (...currentValues) => getStyleRef.current(...currentValues),
398
+ motionValues
399
+ };
400
+ }, []);
265
401
  }
266
402
  };
267
- function getMotionAnimatedProps(props, style, disable, animationState = "default") {
268
- if (disable) return {
269
- dontAnimate: style
270
- };
271
- const animationOptions = getAnimationOptions(props.transition, animationState);
272
- let dontAnimate, doAnimate;
403
+ function getMotionAnimatedProps(props, style, disable, animationState = "default", transitionOverride) {
404
+ if (disable) {
405
+ return {
406
+ dontAnimate: style
407
+ };
408
+ }
409
+ const animationOptions = getAnimationOptions(transitionOverride ?? props.transition ?? null, animationState);
410
+ let dontAnimate;
411
+ let doAnimate;
273
412
  const animateOnly = props.animateOnly;
274
413
  for (const key in style) {
275
414
  const value = style[key];
276
- disableAnimationProps.has(key) || animateOnly && !animateOnly.includes(key) ? (dontAnimate ||= {}, dontAnimate[key] = value) : (doAnimate ||= {}, doAnimate[key] = value);
415
+ if (disableAnimationProps.has(key) || animateOnly && !animateOnly.includes(key)) {
416
+ dontAnimate ||= {};
417
+ dontAnimate[key] = value;
418
+ } else {
419
+ doAnimate ||= {};
420
+ doAnimate[key] = value;
421
+ }
277
422
  }
278
423
  return {
279
424
  dontAnimate,
@@ -282,100 +427,167 @@ function createAnimations(animations) {
282
427
  };
283
428
  }
284
429
  function getAnimationOptions(transitionProp, animationState = "default") {
285
- const normalized = normalizeTransition(transitionProp),
286
- effectiveKey = getEffectiveAnimation(normalized, animationState);
287
- if (!effectiveKey && Object.keys(normalized.properties).length === 0) return {};
288
- const defaultConfig = effectiveKey ? withInferredType(animations[effectiveKey]) : null,
289
- delay = typeof normalized.delay == "number" ? normalized.delay / 1e3 : void 0;
290
- let globalConfigOverride;
291
- normalized.config && (globalConfigOverride = {
430
+ const normalized = normalizeTransition(transitionProp);
431
+ let effectiveKey = getEffectiveAnimation(normalized, animationState);
432
+ if (!effectiveKey && animationState === "default") {
433
+ effectiveKey = normalized.enter || normalized.exit || null;
434
+ }
435
+ const globalConfigOverride = normalized.config ? {
292
436
  ...normalized.config
293
- }, typeof normalized.config.duration == "number" && (globalConfigOverride.duration = normalized.config.duration / 1e3));
437
+ } : void 0;
438
+ if (!effectiveKey && Object.keys(normalized.properties).length === 0 && !globalConfigOverride) {
439
+ return {};
440
+ }
441
+ const defaultConfig = effectiveKey ? withInferredType(animations[effectiveKey]) : null;
442
+ const delay = normalized.delay;
294
443
  const result = {};
295
- defaultConfig && Object.assign(result, defaultConfig), globalConfigOverride && Object.assign(result, globalConfigOverride), delay && (result.delay = delay), (defaultConfig || globalConfigOverride || delay) && (result.default = {
296
- ...defaultConfig,
297
- ...globalConfigOverride,
298
- ...(delay ? {
299
- delay
300
- } : null)
301
- });
302
- for (const [propName, animationNameOrConfig] of Object.entries(normalized.properties)) if (typeof animationNameOrConfig == "string") result[propName] = withInferredType(animations[animationNameOrConfig]);else if (animationNameOrConfig && typeof animationNameOrConfig == "object") {
303
- const baseConfig = animationNameOrConfig.type ? withInferredType(animations[animationNameOrConfig.type]) : defaultConfig;
304
- result[propName] = {
305
- ...baseConfig,
306
- ...animationNameOrConfig
444
+ if (defaultConfig) {
445
+ Object.assign(result, defaultConfig);
446
+ }
447
+ if (globalConfigOverride) {
448
+ Object.assign(result, globalConfigOverride);
449
+ if (result.type === void 0 && result.duration !== void 0 && result.damping === void 0 && result.stiffness === void 0 && result.mass === void 0) {
450
+ result.type = "tween";
451
+ }
452
+ }
453
+ if (delay) {
454
+ result.delay = delay;
455
+ }
456
+ if (defaultConfig || globalConfigOverride || delay) {
457
+ result.default = {
458
+ ...defaultConfig,
459
+ ...globalConfigOverride,
460
+ ...(delay ? {
461
+ delay
462
+ } : null)
307
463
  };
308
464
  }
465
+ for (const [propName, animationNameOrConfig] of Object.entries(normalized.properties)) {
466
+ if (typeof animationNameOrConfig === "string") {
467
+ result[propName] = withInferredType(animations[animationNameOrConfig]);
468
+ } else if (animationNameOrConfig && typeof animationNameOrConfig === "object") {
469
+ const baseConfig = animationNameOrConfig.type ? withInferredType(animations[animationNameOrConfig.type]) : defaultConfig;
470
+ result[propName] = {
471
+ ...baseConfig,
472
+ ...animationNameOrConfig
473
+ };
474
+ }
475
+ }
476
+ convertMsToS(result);
309
477
  convertMsToS(result.default);
310
- for (const key in result) key !== "default" && convertMsToS(result[key]);
478
+ for (const key in result) {
479
+ if (key !== "default" && typeof result[key] === "object") {
480
+ convertMsToS(result[key]);
481
+ }
482
+ }
311
483
  return result;
312
484
  }
313
485
  }
314
486
  function withInferredType(config) {
487
+ if (!config) {
488
+ return {
489
+ type: "spring"
490
+ };
491
+ }
492
+ const isTimingBased = config.duration !== void 0 && config.damping === void 0 && config.stiffness === void 0 && config.mass === void 0;
315
493
  return {
316
- type: config.duration !== void 0 && config.damping === void 0 && config.stiffness === void 0 && config.mass === void 0 ? "tween" : "spring",
494
+ type: isTimingBased ? "tween" : "spring",
317
495
  ...config
318
496
  };
319
497
  }
320
498
  function convertMsToS(config) {
321
- !config || config.type !== "tween" || (typeof config.duration == "number" && (config.duration = config.duration / 1e3), typeof config.delay == "number" && (config.delay = config.delay / 1e3));
499
+ if (!config) return;
500
+ if (typeof config.delay === "number") config.delay = config.delay / 1e3;
501
+ if (typeof config.duration === "number") {
502
+ const isTimingBased = config.type === "tween" || config.type === void 0 && config.damping === void 0 && config.stiffness === void 0 && config.mass === void 0;
503
+ if (isTimingBased) {
504
+ config.duration = config.duration / 1e3;
505
+ }
506
+ }
322
507
  }
323
508
  function removeRemovedStyles(prev, next, node, dontClearIfIn) {
324
- for (const key in prev) if (!(key in next)) {
325
- if (dontClearIfIn && key in dontClearIfIn) continue;
326
- node.style[key] = "";
509
+ for (const key in prev) {
510
+ if (!(key in next)) {
511
+ if (dontClearIfIn && key in dontClearIfIn) {
512
+ continue;
513
+ }
514
+ node.style[key] = "";
515
+ }
327
516
  }
328
517
  }
329
- const disableAnimationProps = /* @__PURE__ */new Set(["alignContent", "alignItems", "aspectRatio", "backdropFilter", "boxSizing", "contain", "containerType", "display", "flexBasis", "flexDirection", "flexGrow", "flexShrink", "fontFamily", "justifyContent", "marginBottom", "marginLeft", "marginRight", "marginTop", "maxHeight", "maxWidth", "minHeight", "minWidth", "overflow", "overflowX", "overflowY", "pointerEvents", "position", "textWrap", "transformOrigin", "userSelect", "WebkitBackdropFilter", "zIndex"]),
330
- MotionView = createMotionView("div"),
331
- MotionText = createMotionView("span");
518
+ const disableAnimationProps = /* @__PURE__ */new Set(["alignContent", "alignItems", "boxSizing", "contain", "containerType", "display", "flexBasis", "flexDirection", "fontFamily", "justifyContent", "overflow", "overflowX", "overflowY", "pointerEvents", "position", "textWrap", "userSelect"]);
519
+ const MotionView = createMotionView("div");
520
+ const MotionText = createMotionView("span");
332
521
  function createMotionView(defaultTag) {
333
- const isText = defaultTag === "span",
334
- Component = forwardRef((propsIn, ref) => {
335
- const {
336
- forwardedRef,
337
- animation,
338
- render = defaultTag,
339
- style,
340
- ...propsRest
341
- } = propsIn,
342
- [scope, animate] = useAnimate(),
343
- hostRef = useRef(null),
344
- composedRefs = useComposedRefs(forwardedRef, ref, hostRef, scope),
345
- stateRef = useRef(null);
346
- stateRef.current || (stateRef.current = {
522
+ const isText = defaultTag === "span";
523
+ const Component = forwardRef((propsIn, ref) => {
524
+ const {
525
+ forwardedRef,
526
+ animation,
527
+ render = defaultTag,
528
+ style,
529
+ ...propsRest
530
+ } = propsIn;
531
+ const [scope, animate] = useAnimateSSRSafe();
532
+ const hostRef = useRef(null);
533
+ const composedRefs = useComposedRefs(forwardedRef, ref, hostRef, scope);
534
+ const stateRef = useRef(null);
535
+ if (!stateRef.current) {
536
+ stateRef.current = {
347
537
  get host() {
348
538
  return hostRef.current;
349
539
  }
540
+ };
541
+ }
542
+ const [_, state] = useThemeWithState({});
543
+ const styles = Array.isArray(style) ? style : [style];
544
+ const [animatedStyle, nonAnimatedStyles] = (() => {
545
+ let animatedStyle2;
546
+ const nonAnimatedStyles2 = [];
547
+ for (const style2 of styles) {
548
+ if (style2.getStyle) {
549
+ animatedStyle2 = style2;
550
+ } else {
551
+ nonAnimatedStyles2.push(style2);
552
+ }
553
+ }
554
+ return [animatedStyle2, nonAnimatedStyles2];
555
+ })();
556
+ function getProps(props2) {
557
+ const out = getSplitStyles(props2, isText ? Text.staticConfig : View.staticConfig, state?.theme, state?.name, {
558
+ unmounted: false
559
+ }, {
560
+ isAnimated: false,
561
+ noClass: true,
562
+ resolveValues: "auto"
350
563
  });
351
- const [_, state] = useThemeWithState({}),
352
- styles = Array.isArray(style) ? style : [style],
353
- [animatedStyle, nonAnimatedStyles] = [styles.find(x => x.getStyle), styles.filter(x => !x.getStyle)];
354
- function getProps(props2) {
355
- const out = getSplitStyles(props2, isText ? Text.staticConfig : View.staticConfig, state?.theme, state?.name, {
356
- unmounted: !1
357
- }, {
358
- isAnimated: !1,
359
- noClass: !0,
360
- // noMergeStyle: true,
361
- resolveValues: "auto"
362
- });
363
- return out ? (out.viewProps.style && (fixStyles(out.viewProps.style), styleToCSS(out.viewProps.style)), out.viewProps) : {};
564
+ if (!out) {
565
+ return {};
566
+ }
567
+ if (out.viewProps.style) {
568
+ fixStyles(out.viewProps.style);
569
+ styleToCSS(out.viewProps.style);
364
570
  }
365
- const props = getProps({
366
- ...propsRest,
367
- style: nonAnimatedStyles
368
- }),
369
- Element = render || "div",
370
- transformedProps = hooks.usePropsTransform?.(render, props, stateRef, !1);
371
- return useEffect(() => {
372
- if (animatedStyle) return animatedStyle.motionValue.on("change", value => {
373
- const nextStyle = animatedStyle.getStyle(value),
374
- animationConfig = MotionValueStrategy.get(animatedStyle.motionValue),
375
- node = hostRef.current,
376
- webStyle = getProps({
377
- style: nextStyle
378
- }).style;
571
+ return out.viewProps;
572
+ }
573
+ const props = getProps({
574
+ ...propsRest,
575
+ style: nonAnimatedStyles
576
+ });
577
+ const Element = render || "div";
578
+ const transformedProps = hooks.usePropsTransform?.(render, props, stateRef, false);
579
+ useEffect(() => {
580
+ if (!animatedStyle) return;
581
+ if (animatedStyle.motionValues) {
582
+ const mvs = animatedStyle.motionValues;
583
+ const unsubs = mvs.map(mv => mv.on("change", () => {
584
+ const currentValues = mvs.map(v => v.get());
585
+ const nextStyle = animatedStyle.getStyle(...currentValues);
586
+ const animationConfig = MotionValueStrategy.get(mv);
587
+ const node = hostRef.current;
588
+ const webStyle = getProps({
589
+ style: nextStyle
590
+ }).style;
379
591
  if (webStyle && node instanceof HTMLElement) {
380
592
  const motionAnimationConfig = animationConfig?.type === "timing" ? {
381
593
  type: "tween",
@@ -387,36 +599,81 @@ function createMotionView(defaultTag) {
387
599
  type: "spring",
388
600
  ...animationConfig
389
601
  };
390
- animate(node, webStyle, motionAnimationConfig);
602
+ const controls = animate(node, webStyle, motionAnimationConfig);
603
+ settlePendingMotionOnFinish(mv, controls);
391
604
  }
392
- });
393
- }, [animatedStyle]), /* @__PURE__ */jsx(Element, {
394
- ...transformedProps,
395
- ref: composedRefs
605
+ }));
606
+ return () => unsubs.forEach(fn => fn());
607
+ }
608
+ if (!animatedStyle.motionValue) return;
609
+ return animatedStyle.motionValue.on("change", value => {
610
+ const nextStyle = animatedStyle.getStyle(value);
611
+ const animationConfig = MotionValueStrategy.get(animatedStyle.motionValue);
612
+ const node = hostRef.current;
613
+ const webStyle = getProps({
614
+ style: nextStyle
615
+ }).style;
616
+ if (webStyle && node instanceof HTMLElement) {
617
+ const motionAnimationConfig = animationConfig?.type === "timing" ? {
618
+ type: "tween",
619
+ duration: (animationConfig?.duration || 0) / 1e3
620
+ } : animationConfig?.type === "direct" ? {
621
+ type: "tween",
622
+ duration: 0
623
+ } : {
624
+ type: "spring",
625
+ ...animationConfig
626
+ };
627
+ const controls = animate(node, webStyle, motionAnimationConfig);
628
+ settlePendingMotionOnFinish(animatedStyle.motionValue, controls);
629
+ }
396
630
  });
631
+ }, [animatedStyle]);
632
+ return /* @__PURE__ */jsx(Element, {
633
+ ...transformedProps,
634
+ ref: composedRefs
397
635
  });
398
- return Component.acceptTagProp = !0, Component;
636
+ });
637
+ Component["acceptRenderProp"] = true;
638
+ return Component;
399
639
  }
400
640
  function getDiff(previous, next) {
401
- if (!previous) return next;
641
+ if (!previous) {
642
+ return next;
643
+ }
402
644
  let diff = null;
403
- for (const key in next) next[key] !== previous[key] && (diff ||= {}, diff[key] = next[key]);
645
+ for (const key in next) {
646
+ if (next[key] !== previous[key]) {
647
+ diff ||= {};
648
+ diff[key] = next[key];
649
+ }
650
+ }
404
651
  return diff;
405
652
  }
406
- function fixTransparentColors(diff, previous) {
653
+ function fixTransparentColors(diff, previous, next) {
407
654
  let result = diff;
408
- for (const key in diff) if (diff[key] === "transparent") {
409
- const prev = previous?.[key];
410
- let fixed = "rgba(0, 0, 0, 0)";
411
- if (typeof prev == "string") {
412
- const rgbaMatch = prev.match(/^rgba?\(([^,]+),\s*([^,]+),\s*([^,)]+)/);
413
- rgbaMatch && (fixed = `rgba(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]}, 0)`);
655
+ for (const key in diff) {
656
+ if (diff[key] === "transparent") {
657
+ let fixed = "rgba(0, 0, 0, 0)";
658
+ const candidates = [previous?.[key], next?.[key]];
659
+ for (const candidate of candidates) {
660
+ if (typeof candidate === "string" && candidate !== "transparent") {
661
+ const rgbaMatch = candidate.match(/^rgba?\(([^,]+),\s*([^,]+),\s*([^,)]+)/);
662
+ if (rgbaMatch) {
663
+ fixed = `rgba(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]}, 0)`;
664
+ break;
665
+ }
666
+ }
667
+ }
668
+ if (result === diff) {
669
+ result = {
670
+ ...diff
671
+ };
672
+ }
673
+ result[key] = fixed;
414
674
  }
415
- result === diff && (result = {
416
- ...diff
417
- }), result[key] = fixed;
418
675
  }
419
676
  return result;
420
677
  }
421
- export { createAnimations };
678
+ export { createAnimations, disableAnimationProps };
422
679
  //# sourceMappingURL=createAnimations.mjs.map