react-native-screen-transitions 3.2.1 → 3.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +105 -10
  2. package/lib/commonjs/blank-stack/components/adjusted-screen.js +2 -2
  3. package/lib/commonjs/blank-stack/components/adjusted-screen.js.map +1 -1
  4. package/lib/commonjs/shared/components/create-transition-aware-component.js +8 -2
  5. package/lib/commonjs/shared/components/create-transition-aware-component.js.map +1 -1
  6. package/lib/commonjs/shared/components/{root-transition-aware.js → screen-container.js} +28 -12
  7. package/lib/commonjs/shared/components/screen-container.js.map +1 -0
  8. package/lib/commonjs/shared/configs/presets.js +3 -3
  9. package/lib/commonjs/shared/configs/presets.js.map +1 -1
  10. package/lib/commonjs/shared/configs/specs.js +6 -1
  11. package/lib/commonjs/shared/configs/specs.js.map +1 -1
  12. package/lib/commonjs/shared/constants.js +36 -10
  13. package/lib/commonjs/shared/constants.js.map +1 -1
  14. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +25 -18
  15. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
  16. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js +25 -202
  17. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  18. package/lib/commonjs/shared/hooks/gestures/use-screen-gesture-handlers.js +342 -0
  19. package/lib/commonjs/shared/hooks/gestures/use-screen-gesture-handlers.js.map +1 -0
  20. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js +47 -4
  21. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  22. package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js +3 -3
  23. package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js.map +1 -1
  24. package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js +25 -3
  25. package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js.map +1 -1
  26. package/lib/commonjs/shared/hooks/navigation/use-screen-state.js +33 -2
  27. package/lib/commonjs/shared/hooks/navigation/use-screen-state.js.map +1 -1
  28. package/lib/commonjs/shared/hooks/use-backdrop-pointer-events.js +32 -0
  29. package/lib/commonjs/shared/hooks/use-backdrop-pointer-events.js.map +1 -0
  30. package/lib/commonjs/shared/providers/gestures.provider.js +4 -2
  31. package/lib/commonjs/shared/providers/gestures.provider.js.map +1 -1
  32. package/lib/commonjs/shared/providers/screen/screen-composer.js +2 -2
  33. package/lib/commonjs/shared/providers/screen/screen-composer.js.map +1 -1
  34. package/lib/commonjs/shared/utils/animation/{start-screen-transition.js → animate-to-progress.js} +16 -8
  35. package/lib/commonjs/shared/utils/animation/animate-to-progress.js.map +1 -0
  36. package/lib/commonjs/shared/utils/gesture/check-gesture-activation.js +138 -0
  37. package/lib/commonjs/shared/utils/gesture/check-gesture-activation.js.map +1 -1
  38. package/lib/commonjs/shared/utils/gesture/determine-snap-target.js +56 -0
  39. package/lib/commonjs/shared/utils/gesture/determine-snap-target.js.map +1 -0
  40. package/lib/commonjs/shared/utils/gesture/validate-snap-points.js +31 -0
  41. package/lib/commonjs/shared/utils/gesture/validate-snap-points.js.map +1 -0
  42. package/lib/commonjs/shared/utils/gesture/velocity.js +11 -0
  43. package/lib/commonjs/shared/utils/gesture/velocity.js.map +1 -1
  44. package/lib/commonjs/shared/utils/logger.js +22 -0
  45. package/lib/commonjs/shared/utils/logger.js.map +1 -0
  46. package/lib/module/blank-stack/components/adjusted-screen.js +1 -1
  47. package/lib/module/blank-stack/components/adjusted-screen.js.map +1 -1
  48. package/lib/module/shared/components/create-transition-aware-component.js +8 -2
  49. package/lib/module/shared/components/create-transition-aware-component.js.map +1 -1
  50. package/lib/module/shared/components/screen-container.js +64 -0
  51. package/lib/module/shared/components/screen-container.js.map +1 -0
  52. package/lib/module/shared/configs/presets.js +3 -3
  53. package/lib/module/shared/configs/presets.js.map +1 -1
  54. package/lib/module/shared/configs/specs.js +5 -0
  55. package/lib/module/shared/configs/specs.js.map +1 -1
  56. package/lib/module/shared/constants.js +34 -9
  57. package/lib/module/shared/constants.js.map +1 -1
  58. package/lib/module/shared/hooks/animation/use-screen-animation.js +25 -18
  59. package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
  60. package/lib/module/shared/hooks/gestures/use-build-gestures.js +25 -201
  61. package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  62. package/lib/module/shared/hooks/gestures/use-screen-gesture-handlers.js +336 -0
  63. package/lib/module/shared/hooks/gestures/use-screen-gesture-handlers.js.map +1 -0
  64. package/lib/module/shared/hooks/gestures/use-scroll-registry.js +47 -4
  65. package/lib/module/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  66. package/lib/module/shared/hooks/lifecycle/use-close-transition.js +3 -3
  67. package/lib/module/shared/hooks/lifecycle/use-close-transition.js.map +1 -1
  68. package/lib/module/shared/hooks/lifecycle/use-open-transition.js +25 -3
  69. package/lib/module/shared/hooks/lifecycle/use-open-transition.js.map +1 -1
  70. package/lib/module/shared/hooks/navigation/use-screen-state.js +35 -4
  71. package/lib/module/shared/hooks/navigation/use-screen-state.js.map +1 -1
  72. package/lib/module/shared/hooks/use-backdrop-pointer-events.js +28 -0
  73. package/lib/module/shared/hooks/use-backdrop-pointer-events.js.map +1 -0
  74. package/lib/module/shared/providers/gestures.provider.js +4 -2
  75. package/lib/module/shared/providers/gestures.provider.js.map +1 -1
  76. package/lib/module/shared/providers/screen/screen-composer.js +2 -2
  77. package/lib/module/shared/providers/screen/screen-composer.js.map +1 -1
  78. package/lib/module/shared/utils/animation/{start-screen-transition.js → animate-to-progress.js} +14 -6
  79. package/lib/module/shared/utils/animation/animate-to-progress.js.map +1 -0
  80. package/lib/module/shared/utils/gesture/check-gesture-activation.js +137 -0
  81. package/lib/module/shared/utils/gesture/check-gesture-activation.js.map +1 -1
  82. package/lib/module/shared/utils/gesture/determine-snap-target.js +52 -0
  83. package/lib/module/shared/utils/gesture/determine-snap-target.js.map +1 -0
  84. package/lib/module/shared/utils/gesture/validate-snap-points.js +26 -0
  85. package/lib/module/shared/utils/gesture/validate-snap-points.js.map +1 -0
  86. package/lib/module/shared/utils/gesture/velocity.js +11 -0
  87. package/lib/module/shared/utils/gesture/velocity.js.map +1 -1
  88. package/lib/module/shared/utils/logger.js +17 -0
  89. package/lib/module/shared/utils/logger.js.map +1 -0
  90. package/lib/typescript/blank-stack/components/adjusted-screen.d.ts.map +1 -1
  91. package/lib/typescript/shared/components/create-transition-aware-component.d.ts.map +1 -1
  92. package/lib/typescript/shared/components/screen-container.d.ts +6 -0
  93. package/lib/typescript/shared/components/screen-container.d.ts.map +1 -0
  94. package/lib/typescript/shared/configs/specs.d.ts +1 -0
  95. package/lib/typescript/shared/configs/specs.d.ts.map +1 -1
  96. package/lib/typescript/shared/constants.d.ts +9 -0
  97. package/lib/typescript/shared/constants.d.ts.map +1 -1
  98. package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
  99. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts +1 -1
  100. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
  101. package/lib/typescript/shared/hooks/gestures/use-screen-gesture-handlers.d.ts +19 -0
  102. package/lib/typescript/shared/hooks/gestures/use-screen-gesture-handlers.d.ts.map +1 -0
  103. package/lib/typescript/shared/hooks/gestures/use-scroll-registry.d.ts +5 -1
  104. package/lib/typescript/shared/hooks/gestures/use-scroll-registry.d.ts.map +1 -1
  105. package/lib/typescript/shared/hooks/lifecycle/use-open-transition.d.ts.map +1 -1
  106. package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts +7 -0
  107. package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts.map +1 -1
  108. package/lib/typescript/shared/hooks/use-backdrop-pointer-events.d.ts +15 -0
  109. package/lib/typescript/shared/hooks/use-backdrop-pointer-events.d.ts.map +1 -0
  110. package/lib/typescript/shared/providers/gestures.provider.d.ts +1 -0
  111. package/lib/typescript/shared/providers/gestures.provider.d.ts.map +1 -1
  112. package/lib/typescript/shared/types/animation.types.d.ts +37 -2
  113. package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
  114. package/lib/typescript/shared/types/screen.types.d.ts +26 -0
  115. package/lib/typescript/shared/types/screen.types.d.ts.map +1 -1
  116. package/lib/typescript/shared/utils/animation/animate-to-progress.d.ts +19 -0
  117. package/lib/typescript/shared/utils/animation/animate-to-progress.d.ts.map +1 -0
  118. package/lib/typescript/shared/utils/gesture/check-gesture-activation.d.ts +23 -0
  119. package/lib/typescript/shared/utils/gesture/check-gesture-activation.d.ts.map +1 -1
  120. package/lib/typescript/shared/utils/gesture/determine-snap-target.d.ts +26 -0
  121. package/lib/typescript/shared/utils/gesture/determine-snap-target.d.ts.map +1 -0
  122. package/lib/typescript/shared/utils/gesture/validate-snap-points.d.ts +13 -0
  123. package/lib/typescript/shared/utils/gesture/validate-snap-points.d.ts.map +1 -0
  124. package/lib/typescript/shared/utils/gesture/velocity.d.ts +1 -0
  125. package/lib/typescript/shared/utils/gesture/velocity.d.ts.map +1 -1
  126. package/lib/typescript/shared/utils/logger.d.ts +6 -0
  127. package/lib/typescript/shared/utils/logger.d.ts.map +1 -0
  128. package/package.json +3 -2
  129. package/src/blank-stack/components/adjusted-screen.tsx +1 -1
  130. package/src/shared/__tests__/derivations.test.ts +1 -0
  131. package/src/shared/__tests__/determine-snap-target.test.ts +268 -0
  132. package/src/shared/__tests__/gesture-activation.test.ts +220 -0
  133. package/src/shared/__tests__/validate-snap-points.test.ts +125 -0
  134. package/src/shared/components/create-transition-aware-component.tsx +11 -1
  135. package/src/shared/components/screen-container.tsx +65 -0
  136. package/src/shared/configs/presets.ts +3 -3
  137. package/src/shared/configs/specs.ts +6 -0
  138. package/src/shared/constants.ts +36 -9
  139. package/src/shared/hooks/animation/use-screen-animation.tsx +32 -21
  140. package/src/shared/hooks/gestures/use-build-gestures.tsx +23 -275
  141. package/src/shared/hooks/gestures/use-screen-gesture-handlers.ts +434 -0
  142. package/src/shared/hooks/gestures/use-scroll-registry.tsx +52 -1
  143. package/src/shared/hooks/lifecycle/use-close-transition.ts +3 -3
  144. package/src/shared/hooks/lifecycle/use-open-transition.ts +27 -3
  145. package/src/shared/hooks/navigation/use-screen-state.tsx +59 -2
  146. package/src/shared/hooks/use-backdrop-pointer-events.ts +32 -0
  147. package/src/shared/providers/gestures.provider.tsx +3 -2
  148. package/src/shared/providers/screen/screen-composer.tsx +2 -2
  149. package/src/shared/types/animation.types.ts +39 -2
  150. package/src/shared/types/screen.types.ts +29 -0
  151. package/src/shared/utils/animation/{start-screen-transition.ts → animate-to-progress.ts} +23 -8
  152. package/src/shared/utils/gesture/check-gesture-activation.ts +129 -0
  153. package/src/shared/utils/gesture/determine-snap-target.ts +75 -0
  154. package/src/shared/utils/gesture/validate-snap-points.ts +37 -0
  155. package/src/shared/utils/gesture/velocity.ts +10 -0
  156. package/src/shared/utils/logger.ts +15 -0
  157. package/lib/commonjs/shared/components/root-transition-aware.js.map +0 -1
  158. package/lib/commonjs/shared/hooks/use-stack-pointer-events.js +0 -23
  159. package/lib/commonjs/shared/hooks/use-stack-pointer-events.js.map +0 -1
  160. package/lib/commonjs/shared/utils/animation/start-screen-transition.js.map +0 -1
  161. package/lib/module/shared/components/root-transition-aware.js +0 -48
  162. package/lib/module/shared/components/root-transition-aware.js.map +0 -1
  163. package/lib/module/shared/hooks/use-stack-pointer-events.js +0 -20
  164. package/lib/module/shared/hooks/use-stack-pointer-events.js.map +0 -1
  165. package/lib/module/shared/utils/animation/start-screen-transition.js.map +0 -1
  166. package/lib/typescript/shared/components/root-transition-aware.d.ts +0 -6
  167. package/lib/typescript/shared/components/root-transition-aware.d.ts.map +0 -1
  168. package/lib/typescript/shared/hooks/use-stack-pointer-events.d.ts +0 -10
  169. package/lib/typescript/shared/hooks/use-stack-pointer-events.d.ts.map +0 -1
  170. package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts +0 -13
  171. package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts.map +0 -1
  172. package/src/shared/components/root-transition-aware.tsx +0 -49
  173. package/src/shared/hooks/use-stack-pointer-events.ts +0 -15
@@ -0,0 +1,125 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { validateSnapPoints } from "../utils/gesture/validate-snap-points";
3
+
4
+ describe("validateSnapPoints", () => {
5
+ describe("when no snap points provided", () => {
6
+ it("returns hasSnapPoints false", () => {
7
+ const result = validateSnapPoints({});
8
+ expect(result.hasSnapPoints).toBe(false);
9
+ });
10
+
11
+ it("returns empty snapPoints array", () => {
12
+ const result = validateSnapPoints({});
13
+ expect(result.snapPoints).toEqual([]);
14
+ });
15
+
16
+ it("returns -1 for min and max", () => {
17
+ const result = validateSnapPoints({});
18
+ expect(result.minSnapPoint).toBe(-1);
19
+ expect(result.maxSnapPoint).toBe(-1);
20
+ });
21
+
22
+ it("handles undefined snapPoints", () => {
23
+ const result = validateSnapPoints({ snapPoints: undefined });
24
+ expect(result.hasSnapPoints).toBe(false);
25
+ });
26
+ });
27
+
28
+ describe("when snap points provided", () => {
29
+ it("returns hasSnapPoints true", () => {
30
+ const result = validateSnapPoints({ snapPoints: [0.3, 0.6, 1] });
31
+ expect(result.hasSnapPoints).toBe(true);
32
+ });
33
+
34
+ it("sorts snap points in ascending order", () => {
35
+ const result = validateSnapPoints({ snapPoints: [1, 0.3, 0.6] });
36
+ expect(result.snapPoints).toEqual([0.3, 0.6, 1]);
37
+ });
38
+
39
+ it("returns correct max snap point", () => {
40
+ const result = validateSnapPoints({ snapPoints: [0.3, 0.6, 1] });
41
+ expect(result.maxSnapPoint).toBe(1);
42
+ });
43
+
44
+ it("handles single snap point", () => {
45
+ const result = validateSnapPoints({ snapPoints: [0.5] });
46
+ expect(result.hasSnapPoints).toBe(true);
47
+ expect(result.snapPoints).toEqual([0.5]);
48
+ expect(result.minSnapPoint).toBe(0.5);
49
+ expect(result.maxSnapPoint).toBe(0.5);
50
+ });
51
+
52
+ it("does not mutate original array", () => {
53
+ const original = [1, 0.3, 0.6];
54
+ validateSnapPoints({ snapPoints: original });
55
+ expect(original).toEqual([1, 0.3, 0.6]);
56
+ });
57
+ });
58
+
59
+ describe("canDismiss behavior", () => {
60
+ it("sets minSnapPoint to 0 when canDismiss is true", () => {
61
+ const result = validateSnapPoints({
62
+ snapPoints: [0.3, 0.6, 1],
63
+ canDismiss: true,
64
+ });
65
+ expect(result.minSnapPoint).toBe(0);
66
+ });
67
+
68
+ it("sets minSnapPoint to first snap point when canDismiss is false", () => {
69
+ const result = validateSnapPoints({
70
+ snapPoints: [0.3, 0.6, 1],
71
+ canDismiss: false,
72
+ });
73
+ expect(result.minSnapPoint).toBe(0.3);
74
+ });
75
+
76
+ it("defaults canDismiss to falsy (minSnapPoint = first snap)", () => {
77
+ const result = validateSnapPoints({ snapPoints: [0.3, 0.6, 1] });
78
+ expect(result.minSnapPoint).toBe(0.3);
79
+ });
80
+
81
+ it("handles canDismiss with unsorted snap points", () => {
82
+ const result = validateSnapPoints({
83
+ snapPoints: [1, 0.5, 0.25],
84
+ canDismiss: false,
85
+ });
86
+ // After sorting: [0.25, 0.5, 1]
87
+ expect(result.minSnapPoint).toBe(0.25);
88
+ expect(result.maxSnapPoint).toBe(1);
89
+ });
90
+ });
91
+
92
+ describe("edge cases", () => {
93
+ it("handles empty array", () => {
94
+ const result = validateSnapPoints({ snapPoints: [] });
95
+ // Empty array is falsy for snap points
96
+ expect(result.hasSnapPoints).toBe(true); // Array exists but is empty
97
+ expect(result.snapPoints).toEqual([]);
98
+ });
99
+
100
+ it("handles snap points at 0", () => {
101
+ const result = validateSnapPoints({
102
+ snapPoints: [0, 0.5, 1],
103
+ canDismiss: true,
104
+ });
105
+ expect(result.minSnapPoint).toBe(0);
106
+ expect(result.snapPoints).toEqual([0, 0.5, 1]);
107
+ });
108
+
109
+ it("handles duplicate snap points", () => {
110
+ const result = validateSnapPoints({ snapPoints: [0.5, 0.5, 1] });
111
+ expect(result.snapPoints).toEqual([0.5, 0.5, 1]);
112
+ });
113
+
114
+ it("handles very small snap point values", () => {
115
+ const result = validateSnapPoints({ snapPoints: [0.01, 0.1, 0.5] });
116
+ expect(result.minSnapPoint).toBe(0.01);
117
+ expect(result.maxSnapPoint).toBe(0.5);
118
+ });
119
+
120
+ it("handles snap points greater than 1", () => {
121
+ const result = validateSnapPoints({ snapPoints: [0.5, 1, 1.5] });
122
+ expect(result.maxSnapPoint).toBe(1.5);
123
+ });
124
+ });
125
+ });
@@ -27,10 +27,18 @@ export function createTransitionAwareComponent<P extends object>(
27
27
  TransitionAwareProps<P>
28
28
  >((props: any, ref) => {
29
29
  const { nativeGesture } = useGestureContext()!;
30
- const { scrollHandler, onContentSizeChange, onLayout } = useScrollRegistry({
30
+ const {
31
+ scrollHandler,
32
+ onContentSizeChange,
33
+ onLayout,
34
+ onTouchStart,
35
+ onTouchEnd,
36
+ } = useScrollRegistry({
31
37
  onScroll: props.onScroll,
32
38
  onContentSizeChange: props.onContentSizeChange,
33
39
  onLayout: props.onLayout,
40
+ onTouchStart: props.onTouchStart,
41
+ onTouchEnd: props.onTouchEnd,
34
42
  });
35
43
 
36
44
  return (
@@ -41,6 +49,8 @@ export function createTransitionAwareComponent<P extends object>(
41
49
  onScroll={scrollHandler}
42
50
  onContentSizeChange={onContentSizeChange}
43
51
  onLayout={onLayout}
52
+ onTouchStart={onTouchStart}
53
+ onTouchEnd={onTouchEnd}
44
54
  scrollEventThrottle={props.scrollEventThrottle || 16}
45
55
  />
46
56
  </GestureDetector>
@@ -0,0 +1,65 @@
1
+ import { StackActions } from "@react-navigation/native";
2
+ import { memo, useCallback } from "react";
3
+ import { Pressable, StyleSheet, View } from "react-native";
4
+ import Animated, { useAnimatedStyle } from "react-native-reanimated";
5
+ import { NO_STYLES } from "../constants";
6
+ import { useBackdropPointerEvents } from "../hooks/use-backdrop-pointer-events";
7
+ import { useKeys } from "../providers/screen/keys.provider";
8
+ import { useScreenStyles } from "../providers/screen/styles.provider";
9
+
10
+ type Props = {
11
+ children: React.ReactNode;
12
+ };
13
+
14
+ export const ScreenContainer = memo(({ children }: Props) => {
15
+ const { stylesMap } = useScreenStyles();
16
+ const { current } = useKeys();
17
+ const { pointerEvents, backdropBehavior } = useBackdropPointerEvents();
18
+
19
+ const isDismissable = backdropBehavior === "dismiss";
20
+
21
+ const handleBackdropPress = useCallback(() => {
22
+ current.navigation.dispatch(StackActions.pop());
23
+ }, [current.navigation]);
24
+
25
+ const animatedContentStyle = useAnimatedStyle(() => {
26
+ "worklet";
27
+ return stylesMap.value.contentStyle || NO_STYLES;
28
+ });
29
+
30
+ const animatedBackdropStyle = useAnimatedStyle(() => {
31
+ "worklet";
32
+ return (
33
+ stylesMap.value.backdropStyle ?? stylesMap.value.overlayStyle ?? NO_STYLES
34
+ );
35
+ });
36
+
37
+ return (
38
+ <View style={styles.container} pointerEvents={pointerEvents}>
39
+ <Pressable
40
+ style={StyleSheet.absoluteFillObject}
41
+ pointerEvents={isDismissable ? "auto" : "none"}
42
+ onPress={isDismissable ? handleBackdropPress : undefined}
43
+ >
44
+ <Animated.View
45
+ style={[StyleSheet.absoluteFillObject, animatedBackdropStyle]}
46
+ />
47
+ </Pressable>
48
+ <Animated.View
49
+ style={[styles.content, animatedContentStyle]}
50
+ pointerEvents={isDismissable ? "box-none" : pointerEvents}
51
+ >
52
+ {children}
53
+ </Animated.View>
54
+ </View>
55
+ );
56
+ });
57
+
58
+ const styles = StyleSheet.create({
59
+ container: {
60
+ flex: 1,
61
+ },
62
+ content: {
63
+ flex: 1,
64
+ },
65
+ });
@@ -208,7 +208,7 @@ export const ElasticCard = (
208
208
  contentStyle: {
209
209
  transform: [{ scale }, { translateX }, { translateY }],
210
210
  },
211
- overlayStyle: {
211
+ backdropStyle: {
212
212
  backgroundColor: !next ? overlayColor : "rgba(0,0,0,0)",
213
213
  },
214
214
  };
@@ -281,7 +281,7 @@ export const SharedIGImage = ({
281
281
  });
282
282
 
283
283
  return {
284
- overlayStyle: {
284
+ backdropStyle: {
285
285
  backgroundColor: "black",
286
286
  opacity: interpolate(progress, [0, 1], [0, 0.5]),
287
287
  },
@@ -634,7 +634,7 @@ export const SharedXImage = ({
634
634
  transform: [{ translateY: contentY }, { translateY: dragY }],
635
635
  pointerEvents: current.animating ? "none" : "auto",
636
636
  },
637
- overlayStyle: {
637
+ backdropStyle: {
638
638
  backgroundColor: overlayClr,
639
639
  },
640
640
  };
@@ -8,3 +8,9 @@ export const DefaultSpec: WithSpringConfig = {
8
8
  // @ts-expect-error
9
9
  restSpeedThreshold: 0.02,
10
10
  };
11
+
12
+ export const DefaultSnapSpec: WithSpringConfig = {
13
+ stiffness: 500,
14
+ damping: 50,
15
+ mass: 1,
16
+ };
@@ -4,6 +4,7 @@ import type { MeasuredDimensions } from "react-native-reanimated";
4
4
  import type { ScreenTransitionState } from "./types/animation.types";
5
5
  import type { ActivationArea } from "./types/gesture.types";
6
6
  import type { Layout } from "./types/screen.types";
7
+ import type { BaseStackRoute } from "./types/stack.types";
7
8
 
8
9
  /**
9
10
  * Masked view integration
@@ -16,6 +17,35 @@ export const CONTAINER_STYLE_ID = "_ROOT_CONTAINER";
16
17
  */
17
18
  export const NO_STYLES = Object.freeze({});
18
19
 
20
+ /**
21
+ * Default gesture values
22
+ */
23
+ const DEFAULT_GESTURE_VALUES = {
24
+ x: 0,
25
+ y: 0,
26
+ normalizedX: 0,
27
+ normalizedY: 0,
28
+ isDismissing: 0,
29
+ isDragging: 0,
30
+ direction: null,
31
+ } as const;
32
+
33
+ /**
34
+ * Creates a new screen transition state object
35
+ */
36
+ export const createScreenTransitionState = (
37
+ route: BaseStackRoute,
38
+ meta?: Record<string, unknown>,
39
+ ): ScreenTransitionState => ({
40
+ progress: 0,
41
+ closing: 0,
42
+ animating: 0,
43
+ entering: 1,
44
+ gesture: { ...DEFAULT_GESTURE_VALUES },
45
+ route,
46
+ meta,
47
+ });
48
+
19
49
  /**
20
50
  * Default screen transition state
21
51
  */
@@ -25,15 +55,7 @@ export const DEFAULT_SCREEN_TRANSITION_STATE: ScreenTransitionState =
25
55
  closing: 0,
26
56
  animating: 0,
27
57
  entering: 1,
28
- gesture: {
29
- x: 0,
30
- y: 0,
31
- normalizedX: 0,
32
- normalizedY: 0,
33
- isDismissing: 0,
34
- isDragging: 0,
35
- direction: null,
36
- },
58
+ gesture: DEFAULT_GESTURE_VALUES,
37
59
  route: {} as RouteProp<ParamListBase>,
38
60
  });
39
61
 
@@ -79,3 +101,8 @@ export const IS_WEB = Platform.OS === "web";
79
101
 
80
102
  export const TRUE = 1;
81
103
  export const FALSE = 0;
104
+
105
+ /**
106
+ * Small value for floating-point comparisons to handle animation/interpolation imprecision
107
+ */
108
+ export const EPSILON = 1e-5;
@@ -3,7 +3,10 @@ import { useWindowDimensions } from "react-native";
3
3
  import { type SharedValue, useDerivedValue } from "react-native-reanimated";
4
4
  import { useSafeAreaInsets } from "react-native-safe-area-context";
5
5
  import type { NativeStackScreenTransitionConfig } from "../../../native-stack/types";
6
- import { DEFAULT_SCREEN_TRANSITION_STATE } from "../../constants";
6
+ import {
7
+ createScreenTransitionState,
8
+ DEFAULT_SCREEN_TRANSITION_STATE,
9
+ } from "../../constants";
7
10
  import {
8
11
  type BaseDescriptor,
9
12
  useKeys,
@@ -31,26 +34,26 @@ type BuiltState = {
31
34
  unwrapped: ScreenTransitionState;
32
35
  };
33
36
 
34
- const createScreenTransitionState = (
35
- route: BaseStackRoute,
36
- meta?: Record<string, unknown>,
37
- ): ScreenTransitionState => ({
38
- progress: 0,
39
- closing: 0,
40
- animating: 0,
41
- entering: 1,
42
- gesture: {
43
- x: 0,
44
- y: 0,
45
- normalizedX: 0,
46
- normalizedY: 0,
47
- isDismissing: 0,
48
- isDragging: 0,
49
- direction: null,
50
- },
51
- route,
52
- meta,
53
- });
37
+ /**
38
+ * Computes the animated snap index based on progress and snap points.
39
+ * Returns -1 if no snap points, otherwise interpolates between indices.
40
+ */
41
+ const computeSnapIndex = (progress: number, snapPoints: number[]): number => {
42
+ "worklet";
43
+ if (snapPoints.length === 0) return -1;
44
+ if (progress <= snapPoints[0]) return 0;
45
+ if (progress >= snapPoints[snapPoints.length - 1])
46
+ return snapPoints.length - 1;
47
+
48
+ for (let i = 0; i < snapPoints.length - 1; i++) {
49
+ if (progress <= snapPoints[i + 1]) {
50
+ const t =
51
+ (progress - snapPoints[i]) / (snapPoints[i + 1] - snapPoints[i]);
52
+ return i + t;
53
+ }
54
+ }
55
+ return snapPoints.length - 1;
56
+ };
54
57
 
55
58
  const unwrapInto = (s: BuiltState): ScreenTransitionState => {
56
59
  "worklet";
@@ -123,6 +126,11 @@ export function _useScreenAnimation() {
123
126
  const currentRouteKey = currentDescriptor?.route?.key;
124
127
  const currentIndex = routeKeys.indexOf(currentRouteKey);
125
128
 
129
+ const sortedSnapPoints = useMemo(() => {
130
+ const points = currentDescriptor?.options?.snapPoints;
131
+ return points ? [...points].sort((a, b) => a - b) : [];
132
+ }, [currentDescriptor?.options?.snapPoints]);
133
+
126
134
  const screenInterpolatorProps = useDerivedValue<
127
135
  Omit<ScreenInterpolationProps, "bounds">
128
136
  >(() => {
@@ -152,6 +160,8 @@ export function _useScreenAnimation() {
152
160
  const stackProgress =
153
161
  currentIndex >= 0 ? rootStackProgress.value - currentIndex : progress;
154
162
 
163
+ const snapIndex = computeSnapIndex(current.progress, sortedSnapPoints);
164
+
155
165
  return {
156
166
  layouts: { screen: dimensions },
157
167
  insets,
@@ -160,6 +170,7 @@ export function _useScreenAnimation() {
160
170
  next,
161
171
  progress,
162
172
  stackProgress,
173
+ snapIndex,
163
174
  ...helpers,
164
175
  };
165
176
  });