react-native-screen-transitions 2.3.1 → 2.3.3

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 (217) hide show
  1. package/README.md +50 -12
  2. package/lib/commonjs/__configs__/index.js.map +1 -0
  3. package/lib/commonjs/{configs → __configs__}/presets.js +7 -3
  4. package/lib/commonjs/__configs__/presets.js.map +1 -0
  5. package/lib/commonjs/{configs → __configs__}/specs.js +1 -0
  6. package/lib/commonjs/__configs__/specs.js.map +1 -0
  7. package/lib/commonjs/components/controllers/screen-lifecycle.js +5 -5
  8. package/lib/commonjs/components/controllers/screen-lifecycle.js.map +1 -1
  9. package/lib/commonjs/components/create-transition-aware-component.js +13 -32
  10. package/lib/commonjs/components/create-transition-aware-component.js.map +1 -1
  11. package/lib/commonjs/components/integrations/masked-view.js +3 -4
  12. package/lib/commonjs/components/integrations/masked-view.js.map +1 -1
  13. package/lib/commonjs/components/root-transition-aware.js +3 -3
  14. package/lib/commonjs/components/root-transition-aware.js.map +1 -1
  15. package/lib/commonjs/constants.js +103 -0
  16. package/lib/commonjs/constants.js.map +1 -0
  17. package/lib/commonjs/hooks/animation/use-associated-style.js +3 -4
  18. package/lib/commonjs/hooks/animation/use-associated-style.js.map +1 -1
  19. package/lib/commonjs/hooks/animation/use-screen-animation.js +6 -28
  20. package/lib/commonjs/hooks/animation/use-screen-animation.js.map +1 -1
  21. package/lib/commonjs/hooks/bounds/use-bound-registry.js +92 -31
  22. package/lib/commonjs/hooks/bounds/use-bound-registry.js.map +1 -1
  23. package/lib/commonjs/hooks/gestures/use-build-gestures.js +27 -41
  24. package/lib/commonjs/hooks/gestures/use-build-gestures.js.map +1 -1
  25. package/lib/commonjs/index.js +3 -3
  26. package/lib/commonjs/index.js.map +1 -1
  27. package/lib/commonjs/integrations/native-stack/utils/debounce.js.map +1 -1
  28. package/lib/commonjs/integrations/native-stack/utils/useAnimatedHeaderHeight.js.map +1 -1
  29. package/lib/commonjs/integrations/native-stack/utils/useDismissedRouteError.js.map +1 -1
  30. package/lib/commonjs/integrations/native-stack/views/FontProcessor.js +1 -1
  31. package/lib/commonjs/integrations/native-stack/views/FontProcessor.js.map +1 -1
  32. package/lib/commonjs/integrations/native-stack/views/FontProcessor.native.js +1 -1
  33. package/lib/commonjs/integrations/native-stack/views/FontProcessor.native.js.map +1 -1
  34. package/lib/commonjs/integrations/native-stack/views/NativeStackView.native.js +2 -2
  35. package/lib/commonjs/integrations/native-stack/views/NativeStackView.native.js.map +1 -1
  36. package/lib/commonjs/providers/gestures.js +1 -9
  37. package/lib/commonjs/providers/gestures.js.map +1 -1
  38. package/lib/commonjs/{components/screen-transition-tree.js → providers/screen-transition-provider.js} +5 -5
  39. package/lib/commonjs/providers/screen-transition-provider.js.map +1 -0
  40. package/lib/commonjs/providers/transition-styles.js +4 -4
  41. package/lib/commonjs/providers/transition-styles.js.map +1 -1
  42. package/lib/commonjs/utils/animation/start-screen-transition.js +54 -0
  43. package/lib/commonjs/utils/animation/start-screen-transition.js.map +1 -0
  44. package/lib/commonjs/utils/bounds/index.js +6 -18
  45. package/lib/commonjs/utils/bounds/index.js.map +1 -1
  46. package/lib/commonjs/utils/gesture/apply-offset-rules.js +12 -21
  47. package/lib/commonjs/utils/gesture/apply-offset-rules.js.map +1 -1
  48. package/lib/commonjs/utils/gesture/determine-dismissal.js +7 -55
  49. package/lib/commonjs/utils/gesture/determine-dismissal.js.map +1 -1
  50. package/lib/commonjs/utils/gesture/reset-gesture-values.js +46 -0
  51. package/lib/commonjs/utils/gesture/reset-gesture-values.js.map +1 -0
  52. package/lib/commonjs/utils/gesture/velocity.js +89 -0
  53. package/lib/commonjs/utils/gesture/velocity.js.map +1 -0
  54. package/lib/commonjs/utils/reanimated/version.js +12 -0
  55. package/lib/commonjs/utils/reanimated/version.js.map +1 -0
  56. package/lib/module/__configs__/index.js.map +1 -0
  57. package/lib/module/{configs → __configs__}/presets.js +7 -3
  58. package/lib/module/__configs__/presets.js.map +1 -0
  59. package/lib/module/{configs → __configs__}/specs.js +1 -0
  60. package/lib/module/__configs__/specs.js.map +1 -0
  61. package/lib/module/components/controllers/screen-lifecycle.js +5 -5
  62. package/lib/module/components/controllers/screen-lifecycle.js.map +1 -1
  63. package/lib/module/components/create-transition-aware-component.js +13 -32
  64. package/lib/module/components/create-transition-aware-component.js.map +1 -1
  65. package/lib/module/components/integrations/masked-view.js +1 -2
  66. package/lib/module/components/integrations/masked-view.js.map +1 -1
  67. package/lib/module/components/root-transition-aware.js +3 -3
  68. package/lib/module/components/root-transition-aware.js.map +1 -1
  69. package/lib/module/constants.js +98 -0
  70. package/lib/module/constants.js.map +1 -0
  71. package/lib/module/hooks/animation/use-associated-style.js +3 -4
  72. package/lib/module/hooks/animation/use-associated-style.js.map +1 -1
  73. package/lib/module/hooks/animation/use-screen-animation.js +6 -28
  74. package/lib/module/hooks/animation/use-screen-animation.js.map +1 -1
  75. package/lib/module/hooks/bounds/use-bound-registry.js +93 -33
  76. package/lib/module/hooks/bounds/use-bound-registry.js.map +1 -1
  77. package/lib/module/hooks/gestures/use-build-gestures.js +22 -36
  78. package/lib/module/hooks/gestures/use-build-gestures.js.map +1 -1
  79. package/lib/module/index.js +1 -1
  80. package/lib/module/index.js.map +1 -1
  81. package/lib/module/integrations/native-stack/utils/debounce.js.map +1 -1
  82. package/lib/module/integrations/native-stack/utils/useAnimatedHeaderHeight.js +1 -1
  83. package/lib/module/integrations/native-stack/utils/useAnimatedHeaderHeight.js.map +1 -1
  84. package/lib/module/integrations/native-stack/utils/useDismissedRouteError.js +1 -1
  85. package/lib/module/integrations/native-stack/utils/useDismissedRouteError.js.map +1 -1
  86. package/lib/module/integrations/native-stack/views/FontProcessor.js +1 -1
  87. package/lib/module/integrations/native-stack/views/FontProcessor.js.map +1 -1
  88. package/lib/module/integrations/native-stack/views/FontProcessor.native.js +2 -2
  89. package/lib/module/integrations/native-stack/views/FontProcessor.native.js.map +1 -1
  90. package/lib/module/integrations/native-stack/views/NativeStackView.native.js +2 -2
  91. package/lib/module/integrations/native-stack/views/NativeStackView.native.js.map +1 -1
  92. package/lib/module/providers/gestures.js +0 -8
  93. package/lib/module/providers/gestures.js.map +1 -1
  94. package/lib/module/{components/screen-transition-tree.js → providers/screen-transition-provider.js} +4 -4
  95. package/lib/module/providers/screen-transition-provider.js.map +1 -0
  96. package/lib/module/providers/transition-styles.js +4 -4
  97. package/lib/module/providers/transition-styles.js.map +1 -1
  98. package/lib/module/utils/animation/{run-transition.js → start-screen-transition.js} +15 -13
  99. package/lib/module/utils/animation/start-screen-transition.js.map +1 -0
  100. package/lib/module/utils/bounds/index.js +5 -17
  101. package/lib/module/utils/bounds/index.js.map +1 -1
  102. package/lib/module/utils/gesture/apply-offset-rules.js +1 -10
  103. package/lib/module/utils/gesture/apply-offset-rules.js.map +1 -1
  104. package/lib/module/utils/gesture/determine-dismissal.js +7 -55
  105. package/lib/module/utils/gesture/determine-dismissal.js.map +1 -1
  106. package/lib/module/utils/gesture/reset-gesture-values.js +41 -0
  107. package/lib/module/utils/gesture/reset-gesture-values.js.map +1 -0
  108. package/lib/module/utils/gesture/velocity.js +85 -0
  109. package/lib/module/utils/gesture/velocity.js.map +1 -0
  110. package/lib/module/utils/reanimated/version.js +7 -0
  111. package/lib/module/utils/reanimated/version.js.map +1 -0
  112. package/lib/typescript/{configs → __configs__}/index.d.ts +1 -1
  113. package/lib/typescript/__configs__/index.d.ts.map +1 -0
  114. package/lib/typescript/__configs__/presets.d.ts.map +1 -0
  115. package/lib/typescript/__configs__/specs.d.ts.map +1 -0
  116. package/lib/typescript/components/create-transition-aware-component.d.ts +14 -2
  117. package/lib/typescript/components/create-transition-aware-component.d.ts.map +1 -1
  118. package/lib/typescript/components/integrations/masked-view.d.ts.map +1 -1
  119. package/lib/typescript/components/root-transition-aware.d.ts.map +1 -1
  120. package/lib/typescript/constants.d.ts +57 -0
  121. package/lib/typescript/constants.d.ts.map +1 -0
  122. package/lib/typescript/hooks/animation/use-associated-style.d.ts.map +1 -1
  123. package/lib/typescript/hooks/animation/use-screen-animation.d.ts +1 -1
  124. package/lib/typescript/hooks/animation/use-screen-animation.d.ts.map +1 -1
  125. package/lib/typescript/hooks/bounds/use-bound-registry.d.ts +8 -9
  126. package/lib/typescript/hooks/bounds/use-bound-registry.d.ts.map +1 -1
  127. package/lib/typescript/hooks/gestures/use-build-gestures.d.ts.map +1 -1
  128. package/lib/typescript/index.d.ts +100 -68
  129. package/lib/typescript/index.d.ts.map +1 -1
  130. package/lib/typescript/integrations/native-stack/utils/debounce.d.ts.map +1 -1
  131. package/lib/typescript/integrations/native-stack/utils/useAnimatedHeaderHeight.d.ts +2 -2
  132. package/lib/typescript/integrations/native-stack/utils/useDismissedRouteError.d.ts +2 -2
  133. package/lib/typescript/integrations/native-stack/utils/useDismissedRouteError.d.ts.map +1 -1
  134. package/lib/typescript/integrations/native-stack/views/FontProcessor.d.ts.map +1 -1
  135. package/lib/typescript/integrations/native-stack/views/FontProcessor.native.d.ts.map +1 -1
  136. package/lib/typescript/providers/gestures.d.ts +0 -1
  137. package/lib/typescript/providers/gestures.d.ts.map +1 -1
  138. package/lib/typescript/{components/screen-transition-tree.d.ts → providers/screen-transition-provider.d.ts} +2 -2
  139. package/lib/typescript/providers/screen-transition-provider.d.ts.map +1 -0
  140. package/lib/typescript/providers/transition-styles.d.ts.map +1 -1
  141. package/lib/typescript/types/core.d.ts +0 -8
  142. package/lib/typescript/types/core.d.ts.map +1 -1
  143. package/lib/typescript/utils/animation/start-screen-transition.d.ts +13 -0
  144. package/lib/typescript/utils/animation/start-screen-transition.d.ts.map +1 -0
  145. package/lib/typescript/utils/bounds/_types/get-bounds.d.ts +2 -2
  146. package/lib/typescript/utils/bounds/_types/get-bounds.d.ts.map +1 -1
  147. package/lib/typescript/utils/bounds/index.d.ts.map +1 -1
  148. package/lib/typescript/utils/gesture/apply-offset-rules.d.ts.map +1 -1
  149. package/lib/typescript/utils/gesture/determine-dismissal.d.ts +0 -1
  150. package/lib/typescript/utils/gesture/determine-dismissal.d.ts.map +1 -1
  151. package/lib/typescript/utils/gesture/reset-gesture-values.d.ts +16 -0
  152. package/lib/typescript/utils/gesture/reset-gesture-values.d.ts.map +1 -0
  153. package/lib/typescript/utils/gesture/velocity.d.ts +25 -0
  154. package/lib/typescript/utils/gesture/velocity.d.ts.map +1 -0
  155. package/lib/typescript/utils/reanimated/version.d.ts +2 -0
  156. package/lib/typescript/utils/reanimated/version.d.ts.map +1 -0
  157. package/package.json +3 -3
  158. package/src/{configs → __configs__}/presets.ts +7 -1
  159. package/src/{configs → __configs__}/specs.ts +1 -0
  160. package/src/components/controllers/screen-lifecycle.tsx +5 -5
  161. package/src/components/create-transition-aware-component.tsx +23 -38
  162. package/src/components/integrations/masked-view.tsx +1 -3
  163. package/src/components/root-transition-aware.tsx +4 -8
  164. package/src/constants.ts +106 -0
  165. package/src/hooks/animation/use-associated-style.tsx +3 -4
  166. package/src/hooks/animation/use-screen-animation.tsx +15 -32
  167. package/src/hooks/bounds/use-bound-registry.tsx +112 -25
  168. package/src/hooks/gestures/use-build-gestures.tsx +31 -25
  169. package/src/index.ts +1 -1
  170. package/src/integrations/native-stack/utils/debounce.tsx +9 -9
  171. package/src/integrations/native-stack/utils/useAnimatedHeaderHeight.tsx +10 -10
  172. package/src/integrations/native-stack/utils/useDismissedRouteError.tsx +21 -21
  173. package/src/integrations/native-stack/views/FontProcessor.native.tsx +7 -7
  174. package/src/integrations/native-stack/views/FontProcessor.tsx +2 -2
  175. package/src/integrations/native-stack/views/NativeStackView.native.tsx +4 -4
  176. package/src/providers/gestures.tsx +0 -9
  177. package/src/{components/screen-transition-tree.tsx → providers/screen-transition-provider.tsx} +3 -3
  178. package/src/providers/transition-styles.tsx +6 -5
  179. package/src/types/core.ts +0 -9
  180. package/src/utils/animation/start-screen-transition.ts +61 -0
  181. package/src/utils/bounds/_types/get-bounds.ts +7 -7
  182. package/src/utils/bounds/index.ts +12 -19
  183. package/src/utils/gesture/apply-offset-rules.ts +9 -11
  184. package/src/utils/gesture/determine-dismissal.ts +18 -69
  185. package/src/utils/gesture/reset-gesture-values.ts +48 -0
  186. package/src/utils/gesture/velocity.ts +144 -0
  187. package/src/utils/reanimated/version.ts +5 -0
  188. package/lib/commonjs/components/bound-capture.js +0 -38
  189. package/lib/commonjs/components/bound-capture.js.map +0 -1
  190. package/lib/commonjs/components/screen-transition-tree.js.map +0 -1
  191. package/lib/commonjs/configs/index.js.map +0 -1
  192. package/lib/commonjs/configs/presets.js.map +0 -1
  193. package/lib/commonjs/configs/specs.js.map +0 -1
  194. package/lib/commonjs/utils/animation/run-transition.js +0 -52
  195. package/lib/commonjs/utils/animation/run-transition.js.map +0 -1
  196. package/lib/module/components/bound-capture.js +0 -33
  197. package/lib/module/components/bound-capture.js.map +0 -1
  198. package/lib/module/components/screen-transition-tree.js.map +0 -1
  199. package/lib/module/configs/index.js.map +0 -1
  200. package/lib/module/configs/presets.js.map +0 -1
  201. package/lib/module/configs/specs.js.map +0 -1
  202. package/lib/module/utils/animation/run-transition.js.map +0 -1
  203. package/lib/typescript/components/bound-capture.d.ts +0 -8
  204. package/lib/typescript/components/bound-capture.d.ts.map +0 -1
  205. package/lib/typescript/components/screen-transition-tree.d.ts.map +0 -1
  206. package/lib/typescript/configs/index.d.ts.map +0 -1
  207. package/lib/typescript/configs/presets.d.ts.map +0 -1
  208. package/lib/typescript/configs/specs.d.ts.map +0 -1
  209. package/lib/typescript/utils/animation/run-transition.d.ts +0 -12
  210. package/lib/typescript/utils/animation/run-transition.d.ts.map +0 -1
  211. package/src/components/bound-capture.tsx +0 -32
  212. package/src/utils/animation/run-transition.ts +0 -52
  213. /package/lib/commonjs/{configs → __configs__}/index.js +0 -0
  214. /package/lib/module/{configs → __configs__}/index.js +0 -0
  215. /package/lib/typescript/{configs → __configs__}/presets.d.ts +0 -0
  216. /package/lib/typescript/{configs → __configs__}/specs.d.ts +0 -0
  217. /package/src/{configs → __configs__}/index.ts +0 -0
@@ -1,72 +1,159 @@
1
- import { useCallback } from "react";
1
+ import {
2
+ createContext,
3
+ Fragment,
4
+ useCallback,
5
+ useContext,
6
+ useMemo,
7
+ } from "react";
2
8
  import type { View } from "react-native";
3
9
  import {
4
10
  type AnimatedRef,
5
11
  measure,
12
+ runOnJS,
13
+ runOnUI,
6
14
  type StyleProps,
15
+ useAnimatedReaction,
7
16
  useSharedValue,
8
17
  } from "react-native-reanimated";
18
+ import type { SharedValue } from "react-native-reanimated/lib/typescript/commonTypes";
9
19
  import { useKeys } from "../../providers/keys";
10
20
  import { Bounds } from "../../stores/bounds";
11
21
  import { flattenStyle } from "../../utils/bounds/_utils/flatten-styles";
12
22
  import { isBoundsEqual } from "../../utils/bounds/_utils/is-bounds-equal";
23
+ import useStableCallback from "../use-stable-callback";
13
24
 
14
25
  interface BoundMeasurerHookProps {
15
- sharedBoundTag: string;
26
+ sharedBoundTag?: string;
16
27
  animatedRef: AnimatedRef<View>;
17
- current: { route: { key: string } };
28
+
18
29
  style: StyleProps;
30
+ onPress?: ((...args: unknown[]) => void) | undefined;
31
+ }
32
+
33
+ interface MeasurementUpdateContextType {
34
+ updateSignal: SharedValue<number>;
19
35
  }
20
36
 
37
+ const MeasurementUpdateContext =
38
+ createContext<MeasurementUpdateContextType | null>(null);
39
+
21
40
  export const useBoundsRegistry = ({
22
41
  sharedBoundTag,
23
42
  animatedRef,
24
- current,
25
43
  style,
44
+ onPress,
26
45
  }: BoundMeasurerHookProps) => {
27
- const { previous } = useKeys();
46
+ const { previous, current } = useKeys();
28
47
 
29
- const isMeasured = useSharedValue(false);
48
+ const ROOT_MEASUREMENT_SIGNAL = useContext(MeasurementUpdateContext);
49
+ const ROOT_SIGNAL = useSharedValue(0);
50
+ const IS_ROOT = !ROOT_MEASUREMENT_SIGNAL;
51
+ const hasMeasured = useSharedValue(false);
30
52
 
31
- const measureBounds = useCallback(() => {
53
+ const emitUpdate = useCallback(() => {
32
54
  "worklet";
33
- if (!sharedBoundTag) return;
34
- const measured = measure(animatedRef);
35
- if (measured) {
55
+ if (IS_ROOT) ROOT_SIGNAL.value = ROOT_SIGNAL.value + 1;
56
+ }, [IS_ROOT, ROOT_SIGNAL]);
57
+
58
+ const maybeMeasureAndStore = useCallback(
59
+ ({
60
+ onPress,
61
+ skipMarkingActive,
62
+ }: {
63
+ onPress?: () => void;
64
+ skipMarkingActive?: boolean;
65
+ }) => {
66
+ "worklet";
67
+ if (!sharedBoundTag) return;
68
+
69
+ const measured = measure(animatedRef);
70
+
71
+ if (!measured) {
72
+ console.warn(
73
+ `[react-native-screen-transitions] measure() returned null for sharedBoundTag="${sharedBoundTag}"`,
74
+ );
75
+ return;
76
+ }
77
+
36
78
  const key = current.route.key;
79
+
37
80
  if (isBoundsEqual({ measured, key, sharedBoundTag })) {
38
- if (Bounds.getRouteActive(key) === sharedBoundTag) {
81
+ emitUpdate();
82
+ if (!skipMarkingActive) {
39
83
  Bounds.setRouteActive(key, sharedBoundTag);
40
84
  }
85
+ if (onPress) runOnJS(onPress)();
41
86
  return;
42
87
  }
43
88
 
89
+ emitUpdate();
90
+
44
91
  Bounds.setBounds(key, sharedBoundTag, measured, flattenStyle(style));
45
- if (Bounds.getRouteActive(key) === sharedBoundTag) {
92
+ if (!skipMarkingActive) {
46
93
  Bounds.setRouteActive(key, sharedBoundTag);
47
94
  }
48
- }
49
- }, [sharedBoundTag, animatedRef, current.route.key, style]);
50
95
 
51
- const handleLayout = useCallback(() => {
96
+ if (onPress) runOnJS(onPress)();
97
+ },
98
+ [sharedBoundTag, animatedRef, current.route.key, style, emitUpdate],
99
+ );
100
+
101
+ const handleTransitionLayout = useCallback(() => {
52
102
  "worklet";
53
- const previousRouteKey = previous?.route.key;
54
103
 
55
- if (!sharedBoundTag || isMeasured.value || !previousRouteKey) {
104
+ const prevKey = previous?.route.key;
105
+ if (!sharedBoundTag || hasMeasured.value || !prevKey) {
106
+ return;
107
+ }
108
+
109
+ const prevBounds = Bounds.getBounds(prevKey)?.[sharedBoundTag];
110
+
111
+ if (prevBounds) {
112
+ // Should skip mark active if we are in a transition
113
+ maybeMeasureAndStore({ skipMarkingActive: true });
114
+ // Should not measure again while in transition
115
+ hasMeasured.value = true;
116
+ }
117
+ }, [maybeMeasureAndStore, sharedBoundTag, previous?.route.key, hasMeasured]);
118
+
119
+ const captureActiveOnPress = useStableCallback(() => {
120
+ if (!sharedBoundTag) {
121
+ if (onPress) onPress();
56
122
  return;
57
123
  }
58
124
 
59
- const previousBounds = Bounds.getBounds(previousRouteKey);
60
- const hasPreviousBoundForTag = previousBounds[sharedBoundTag];
125
+ // In this case, we DO want to mark active
126
+ runOnUI(maybeMeasureAndStore)({ onPress });
127
+ });
61
128
 
62
- if (hasPreviousBoundForTag) {
63
- measureBounds();
64
- isMeasured.value = true;
129
+ const MeasurementSyncProvider = useMemo(() => {
130
+ if (!IS_ROOT || !sharedBoundTag) {
131
+ return Fragment;
65
132
  }
66
- }, [measureBounds, sharedBoundTag, previous?.route.key, isMeasured]);
133
+
134
+ return ({ children }: { children: React.ReactNode }) => (
135
+ <MeasurementUpdateContext.Provider value={{ updateSignal: ROOT_SIGNAL }}>
136
+ {children}
137
+ </MeasurementUpdateContext.Provider>
138
+ );
139
+ }, [IS_ROOT, sharedBoundTag, ROOT_SIGNAL]);
140
+
141
+ useAnimatedReaction(
142
+ () => ROOT_MEASUREMENT_SIGNAL?.updateSignal.value,
143
+ (current) => {
144
+ "worklet";
145
+
146
+ // We don't want to run on the initial amount)
147
+ if (current === 0 || current === undefined) return;
148
+
149
+ // Children should not have the ability to mark active
150
+ maybeMeasureAndStore({ skipMarkingActive: true });
151
+ },
152
+ );
67
153
 
68
154
  return {
69
- measureBounds,
70
- handleLayout,
155
+ handleTransitionLayout,
156
+ captureActiveOnPress,
157
+ MeasurementSyncProvider,
71
158
  };
72
159
  };
@@ -14,25 +14,27 @@ import {
14
14
  type SharedValue,
15
15
  useSharedValue,
16
16
  } from "react-native-reanimated";
17
+ import {
18
+ DEFAULT_GESTURE_ACTIVATION_AREA,
19
+ DEFAULT_GESTURE_DIRECTION,
20
+ DEFAULT_GESTURE_DRIVES_PROGRESS,
21
+ DEFAULT_GESTURE_ENABLED,
22
+ GESTURE_VELOCITY_IMPACT,
23
+ } from "../../constants";
17
24
  import type { ScrollConfig } from "../../providers/gestures";
18
25
  import { useKeys } from "../../providers/keys";
19
26
  import { Animations } from "../../stores/animations";
20
27
  import { Gestures } from "../../stores/gestures";
21
28
  import { NavigatorDismissState } from "../../stores/navigator-dismiss-state";
22
- import { type ActivationArea, GestureOffsetState } from "../../types/gesture";
23
- import { animate } from "../../utils/animation/animate";
24
- import { runTransition } from "../../utils/animation/run-transition";
29
+ import { GestureOffsetState } from "../../types/gesture";
30
+ import { startScreenTransition } from "../../utils/animation/start-screen-transition";
25
31
  import { applyOffsetRules } from "../../utils/gesture/check-gesture-activation";
26
32
  import { determineDismissal } from "../../utils/gesture/determine-dismissal";
27
33
  import { mapGestureToProgress } from "../../utils/gesture/map-gesture-to-progress";
34
+ import { resetGestureValues } from "../../utils/gesture/reset-gesture-values";
35
+ import { velocity } from "../../utils/gesture/velocity";
28
36
  import useStableCallback from "../use-stable-callback";
29
37
 
30
- const GESTURE_VELOCITY_IMPACT = 0.3;
31
- const DEFAULT_GESTURE_DIRECTION = "horizontal";
32
- const DEFAULT_GESTURE_ENABLED = false;
33
- const DEFAULT_GESTURE_DRIVES_PROGRESS = true;
34
- const DEFAULT_GESTURE_ACTIVATION_AREA: ActivationArea = "screen";
35
-
36
38
  interface BuildGesturesHookProps {
37
39
  scrollConfig: SharedValue<ScrollConfig | null>;
38
40
  }
@@ -50,6 +52,7 @@ export const useBuildGestures = ({
50
52
  x: 0,
51
53
  y: 0,
52
54
  });
55
+
53
56
  const gestureOffsetState = useSharedValue<GestureOffsetState>(
54
57
  GestureOffsetState.PENDING,
55
58
  );
@@ -267,7 +270,7 @@ export const useBuildGestures = ({
267
270
  (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
268
271
  "worklet";
269
272
 
270
- const { shouldDismiss, velocity } = determineDismissal({
273
+ const { shouldDismiss } = determineDismissal({
271
274
  event,
272
275
  directions,
273
276
  dimensions,
@@ -276,29 +279,32 @@ export const useBuildGestures = ({
276
279
 
277
280
  const spec = shouldDismiss ? transitionSpec?.close : transitionSpec?.open;
278
281
 
279
- gestures.isDismissing.value = Number(shouldDismiss);
280
-
281
- // Provide per-axis velocities so drag return can bounce naturally
282
- const vxPx = event.velocityX;
283
- const vyPx = event.velocityY;
284
- const vxNorm = vxPx / Math.max(1, dimensions.width);
285
- const vyNorm = vyPx / Math.max(1, dimensions.height);
286
- gestures.x.value = animate(0, { ...spec, velocity: vxPx });
287
- gestures.y.value = animate(0, { ...spec, velocity: vyPx });
288
- gestures.normalizedX.value = animate(0, { ...spec, velocity: vxNorm });
289
- gestures.normalizedY.value = animate(0, { ...spec, velocity: vyNorm });
290
- gestures.isDragging.value = 0;
282
+ resetGestureValues({
283
+ spec,
284
+ gestures,
285
+ shouldDismiss,
286
+ event,
287
+ dimensions,
288
+ });
291
289
 
292
290
  if (shouldDismiss) {
293
291
  runOnJS(setNavigatorDismissal)();
294
292
  }
295
293
 
296
- runTransition({
294
+ const initialVelocity = velocity.calculateProgressVelocity({
295
+ animations,
296
+ shouldDismiss,
297
+ event,
298
+ dimensions,
299
+ directions,
300
+ });
301
+
302
+ startScreenTransition({
297
303
  target: shouldDismiss ? "close" : "open",
298
- onFinish: shouldDismiss ? handleDismiss : undefined,
304
+ onAnimationFinish: shouldDismiss ? handleDismiss : undefined,
299
305
  spec: transitionSpec,
300
- velocity,
301
306
  animations,
307
+ initialVelocity,
302
308
  });
303
309
  },
304
310
  [
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { FlatList, Pressable, ScrollView, View } from "react-native";
2
+ import { presets, specs } from "./__configs__";
2
3
  import { createTransitionAwareComponent } from "./components/create-transition-aware-component";
3
4
  import MaskedView from "./components/integrations/masked-view";
4
- import { presets, specs } from "./configs";
5
5
 
6
6
  export default {
7
7
  View: createTransitionAwareComponent(View),
@@ -1,14 +1,14 @@
1
1
  export function debounce<T extends (...args: any[]) => void>(
2
- func: T,
3
- duration: number
2
+ func: T,
3
+ duration: number,
4
4
  ): T {
5
- let timeout: ReturnType<typeof setTimeout>;
5
+ let timeout: ReturnType<typeof setTimeout>;
6
6
 
7
- return function (this: unknown, ...args) {
8
- clearTimeout(timeout);
7
+ return function (this: unknown, ...args) {
8
+ clearTimeout(timeout);
9
9
 
10
- timeout = setTimeout(() => {
11
- func.apply(this, args);
12
- }, duration);
13
- } as T;
10
+ timeout = setTimeout(() => {
11
+ func.apply(this, args);
12
+ }, duration);
13
+ } as T;
14
14
  }
@@ -1,18 +1,18 @@
1
- import * as React from 'react';
2
- import type { Animated } from 'react-native';
1
+ import * as React from "react";
2
+ import type { Animated } from "react-native";
3
3
 
4
4
  export const AnimatedHeaderHeightContext = React.createContext<
5
- Animated.AnimatedInterpolation<number> | undefined
5
+ Animated.AnimatedInterpolation<number> | undefined
6
6
  >(undefined);
7
7
 
8
8
  export function useAnimatedHeaderHeight() {
9
- const animatedValue = React.useContext(AnimatedHeaderHeightContext);
9
+ const animatedValue = React.useContext(AnimatedHeaderHeightContext);
10
10
 
11
- if (animatedValue === undefined) {
12
- throw new Error(
13
- "Couldn't find the header height. Are you inside a screen in a native stack navigator?"
14
- );
15
- }
11
+ if (animatedValue === undefined) {
12
+ throw new Error(
13
+ "Couldn't find the header height. Are you inside a screen in a native stack navigator?",
14
+ );
15
+ }
16
16
 
17
- return animatedValue;
17
+ return animatedValue;
18
18
  }
@@ -1,30 +1,30 @@
1
1
  import type {
2
- ParamListBase,
3
- StackNavigationState,
4
- } from '@react-navigation/native';
5
- import * as React from 'react';
2
+ ParamListBase,
3
+ StackNavigationState,
4
+ } from "@react-navigation/native";
5
+ import * as React from "react";
6
6
 
7
7
  export function useDismissedRouteError(
8
- state: StackNavigationState<ParamListBase>
8
+ state: StackNavigationState<ParamListBase>,
9
9
  ) {
10
- const [nextDismissedKey, setNextDismissedKey] = React.useState<string | null>(
11
- null
12
- );
10
+ const [nextDismissedKey, setNextDismissedKey] = React.useState<string | null>(
11
+ null,
12
+ );
13
13
 
14
- const dismissedRouteName = nextDismissedKey
15
- ? state.routes.find((route) => route.key === nextDismissedKey)?.name
16
- : null;
14
+ const dismissedRouteName = nextDismissedKey
15
+ ? state.routes.find((route) => route.key === nextDismissedKey)?.name
16
+ : null;
17
17
 
18
- React.useEffect(() => {
19
- if (dismissedRouteName) {
20
- const message =
21
- `The screen '${dismissedRouteName}' was removed natively but didn't get removed from JS state. ` +
22
- `This can happen if the action was prevented in a 'beforeRemove' listener, which is not fully supported in native-stack.\n\n` +
23
- `Consider using a 'usePreventRemove' hook with 'headerBackButtonMenuEnabled: false' to prevent users from natively going back multiple screens.`;
18
+ React.useEffect(() => {
19
+ if (dismissedRouteName) {
20
+ const message =
21
+ `The screen '${dismissedRouteName}' was removed natively but didn't get removed from JS state. ` +
22
+ `This can happen if the action was prevented in a 'beforeRemove' listener, which is not fully supported in native-stack.\n\n` +
23
+ `Consider using a 'usePreventRemove' hook with 'headerBackButtonMenuEnabled: false' to prevent users from natively going back multiple screens.`;
24
24
 
25
- console.error(message);
26
- }
27
- }, [dismissedRouteName]);
25
+ console.error(message);
26
+ }
27
+ }, [dismissedRouteName]);
28
28
 
29
- return { setNextDismissedKey };
29
+ return { setNextDismissedKey };
30
30
  }
@@ -1,12 +1,12 @@
1
1
  // @ts-expect-error importing private module
2
- import ReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
2
+ import ReactNativeStyleAttributes from "react-native/Libraries/Components/View/ReactNativeStyleAttributes";
3
3
 
4
4
  export function processFonts(
5
- fontFamilies: (string | undefined)[]
5
+ fontFamilies: (string | undefined)[],
6
6
  ): (string | undefined)[] {
7
- const fontFamilyProcessor = ReactNativeStyleAttributes.fontFamily?.process;
8
- if (typeof fontFamilyProcessor === 'function') {
9
- return fontFamilies.map(fontFamilyProcessor);
10
- }
11
- return fontFamilies;
7
+ const fontFamilyProcessor = ReactNativeStyleAttributes.fontFamily?.process;
8
+ if (typeof fontFamilyProcessor === "function") {
9
+ return fontFamilies.map(fontFamilyProcessor);
10
+ }
11
+ return fontFamilies;
12
12
  }
@@ -1,5 +1,5 @@
1
1
  export function processFonts(
2
- _: (string | undefined)[]
2
+ _: (string | undefined)[],
3
3
  ): (string | undefined)[] {
4
- throw new Error('Not supported on Web');
4
+ throw new Error("Not supported on Web");
5
5
  }
@@ -32,7 +32,7 @@ import {
32
32
  ScreenStack,
33
33
  ScreenStackItem,
34
34
  } from "react-native-screens";
35
- import { ScreenTransitionTree } from "../../../components/screen-transition-tree";
35
+ import { ScreenTransitionProvider } from "../../../providers/screen-transition-provider";
36
36
  import type {
37
37
  NativeStackDescriptor,
38
38
  NativeStackDescriptorMap,
@@ -267,12 +267,12 @@ const SceneView = ({
267
267
  }, [canGoBack, backTitle]);
268
268
 
269
269
  const isRemovePrevented = preventedRoutes[route.key]?.preventRemove;
270
+
270
271
  const modifiedPresentation = enableTransitions
271
272
  ? "containedTransparentModal"
272
273
  : presentation === "card"
273
274
  ? "push"
274
275
  : presentation;
275
-
276
276
  const modifiedAnimation = enableTransitions ? "none" : animation;
277
277
  const modifiedHeaderShown =
278
278
  enableTransitions || header !== undefined ? false : headerShown;
@@ -465,13 +465,13 @@ const SceneView = ({
465
465
  value={isParentHeaderShown || headerShown !== false}
466
466
  >
467
467
  <HeaderBackContext.Provider value={headerBack}>
468
- <ScreenTransitionTree
468
+ <ScreenTransitionProvider
469
469
  previous={previousDescriptor}
470
470
  current={descriptor}
471
471
  next={nextDescriptor}
472
472
  >
473
473
  {render()}
474
- </ScreenTransitionTree>
474
+ </ScreenTransitionProvider>
475
475
  </HeaderBackContext.Provider>
476
476
  </HeaderShownContext.Provider>
477
477
  </HeaderHeightContext.Provider>
@@ -26,15 +26,6 @@ type ScreenGestureProviderProps = {
26
26
  children: React.ReactNode;
27
27
  };
28
28
 
29
- export const DEFAULT_SCROLL_CONFIG: ScrollConfig = {
30
- x: 0,
31
- y: 0,
32
- contentHeight: 0,
33
- contentWidth: 0,
34
- layoutHeight: 0,
35
- layoutWidth: 0,
36
- };
37
-
38
29
  const GestureContext = createContext<GestureContextType | undefined>(undefined);
39
30
 
40
31
  export const ScreenGestureProvider = ({
@@ -1,10 +1,10 @@
1
1
  import type React from "react";
2
+ import { ScreenLifecycleController } from "../components/controllers/screen-lifecycle";
3
+ import { RootTransitionAware } from "../components/root-transition-aware";
2
4
  import { ScreenGestureProvider } from "../providers/gestures";
3
5
  import { KeysProvider } from "../providers/keys";
4
6
  import { TransitionStylesProvider } from "../providers/transition-styles";
5
7
  import type { NativeStackDescriptor } from "../types/navigator";
6
- import { ScreenLifecycleController } from "./controllers/screen-lifecycle";
7
- import { RootTransitionAware } from "./root-transition-aware";
8
8
 
9
9
  type Props = {
10
10
  previous?: NativeStackDescriptor;
@@ -13,7 +13,7 @@ type Props = {
13
13
  children: React.ReactNode;
14
14
  };
15
15
 
16
- export function ScreenTransitionTree({
16
+ export function ScreenTransitionProvider({
17
17
  previous,
18
18
  current,
19
19
  next,
@@ -1,5 +1,6 @@
1
1
  import { createContext, useContext, useMemo } from "react";
2
2
  import { isWorkletFunction, useDerivedValue } from "react-native-reanimated";
3
+ import { NO_STYLES } from "../constants";
3
4
  import { _useScreenAnimation } from "../hooks/animation/use-screen-animation";
4
5
  import type { TransitionInterpolatedStyle } from "../types/animation";
5
6
 
@@ -7,8 +8,6 @@ type Props = {
7
8
  children: React.ReactNode;
8
9
  };
9
10
 
10
- const EMPTY_MAP = Object.freeze({});
11
-
12
11
  const TransitionStylesContext = createContext<ReturnType<
13
12
  typeof useMemo<{
14
13
  stylesMap: ReturnType<typeof useDerivedValue<TransitionInterpolatedStyle>>;
@@ -25,13 +24,15 @@ export function TransitionStylesProvider({ children }: Props) {
25
24
  "worklet";
26
25
 
27
26
  if (screenStyleInterpolator && !isFunctionWorklet && __DEV__) {
28
- console.warn("screenStyleInterpolator is not a worklet function");
29
- return EMPTY_MAP;
27
+ console.warn(
28
+ `[react-native-screen-transitions] screenStyleInterpolator is not a worklet function`,
29
+ );
30
+ return NO_STYLES;
30
31
  }
31
32
 
32
33
  return screenStyleInterpolator
33
34
  ? screenStyleInterpolator(screenInterpolatorProps.value)
34
- : EMPTY_MAP;
35
+ : NO_STYLES;
35
36
  });
36
37
 
37
38
  const value = useMemo(() => {
package/src/types/core.ts CHANGED
@@ -42,15 +42,6 @@ export type TransitionAwareProps<T extends object> = AnimatedProps<T> & {
42
42
  * </Transition.View>
43
43
  */
44
44
  sharedBoundTag?: string;
45
-
46
- /**
47
- * Eagerly measure this component on layout and store the result in the
48
- * Bounds registry. Useful for nested shared elements that may not receive
49
- * the press event but still need up-to-date measurements at navigation time.
50
- *
51
- * Only has an effect when used together with `sharedBoundTag`.
52
- */
53
- measureOnLayout?: boolean;
54
45
  };
55
46
 
56
47
  export type TransitionConfig = {
@@ -0,0 +1,61 @@
1
+ import { runOnJS } from "react-native-reanimated";
2
+ import type { AnimationMap } from "../../stores/animations";
3
+ import type { TransitionSpec } from "../../types/animation";
4
+ import { animate } from "./animate";
5
+
6
+ interface StartScreenTransitionProps {
7
+ target: "open" | "close";
8
+ spec?: TransitionSpec;
9
+ onAnimationFinish?: (finished: boolean) => void;
10
+ animations: AnimationMap;
11
+ /** Optional initial velocity for spring-based progress (units: progress/sec). */
12
+ initialVelocity?: number;
13
+ }
14
+
15
+ export const startScreenTransition = ({
16
+ target,
17
+ spec,
18
+ onAnimationFinish,
19
+ animations,
20
+ initialVelocity,
21
+ }: StartScreenTransitionProps) => {
22
+ "worklet";
23
+ const value = target === "open" ? 1 : 0;
24
+ const config = target === "open" ? spec?.open : spec?.close;
25
+
26
+ const isSpringConfig =
27
+ !!config && !("duration" in config) && !("easing" in config);
28
+
29
+ const effectiveConfig =
30
+ isSpringConfig && typeof initialVelocity === "number"
31
+ ? { ...config, velocity: initialVelocity }
32
+ : config;
33
+
34
+ const { progress, animating, closing } = animations;
35
+
36
+ if (target === "close") {
37
+ closing.value = 1;
38
+ }
39
+
40
+ if (!config) {
41
+ animating.value = 0;
42
+ progress.value = value;
43
+
44
+ if (onAnimationFinish) {
45
+ runOnJS(onAnimationFinish)(true);
46
+ }
47
+ return;
48
+ }
49
+
50
+ animating.value = 1;
51
+
52
+ progress.value = animate(value, effectiveConfig, (finished) => {
53
+ "worklet";
54
+ if (finished) {
55
+ if (onAnimationFinish) {
56
+ runOnJS(onAnimationFinish)(finished);
57
+ }
58
+ animating.value = 0;
59
+ }
60
+ });
61
+ };
@@ -1,10 +1,10 @@
1
- import type { ScreenPhase } from '../../../types/core';
2
- import type { ScreenTransitionState } from '../../../types/animation';
1
+ import type { ScreenTransitionState } from "../../../types/animation";
2
+ import type { ScreenPhase } from "../../../types/core";
3
3
 
4
4
  export type GetBoundsParams = {
5
- id: string | null;
6
- phase?: ScreenPhase;
7
- previous?: ScreenTransitionState;
8
- current: ScreenTransitionState;
9
- next?: ScreenTransitionState;
5
+ id: string | null;
6
+ phase?: ScreenPhase;
7
+ previous?: ScreenTransitionState;
8
+ current: ScreenTransitionState;
9
+ next?: ScreenTransitionState;
10
10
  };