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