@tamagui/animations-reanimated 2.0.0-rc.4 → 2.0.0-rc.40

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.
@@ -2,178 +2,259 @@ var __create = Object.create;
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf,
6
- __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __export = (target, all) => {
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: !0
11
- });
12
- },
13
- __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
15
16
  get: () => from[key],
16
17
  enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
18
  });
18
- return to;
19
- };
19
+ }
20
+ return to;
21
+ };
20
22
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
26
- value: mod,
27
- enumerable: !0
28
- }) : target, mod)),
29
- __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
30
- value: !0
31
- }), mod);
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
32
+ value: true
33
+ }), mod);
32
34
  var createAnimations_exports = {};
33
35
  __export(createAnimations_exports, {
34
36
  createAnimations: () => createAnimations
35
37
  });
36
38
  module.exports = __toCommonJS(createAnimations_exports);
37
- var import_animation_helpers = require("@tamagui/animation-helpers"),
38
- import_core = require("@tamagui/core"),
39
- import_use_presence = require("@tamagui/use-presence"),
40
- import_react = __toESM(require("react"), 1),
41
- import_react_native_reanimated = __toESM(require("react-native-reanimated"), 1),
42
- import_jsx_runtime = require("react/jsx-runtime");
39
+ var import_animation_helpers = require("@tamagui/animation-helpers");
40
+ var import_core = require("@tamagui/core");
41
+ var import_use_presence = require("@tamagui/use-presence");
42
+ var import_react = __toESM(require("react"), 1);
43
+ var import_react_native_reanimated = __toESM(require("react-native-reanimated"), 1);
44
+ var import_jsx_runtime = require("react/jsx-runtime");
43
45
  const getDefaultExport = module2 => {
44
- const mod = module2;
45
- return (mod.__esModule || mod[Symbol.toStringTag] === "Module") && mod.default || mod;
46
- },
47
- Animated = getDefaultExport(import_react_native_reanimated.default),
48
- resolveDynamicValue = (value, isDark) => {
49
- if (value !== null && typeof value == "object" && "dynamic" in value && typeof value.dynamic == "object") {
50
- const dynamic = value.dynamic;
51
- return isDark ? dynamic.dark : dynamic.light;
52
- }
53
- return value;
54
- },
55
- applyAnimation = (targetValue, config) => {
56
- "worklet";
46
+ const mod = module2;
47
+ if (mod.__esModule || mod[Symbol.toStringTag] === "Module") {
48
+ return mod.default || mod;
49
+ }
50
+ return mod;
51
+ };
52
+ const Animated = getDefaultExport(import_react_native_reanimated.default);
53
+ const resolveDynamicValue = (value, isDark) => {
54
+ if (value !== null && typeof value === "object" && "dynamic" in value && typeof value.dynamic === "object") {
55
+ const dynamic = value.dynamic;
56
+ return isDark ? dynamic.dark : dynamic.light;
57
+ }
58
+ return value;
59
+ };
60
+ const applyAnimation = (targetValue, config, callback) => {
61
+ "worklet";
57
62
 
58
- const delay = config.delay;
59
- let animatedValue;
60
- return config.type === "timing" ? animatedValue = (0, import_react_native_reanimated.withTiming)(targetValue, config) : animatedValue = (0, import_react_native_reanimated.withSpring)(targetValue, config), delay && delay > 0 && (animatedValue = (0, import_react_native_reanimated.withDelay)(delay, animatedValue)), animatedValue;
61
- },
62
- ANIMATABLE_PROPERTIES = {
63
- // Transform
64
- transform: !0,
65
- // Opacity
66
- opacity: !0,
67
- // Dimensions
68
- height: !0,
69
- width: !0,
70
- minWidth: !0,
71
- minHeight: !0,
72
- maxWidth: !0,
73
- maxHeight: !0,
74
- // Background
75
- backgroundColor: !0,
76
- // Border colors
77
- borderColor: !0,
78
- borderLeftColor: !0,
79
- borderRightColor: !0,
80
- borderTopColor: !0,
81
- borderBottomColor: !0,
82
- // Border radius
83
- borderRadius: !0,
84
- borderTopLeftRadius: !0,
85
- borderTopRightRadius: !0,
86
- borderBottomLeftRadius: !0,
87
- borderBottomRightRadius: !0,
88
- // Border width
89
- borderWidth: !0,
90
- borderLeftWidth: !0,
91
- borderRightWidth: !0,
92
- borderTopWidth: !0,
93
- borderBottomWidth: !0,
94
- // Text
95
- color: !0,
96
- fontSize: !0,
97
- fontWeight: !0,
98
- lineHeight: !0,
99
- letterSpacing: !0,
100
- // Position
101
- left: !0,
102
- right: !0,
103
- top: !0,
104
- bottom: !0,
105
- // Margin
106
- margin: !0,
107
- marginTop: !0,
108
- marginBottom: !0,
109
- marginLeft: !0,
110
- marginRight: !0,
111
- marginHorizontal: !0,
112
- marginVertical: !0,
113
- // Padding
114
- padding: !0,
115
- paddingTop: !0,
116
- paddingBottom: !0,
117
- paddingLeft: !0,
118
- paddingRight: !0,
119
- paddingHorizontal: !0,
120
- paddingVertical: !0,
121
- // Flex/Gap
122
- gap: !0,
123
- rowGap: !0,
124
- columnGap: !0,
125
- flex: !0,
126
- flexGrow: !0,
127
- flexShrink: !0
128
- },
129
- canAnimateProperty = (key, value, animateOnly) => !(!ANIMATABLE_PROPERTIES[key] || value === "auto" || typeof value == "string" && value.startsWith("calc") || animateOnly && !animateOnly.includes(key));
63
+ const delay = config.delay;
64
+ let animatedValue;
65
+ if (config.type === "timing") {
66
+ animatedValue = (0, import_react_native_reanimated.withTiming)(targetValue, config, callback);
67
+ } else {
68
+ animatedValue = (0, import_react_native_reanimated.withSpring)(targetValue, config, callback);
69
+ }
70
+ if (delay && delay > 0) {
71
+ animatedValue = (0, import_react_native_reanimated.withDelay)(delay, animatedValue);
72
+ }
73
+ return animatedValue;
74
+ };
75
+ const ANIMATABLE_PROPERTIES = {
76
+ // Transform
77
+ transform: true,
78
+ // Opacity
79
+ opacity: true,
80
+ // Dimensions
81
+ height: true,
82
+ width: true,
83
+ minWidth: true,
84
+ minHeight: true,
85
+ maxWidth: true,
86
+ maxHeight: true,
87
+ // Background
88
+ backgroundColor: true,
89
+ // Border colors
90
+ borderColor: true,
91
+ borderLeftColor: true,
92
+ borderRightColor: true,
93
+ borderTopColor: true,
94
+ borderBottomColor: true,
95
+ // Border radius
96
+ borderRadius: true,
97
+ borderTopLeftRadius: true,
98
+ borderTopRightRadius: true,
99
+ borderBottomLeftRadius: true,
100
+ borderBottomRightRadius: true,
101
+ // Border width
102
+ borderWidth: true,
103
+ borderLeftWidth: true,
104
+ borderRightWidth: true,
105
+ borderTopWidth: true,
106
+ borderBottomWidth: true,
107
+ // Text
108
+ color: true,
109
+ fontSize: true,
110
+ fontWeight: true,
111
+ lineHeight: true,
112
+ letterSpacing: true,
113
+ // Position
114
+ left: true,
115
+ right: true,
116
+ top: true,
117
+ bottom: true,
118
+ // Margin
119
+ margin: true,
120
+ marginTop: true,
121
+ marginBottom: true,
122
+ marginLeft: true,
123
+ marginRight: true,
124
+ marginHorizontal: true,
125
+ marginVertical: true,
126
+ // Padding
127
+ padding: true,
128
+ paddingTop: true,
129
+ paddingBottom: true,
130
+ paddingLeft: true,
131
+ paddingRight: true,
132
+ paddingHorizontal: true,
133
+ paddingVertical: true,
134
+ // Flex/Gap
135
+ gap: true,
136
+ rowGap: true,
137
+ columnGap: true,
138
+ flex: true,
139
+ flexGrow: true,
140
+ flexShrink: true
141
+ };
142
+ const canAnimateProperty = (key, value, animateOnly) => {
143
+ if (!ANIMATABLE_PROPERTIES[key]) return false;
144
+ if (value === "auto") return false;
145
+ if (typeof value === "string" && value.startsWith("calc")) return false;
146
+ if (animateOnly && !animateOnly.includes(key)) return false;
147
+ return true;
148
+ };
130
149
  function createWebAnimatedComponent(defaultTag) {
131
- const isText = defaultTag === "span",
132
- Component = Animated.createAnimatedComponent((0, import_react.forwardRef)((propsIn, ref) => {
133
- const {
134
- forwardedRef,
135
- render = defaultTag,
136
- ...rest
137
- } = propsIn,
138
- hostRef = (0, import_react.useRef)(null),
139
- composedRefs = (0, import_core.useComposedRefs)(forwardedRef, ref, hostRef),
140
- stateRef = (0, import_react.useRef)({
141
- get host() {
142
- return hostRef.current;
143
- }
144
- }),
145
- [, themeState] = (0, import_core.useThemeWithState)({}),
146
- viewProps = (0, import_core.getSplitStyles)(rest, isText ? import_core.Text.staticConfig : import_core.View.staticConfig, themeState?.theme ?? {}, themeState?.name ?? "", {
147
- unmounted: !1
148
- }, {
149
- isAnimated: !1,
150
- noClass: !0
151
- })?.viewProps ?? {},
152
- Element = render,
153
- transformedProps = import_core.hooks.usePropsTransform?.(render, viewProps, stateRef, !1);
154
- return /* @__PURE__ */(0, import_jsx_runtime.jsx)(Element, {
155
- ...transformedProps,
156
- ref: composedRefs
157
- });
158
- }));
159
- return Component.acceptTagProp = !0, Component;
150
+ const isText = defaultTag === "span";
151
+ const Component = Animated.createAnimatedComponent((0, import_react.forwardRef)((propsIn, ref) => {
152
+ const {
153
+ forwardedRef,
154
+ render = defaultTag,
155
+ ...rest
156
+ } = propsIn;
157
+ const hostRef = (0, import_react.useRef)(null);
158
+ const composedRefs = (0, import_core.useComposedRefs)(forwardedRef, ref, hostRef);
159
+ const stateRef = (0, import_react.useRef)({
160
+ get host() {
161
+ return hostRef.current;
162
+ }
163
+ });
164
+ const [, themeState] = (0, import_core.useThemeWithState)({});
165
+ const result = (0, import_core.getSplitStyles)(rest, isText ? import_core.Text.staticConfig : import_core.View.staticConfig, themeState?.theme ?? {}, themeState?.name ?? "", {
166
+ unmounted: false
167
+ }, {
168
+ isAnimated: false,
169
+ noClass: true
170
+ });
171
+ const viewProps = result?.viewProps ?? {};
172
+ const Element = render;
173
+ const transformedProps = import_core.hooks.usePropsTransform?.(render, viewProps, stateRef, false);
174
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(Element, {
175
+ ...transformedProps,
176
+ ref: composedRefs
177
+ });
178
+ }));
179
+ Component.acceptRenderProp = true;
180
+ return Component;
181
+ }
182
+ const AnimatedView = createWebAnimatedComponent("div");
183
+ const AnimatedText = createWebAnimatedComponent("span");
184
+ function buildTransitionConfig(transition, animations, animationState, styleKeys) {
185
+ const normalized = (0, import_animation_helpers.normalizeTransition)(transition);
186
+ const effectiveKey = (0, import_animation_helpers.getEffectiveAnimation)(normalized, animationState);
187
+ let base = effectiveKey ? animations[effectiveKey] ?? {
188
+ type: "spring"
189
+ } : {
190
+ type: "spring"
191
+ };
192
+ if (normalized.delay) {
193
+ base = {
194
+ ...base,
195
+ delay: normalized.delay
196
+ };
197
+ }
198
+ if (normalized.config) {
199
+ base = {
200
+ ...base,
201
+ ...normalized.config
202
+ };
203
+ if (base.type !== "timing" && normalized.config.duration !== void 0 && normalized.config.damping === void 0 && normalized.config.stiffness === void 0 && normalized.config.mass === void 0) {
204
+ base = {
205
+ ...base,
206
+ type: "timing"
207
+ };
208
+ }
209
+ }
210
+ const propertyConfigs = {};
211
+ for (const key of styleKeys) {
212
+ const propAnimation = normalized.properties[key];
213
+ if (typeof propAnimation === "string") {
214
+ propertyConfigs[key] = animations[propAnimation] ?? base;
215
+ } else if (propAnimation && typeof propAnimation === "object") {
216
+ const configType = propAnimation.type;
217
+ const baseForProp = configType ? animations[configType] ?? base : base;
218
+ propertyConfigs[key] = {
219
+ ...baseForProp,
220
+ ...propAnimation
221
+ };
222
+ } else {
223
+ propertyConfigs[key] = base;
224
+ }
225
+ }
226
+ return {
227
+ baseConfig: base,
228
+ propertyConfigs
229
+ };
230
+ }
231
+ function getStyleKeys(style) {
232
+ const keys = new Set(Object.keys(style));
233
+ if (style.transform && Array.isArray(style.transform)) {
234
+ for (const t of style.transform) {
235
+ if (t && typeof t === "object") {
236
+ keys.add(Object.keys(t)[0]);
237
+ }
238
+ }
239
+ }
240
+ return keys;
160
241
  }
161
- const AnimatedView = createWebAnimatedComponent("div"),
162
- AnimatedText = createWebAnimatedComponent("span");
163
242
  function createAnimations(animationsConfig) {
164
243
  const animations = {};
165
- for (const key in animationsConfig) animations[key] = {
166
- type: "spring",
167
- ...animationsConfig[key]
168
- };
244
+ for (const key in animationsConfig) {
245
+ animations[key] = {
246
+ type: "spring",
247
+ ...animationsConfig[key]
248
+ };
249
+ }
169
250
  return {
251
+ needsCustomComponent: true,
170
252
  View: import_core.isWeb ? AnimatedView : Animated.View,
171
253
  Text: import_core.isWeb ? AnimatedText : Animated.Text,
172
- isReactNative: !0,
173
- supportsCSS: !1,
254
+ isReactNative: true,
174
255
  inputStyle: "value",
175
256
  outputStyle: "inline",
176
- avoidReRenders: !0,
257
+ avoidReRenders: true,
177
258
  animations,
178
259
  usePresence: import_use_presence.usePresence,
179
260
  ResetPresence: import_use_presence.ResetPresence,
@@ -196,18 +277,27 @@ function createAnimations(animationsConfig) {
196
277
  setValue(next, config = {
197
278
  type: "spring"
198
279
  }, onFinish) {
199
- "worklet";
280
+ if (config.type === "direct") {
281
+ sharedValue.value = next;
282
+ onFinish?.();
283
+ } else {
284
+ const cb = onFinish ? () => {
285
+ "worklet";
200
286
 
201
- const handleFinish = onFinish ? () => {
202
- "worklet";
287
+ (0, import_react_native_reanimated.runOnJS)(onFinish)();
288
+ } : void 0;
289
+ if (import_core.isWeb) {
290
+ sharedValue.value = config.type === "spring" ? (0, import_react_native_reanimated.withSpring)(next, config, cb) : (0, import_react_native_reanimated.withTiming)(next, config, cb);
291
+ } else {
292
+ (0, import_react_native_reanimated.runOnUI)(() => {
293
+ "worklet";
203
294
 
204
- (0, import_react_native_reanimated.runOnJS)(onFinish)();
205
- } : void 0;
206
- config.type === "direct" ? (sharedValue.value = next, onFinish?.()) : config.type === "spring" ? sharedValue.value = (0, import_react_native_reanimated.withSpring)(next, config, handleFinish) : sharedValue.value = (0, import_react_native_reanimated.withTiming)(next, config, handleFinish);
295
+ sharedValue.value = config.type === "spring" ? (0, import_react_native_reanimated.withSpring)(next, config, cb) : (0, import_react_native_reanimated.withTiming)(next, config, cb);
296
+ })();
297
+ }
298
+ }
207
299
  },
208
300
  stop() {
209
- "worklet";
210
-
211
301
  (0, import_react_native_reanimated.cancelAnimation)(sharedValue);
212
302
  }
213
303
  }), [sharedValue]);
@@ -220,121 +310,161 @@ function createAnimations(animationsConfig) {
220
310
  }, onValue) {
221
311
  const instance = value.getInstance();
222
312
  return (0, import_react_native_reanimated.useAnimatedReaction)(() => instance.value, (next, prev) => {
223
- prev !== next && (0, import_react_native_reanimated.runOnJS)(onValue)(next);
313
+ if (prev !== next) {
314
+ (0, import_react_native_reanimated.runOnJS)(onValue)(next);
315
+ }
224
316
  }, [onValue, instance]);
225
317
  },
226
318
  // =========================================================================
227
319
  // useAnimatedNumberStyle - Create animated styles from values
228
320
  // =========================================================================
229
321
  useAnimatedNumberStyle(val, getStyle) {
230
- const instance = val.getInstance(),
231
- derivedValue = (0, import_react_native_reanimated.useDerivedValue)(() => instance.value, [instance, getStyle]);
232
- return (0, import_react_native_reanimated.useAnimatedStyle)(() => getStyle(derivedValue.value), [val, getStyle, derivedValue, instance]);
322
+ const instance = val.getInstance();
323
+ if (import_core.isWeb) {
324
+ return (0, import_react_native_reanimated.useAnimatedStyle)(() => {
325
+ "worklet";
326
+
327
+ return getStyle(instance.value);
328
+ }, [instance, getStyle]);
329
+ }
330
+ const styleVal = (0, import_react_native_reanimated.useDerivedValue)(() => {
331
+ "worklet";
332
+
333
+ return getStyle(instance.value);
334
+ });
335
+ return (0, import_react_native_reanimated.useAnimatedStyle)(() => {
336
+ "worklet";
337
+
338
+ return styleVal.value;
339
+ });
340
+ },
341
+ useAnimatedNumbersStyle(vals, getStyle) {
342
+ const instances = vals.map(v => v.getInstance());
343
+ return (0, import_react_native_reanimated.useAnimatedStyle)(() => {
344
+ "worklet";
345
+
346
+ const currentValues = instances.map(inst => inst.value);
347
+ return getStyle(...currentValues);
348
+ }, import_core.isWeb ? [getStyle, ...instances] : void 0);
233
349
  },
234
350
  // =========================================================================
235
351
  // useAnimations - Main animation hook for components
236
352
  // =========================================================================
237
353
  useAnimations(animationProps) {
238
354
  const {
239
- props,
240
- presence,
241
- style,
242
- componentState,
243
- useStyleEmitter,
244
- themeName
245
- } = animationProps,
246
- isHydrating = componentState.unmounted === !0,
247
- isMounting = componentState.unmounted === "should-enter",
248
- isEntering = !!componentState.unmounted,
249
- isExiting = presence?.[0] === !1,
250
- wasEnteringRef = (0, import_react.useRef)(isEntering),
251
- justFinishedEntering = wasEnteringRef.current && !isEntering;
355
+ props,
356
+ presence,
357
+ style,
358
+ componentState,
359
+ useStyleEmitter,
360
+ themeName,
361
+ stateRef,
362
+ styleState
363
+ } = animationProps;
364
+ const isHydrating = componentState.unmounted === true;
365
+ const isMounting = componentState.unmounted === "should-enter";
366
+ const isEntering = !!componentState.unmounted;
367
+ const isExiting = presence?.[0] === false;
368
+ const wasEnteringRef = (0, import_react.useRef)(isEntering);
369
+ const justFinishedEntering = wasEnteringRef.current && !isEntering;
252
370
  import_react.default.useEffect(() => {
253
371
  wasEnteringRef.current = isEntering;
254
372
  });
255
- const normalized = (0, import_animation_helpers.normalizeTransition)(props.transition),
256
- animationState = isExiting ? "exit" : isMounting || justFinishedEntering ? "enter" : "default",
257
- animationKey = (0, import_animation_helpers.getEffectiveAnimation)(normalized, animationState),
258
- disableAnimation = isHydrating || !animationKey,
259
- isDark = themeName?.startsWith("dark") || !1,
260
- sendExitComplete = presence?.[1],
261
- exitProgress = (0, import_react_native_reanimated.useSharedValue)(0),
262
- animatedTargetsRef = (0, import_react_native_reanimated.useSharedValue)(null),
263
- staticTargetsRef = (0, import_react_native_reanimated.useSharedValue)(null),
264
- transformTargetsRef = (0, import_react_native_reanimated.useSharedValue)(null),
265
- {
266
- animatedStyles,
267
- staticStyles
268
- } = (0, import_react.useMemo)(() => {
269
- const animated = {},
270
- staticStyles2 = {},
271
- animateOnly = props.animateOnly;
272
- for (const key in style) {
273
- const rawValue = style[key],
274
- value = resolveDynamicValue(rawValue, isDark);
275
- if (value !== void 0) {
276
- if (disableAnimation) {
277
- staticStyles2[key] = value;
278
- continue;
279
- }
280
- canAnimateProperty(key, value, animateOnly) ? animated[key] = value : staticStyles2[key] = value;
281
- }
373
+ const effectiveTransition = styleState?.effectiveTransition ?? props.transition;
374
+ const normalized = (0, import_animation_helpers.normalizeTransition)(effectiveTransition);
375
+ const animationState = isExiting ? "exit" : isMounting || justFinishedEntering ? "enter" : "default";
376
+ const animationKey = (0, import_animation_helpers.getEffectiveAnimation)(normalized, animationState);
377
+ const disableAnimation = isHydrating || !animationKey;
378
+ const isDark = themeName?.startsWith("dark") || false;
379
+ const sendExitComplete = presence?.[1];
380
+ const exitCycleIdRef = (0, import_react.useRef)(0);
381
+ const pendingExitKeysRef = (0, import_react.useRef)(/* @__PURE__ */new Set());
382
+ const exitCompletedRef = (0, import_react.useRef)(false);
383
+ const wasExitingRef = (0, import_react.useRef)(false);
384
+ const justStartedExiting = isExiting && !wasExitingRef.current;
385
+ const justStoppedExiting = !isExiting && wasExitingRef.current;
386
+ const markExitKeyDone = (0, import_core.useEvent)((key, cycleId, finished) => {
387
+ if (cycleId !== exitCycleIdRef.current) return;
388
+ if (exitCompletedRef.current) return;
389
+ pendingExitKeysRef.current.delete(key);
390
+ if (pendingExitKeysRef.current.size === 0) {
391
+ exitCompletedRef.current = true;
392
+ sendExitComplete?.();
393
+ }
394
+ });
395
+ const isExitingRef = (0, import_react_native_reanimated.useSharedValue)(isExiting);
396
+ const exitCycleIdShared = (0, import_react_native_reanimated.useSharedValue)(exitCycleIdRef.current);
397
+ if (justStartedExiting) {
398
+ exitCycleIdRef.current++;
399
+ exitCompletedRef.current = false;
400
+ pendingExitKeysRef.current.clear();
401
+ }
402
+ if (justStoppedExiting) {
403
+ exitCycleIdRef.current++;
404
+ pendingExitKeysRef.current.clear();
405
+ }
406
+ (0, import_core.useIsomorphicLayoutEffect)(() => {
407
+ isExitingRef.value = isExiting;
408
+ exitCycleIdShared.value = exitCycleIdRef.current;
409
+ }, [isExiting, exitCycleIdRef.current]);
410
+ import_react.default.useEffect(() => {
411
+ wasExitingRef.current = isExiting;
412
+ });
413
+ const animatedTargetsRef = (0, import_react_native_reanimated.useSharedValue)(null);
414
+ const staticTargetsRef = (0, import_react_native_reanimated.useSharedValue)(null);
415
+ const transformTargetsRef = (0, import_react_native_reanimated.useSharedValue)(null);
416
+ const {
417
+ animatedStyles,
418
+ staticStyles
419
+ } = (0, import_react.useMemo)(() => {
420
+ const animated = {};
421
+ const staticStyles2 = {};
422
+ const animateOnly = props.animateOnly;
423
+ for (const key in style) {
424
+ const rawValue = style[key];
425
+ const value = resolveDynamicValue(rawValue, isDark);
426
+ if (value === void 0) continue;
427
+ if (disableAnimation) {
428
+ staticStyles2[key] = value;
429
+ continue;
430
+ }
431
+ if (canAnimateProperty(key, value, animateOnly)) {
432
+ animated[key] = value;
433
+ } else {
434
+ staticStyles2[key] = value;
435
+ }
436
+ }
437
+ if (isMounting) {
438
+ for (const key in animated) {
439
+ staticStyles2[key] = animated[key];
282
440
  }
283
- if (isMounting) for (const key in animated) staticStyles2[key] = animated[key];
441
+ }
442
+ return {
443
+ animatedStyles: animated,
444
+ staticStyles: staticStyles2
445
+ };
446
+ }, [disableAnimation, style, isDark, isMounting, props.animateOnly]);
447
+ const {
448
+ baseConfig,
449
+ propertyConfigs
450
+ } = (0, import_react.useMemo)(() => {
451
+ if (isHydrating) {
284
452
  return {
285
- animatedStyles: animated,
286
- staticStyles: staticStyles2
287
- };
288
- }, [disableAnimation, style, isDark, isMounting, props.animateOnly]),
289
- {
290
- baseConfig,
291
- propertyConfigs
292
- } = (0, import_react.useMemo)(() => {
293
- if (isHydrating) return {
294
453
  baseConfig: {
295
454
  type: "timing",
296
455
  duration: 0
297
456
  },
298
457
  propertyConfigs: {}
299
458
  };
300
- const normalized2 = (0, import_animation_helpers.normalizeTransition)(props.transition),
301
- effectiveKey = (0, import_animation_helpers.getEffectiveAnimation)(normalized2, animationState);
302
- let base = effectiveKey ? animations[effectiveKey] ?? {
303
- type: "spring"
304
- } : {
305
- type: "spring"
306
- };
307
- normalized2.delay && (base = {
308
- ...base,
309
- delay: normalized2.delay
310
- });
311
- const overrides = {};
312
- for (const key in normalized2.properties) {
313
- const animationNameOrConfig = normalized2.properties[key];
314
- if (typeof animationNameOrConfig == "string") overrides[key] = animations[animationNameOrConfig] ?? base;else if (animationNameOrConfig && typeof animationNameOrConfig == "object") {
315
- const configType = animationNameOrConfig.type,
316
- baseForProp = configType ? animations[configType] ?? base : base;
317
- overrides[key] = {
318
- ...baseForProp,
319
- ...animationNameOrConfig
320
- };
321
- }
322
- }
323
- const configs = {},
324
- allKeys = new Set(Object.keys(animatedStyles));
325
- if (animatedStyles.transform && Array.isArray(animatedStyles.transform)) for (const t of animatedStyles.transform) allKeys.add(Object.keys(t)[0]);
326
- for (const key of allKeys) configs[key] = overrides[key] ?? base;
327
- return {
328
- baseConfig: base,
329
- propertyConfigs: configs
330
- };
331
- }, [isHydrating, props.transition, animatedStyles, animationState]),
332
- configRef = (0, import_react_native_reanimated.useSharedValue)({
333
- baseConfig,
334
- propertyConfigs,
335
- disableAnimation,
336
- isHydrating
337
- });
459
+ }
460
+ return buildTransitionConfig(props.transition, animations, animationState, getStyleKeys(animatedStyles));
461
+ }, [isHydrating, props.transition, animatedStyles, animationState]);
462
+ const configRef = (0, import_react_native_reanimated.useSharedValue)({
463
+ baseConfig,
464
+ propertyConfigs,
465
+ disableAnimation,
466
+ isHydrating
467
+ });
338
468
  (0, import_core.useIsomorphicLayoutEffect)(() => {
339
469
  configRef.set({
340
470
  baseConfig,
@@ -342,68 +472,129 @@ function createAnimations(animationsConfig) {
342
472
  disableAnimation,
343
473
  isHydrating
344
474
  });
345
- }, [baseConfig, propertyConfigs, disableAnimation, isHydrating]), useStyleEmitter?.(nextStyle => {
346
- const animateOnly = props.animateOnly,
347
- animated = {},
348
- statics = {},
349
- transforms = [];
475
+ }, [baseConfig, propertyConfigs, disableAnimation, isHydrating]);
476
+ useStyleEmitter?.((nextStyle, effectiveTransition2) => {
477
+ const animateOnly = props.animateOnly;
478
+ const animated = {};
479
+ const statics = {};
480
+ const transforms = [];
481
+ const transitionToUse = effectiveTransition2 ?? props.transition;
482
+ const {
483
+ baseConfig: newBase,
484
+ propertyConfigs: newPropertyConfigs
485
+ } = buildTransitionConfig(transitionToUse, animations, animationState, getStyleKeys(nextStyle));
486
+ configRef.set({
487
+ baseConfig: newBase,
488
+ propertyConfigs: newPropertyConfigs,
489
+ disableAnimation: configRef.get().disableAnimation,
490
+ isHydrating: configRef.get().isHydrating
491
+ });
350
492
  for (const key in nextStyle) {
351
- const rawValue = nextStyle[key],
352
- value = resolveDynamicValue(rawValue, isDark);
353
- if (value != null) {
354
- if (configRef.get().disableAnimation) {
355
- statics[key] = value;
356
- continue;
493
+ const rawValue = nextStyle[key];
494
+ const value = resolveDynamicValue(rawValue, isDark);
495
+ if (value == void 0) continue;
496
+ if (configRef.get().disableAnimation) {
497
+ statics[key] = value;
498
+ continue;
499
+ }
500
+ if (key === "transform" && Array.isArray(value)) {
501
+ for (const t of value) {
502
+ if (t && typeof t === "object") {
503
+ const tKey = Object.keys(t)[0];
504
+ const tVal = t[tKey];
505
+ if (typeof tVal === "number" || typeof tVal === "string") {
506
+ transforms.push(t);
507
+ }
508
+ }
357
509
  }
358
- if (key === "transform" && Array.isArray(value)) {
359
- for (const t of value) if (t && typeof t == "object") {
360
- const tKey = Object.keys(t)[0],
361
- tVal = t[tKey];
362
- (typeof tVal == "number" || typeof tVal == "string") && transforms.push(t);
510
+ continue;
511
+ }
512
+ if (canAnimateProperty(key, value, animateOnly)) {
513
+ animated[key] = value;
514
+ } else {
515
+ statics[key] = value;
516
+ }
517
+ }
518
+ animatedTargetsRef.set(animated);
519
+ staticTargetsRef.set(statics);
520
+ transformTargetsRef.set(transforms);
521
+ if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
522
+ console.info("[animations-reanimated] useStyleEmitter update", {
523
+ animated,
524
+ statics,
525
+ transforms
526
+ });
527
+ }
528
+ });
529
+ const exitKeysRegistered = (0, import_react.useRef)(false);
530
+ if (justStartedExiting && sendExitComplete) {
531
+ const exitKeys = [];
532
+ const animateOnly = props.animateOnly;
533
+ for (const key in animatedStyles) {
534
+ if (key === "transform") continue;
535
+ if (canAnimateProperty(key, animatedStyles[key], animateOnly)) {
536
+ exitKeys.push(key);
537
+ }
538
+ }
539
+ const transforms = animatedStyles.transform;
540
+ if (transforms && Array.isArray(transforms)) {
541
+ for (const t of transforms) {
542
+ if (!t) continue;
543
+ const tKey = Object.keys(t)[0];
544
+ if (tKey) {
545
+ if (animateOnly && !animateOnly.includes(tKey)) {
546
+ continue;
363
547
  }
364
- continue;
548
+ exitKeys.push(`transform:${tKey}`);
365
549
  }
366
- canAnimateProperty(key, value, animateOnly) ? animated[key] = value : statics[key] = value;
367
550
  }
368
551
  }
369
- animatedTargetsRef.set(animated), staticTargetsRef.set(statics), transformTargetsRef.set(transforms), process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile" && console.info("[animations-reanimated] useStyleEmitter update", {
370
- animated,
371
- statics,
372
- transforms
373
- });
374
- }), import_react.default.useEffect(() => {
375
- if (!isExiting || !sendExitComplete) return;
376
- const config = configRef.get().baseConfig;
377
- return config.type === "timing" ? exitProgress.set((0, import_react_native_reanimated.withTiming)(1, config, finished => {
378
- "worklet";
379
-
380
- finished && (0, import_react_native_reanimated.runOnJS)(sendExitComplete)();
381
- })) : exitProgress.set((0, import_react_native_reanimated.withSpring)(1, config, finished => {
382
- "worklet";
383
-
384
- finished && (0, import_react_native_reanimated.runOnJS)(sendExitComplete)();
385
- })), () => {
386
- (0, import_react_native_reanimated.cancelAnimation)(exitProgress);
387
- };
388
- }, [isExiting, sendExitComplete]);
552
+ pendingExitKeysRef.current = new Set(exitKeys);
553
+ exitKeysRegistered.current = exitKeys.length > 0;
554
+ }
555
+ import_react.default.useEffect(() => {
556
+ if (!justStartedExiting || !sendExitComplete) return;
557
+ if (!exitKeysRegistered.current && pendingExitKeysRef.current.size === 0) {
558
+ if (!exitCompletedRef.current) {
559
+ exitCompletedRef.current = true;
560
+ sendExitComplete();
561
+ }
562
+ }
563
+ }, [justStartedExiting, sendExitComplete]);
389
564
  const animatedStyle = (0, import_react_native_reanimated.useAnimatedStyle)(() => {
390
565
  "worklet";
391
566
 
392
- if (disableAnimation || isHydrating) return {};
393
- const result = {},
394
- config = configRef.get(),
395
- emitterAnimated = animatedTargetsRef.value,
396
- emitterStatic = staticTargetsRef.value,
397
- emitterTransforms = transformTargetsRef.value,
398
- hasEmitterUpdates = emitterAnimated !== null,
399
- animatedValues = hasEmitterUpdates ? emitterAnimated : animatedStyles,
400
- staticValues = hasEmitterUpdates ? emitterStatic : {};
401
- for (const key in staticValues) result[key] = staticValues[key];
567
+ if (disableAnimation || isHydrating) {
568
+ return {};
569
+ }
570
+ const result = {};
571
+ const config = configRef.get();
572
+ const emitterAnimated = animatedTargetsRef.value;
573
+ const emitterStatic = staticTargetsRef.value;
574
+ const emitterTransforms = transformTargetsRef.value;
575
+ const hasEmitterUpdates = emitterAnimated !== null;
576
+ const animatedValues = hasEmitterUpdates ? emitterAnimated : animatedStyles;
577
+ const staticValues = hasEmitterUpdates ? emitterStatic : {};
578
+ const currentlyExiting = isExitingRef.value;
579
+ const currentCycleId = exitCycleIdShared.value;
580
+ for (const key in staticValues) {
581
+ result[key] = staticValues[key];
582
+ }
402
583
  for (const key in animatedValues) {
403
584
  if (key === "transform") continue;
404
- const targetValue = animatedValues[key],
405
- propConfig = config.propertyConfigs[key] ?? config.baseConfig;
406
- result[key] = applyAnimation(targetValue, propConfig);
585
+ const targetValue = animatedValues[key];
586
+ const propConfig = config.propertyConfigs[key] ?? config.baseConfig;
587
+ let callback;
588
+ if (currentlyExiting) {
589
+ const capturedKey = key;
590
+ const capturedCycleId = currentCycleId;
591
+ callback = finished => {
592
+ "worklet";
593
+
594
+ (0, import_react_native_reanimated.runOnJS)(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
595
+ };
596
+ }
597
+ result[key] = applyAnimation(targetValue, propConfig, callback);
407
598
  }
408
599
  const transforms = hasEmitterUpdates ? emitterTransforms : animatedStyles.transform;
409
600
  if (transforms && Array.isArray(transforms)) {
@@ -413,30 +604,45 @@ function createAnimations(animationsConfig) {
413
604
  const keys = Object.keys(t);
414
605
  if (keys.length === 0) continue;
415
606
  const value = t[keys[0]];
416
- if (typeof value == "number" || typeof value == "string") {
417
- const transformKey = Object.keys(t)[0],
418
- targetValue = t[transformKey],
419
- propConfig = config.propertyConfigs[transformKey] ?? config.baseConfig;
607
+ if (typeof value === "number" || typeof value === "string") {
608
+ const transformKey = Object.keys(t)[0];
609
+ const targetValue = t[transformKey];
610
+ const propConfig = config.propertyConfigs[transformKey] ?? config.baseConfig;
611
+ let callback;
612
+ if (currentlyExiting) {
613
+ const capturedKey = `transform:${transformKey}`;
614
+ const capturedCycleId = currentCycleId;
615
+ callback = finished => {
616
+ "worklet";
617
+
618
+ (0, import_react_native_reanimated.runOnJS)(markExitKeyDone)(capturedKey, capturedCycleId, finished ?? false);
619
+ };
620
+ }
420
621
  validTransforms.push({
421
- [transformKey]: applyAnimation(targetValue, propConfig)
622
+ [transformKey]: applyAnimation(targetValue, propConfig, callback)
422
623
  });
423
624
  }
424
625
  }
425
- validTransforms.length > 0 && (result.transform = validTransforms);
626
+ if (validTransforms.length > 0) {
627
+ result.transform = validTransforms;
628
+ }
426
629
  }
427
630
  return result;
428
- }, [animatedStyles, baseConfig, propertyConfigs, disableAnimation, isHydrating,
429
- // Pass SharedValues so the mapper watches them on web (see useAnimatedStyle.ts line 470-472)
430
- animatedTargetsRef, staticTargetsRef, transformTargetsRef]);
431
- return process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile" && console.info("[animations-reanimated] useAnimations", {
432
- animationKey,
433
- componentState,
434
- isExiting,
435
- animatedStyles,
436
- staticStyles,
437
- baseConfig,
438
- propertyConfigs
439
- }), {
631
+ }, import_core.isWeb ? [animatedStyles, baseConfig, propertyConfigs, disableAnimation, isHydrating,
632
+ // pass SharedValues so the mapper watches them on web (no babel plugin)
633
+ animatedTargetsRef, staticTargetsRef, transformTargetsRef, isExitingRef, exitCycleIdShared, markExitKeyDone] : void 0);
634
+ if (process.env.NODE_ENV === "development" && props.debug && props.debug !== "profile") {
635
+ console.info("[animations-reanimated] useAnimations", {
636
+ animationKey,
637
+ componentState,
638
+ isExiting,
639
+ animatedStyles,
640
+ staticStyles,
641
+ baseConfig,
642
+ propertyConfigs
643
+ });
644
+ }
645
+ return {
440
646
  style: [staticStyles, animatedStyle]
441
647
  };
442
648
  }