react-native-screen-transitions 3.0.0-rc.2 → 3.0.0-rc.4

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 (241) hide show
  1. package/README.md +421 -371
  2. package/lib/commonjs/blank-stack/components/{Overlay.js → overlay.js} +7 -5
  3. package/lib/commonjs/blank-stack/components/overlay.js.map +1 -0
  4. package/lib/commonjs/blank-stack/components/{Screens.js → screens.js} +20 -17
  5. package/lib/commonjs/blank-stack/components/screens.js.map +1 -0
  6. package/lib/commonjs/blank-stack/components/stack-view.js +101 -0
  7. package/lib/commonjs/blank-stack/components/stack-view.js.map +1 -0
  8. package/lib/commonjs/blank-stack/index.js +1 -8
  9. package/lib/commonjs/blank-stack/index.js.map +1 -1
  10. package/lib/commonjs/blank-stack/navigators/{createBlankStackNavigator.js → create-blank-stack-navigator.js} +3 -3
  11. package/lib/commonjs/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -0
  12. package/lib/commonjs/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js +1 -11
  13. package/lib/commonjs/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js.map +1 -1
  14. package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js +1 -12
  15. package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js.map +1 -1
  16. package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.js.map +1 -1
  17. package/lib/commonjs/blank-stack/utils/with-stack-navigation/index.js +49 -55
  18. package/lib/commonjs/blank-stack/utils/with-stack-navigation/index.js.map +1 -1
  19. package/lib/commonjs/blank-stack/utils/with-stack-navigation/{_types.js → types.js} +1 -1
  20. package/lib/commonjs/blank-stack/utils/with-stack-navigation/types.js.map +1 -0
  21. package/lib/commonjs/native-stack/views/NativeStackView.native.js +110 -103
  22. package/lib/commonjs/native-stack/views/NativeStackView.native.js.map +1 -1
  23. package/lib/commonjs/shared/components/controllers/blank-stack-lifecycle.js +72 -0
  24. package/lib/commonjs/shared/components/controllers/blank-stack-lifecycle.js.map +1 -0
  25. package/lib/commonjs/shared/components/controllers/native-stack-lifecycle.js +79 -0
  26. package/lib/commonjs/shared/components/controllers/native-stack-lifecycle.js.map +1 -0
  27. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +49 -23
  28. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
  29. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js +11 -6
  30. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  31. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js +7 -7
  32. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  33. package/lib/commonjs/shared/providers/flags.provider.js +25 -0
  34. package/lib/commonjs/shared/providers/flags.provider.js.map +1 -0
  35. package/lib/commonjs/shared/providers/gestures.provider.js +32 -5
  36. package/lib/commonjs/shared/providers/gestures.provider.js.map +1 -1
  37. package/lib/commonjs/shared/providers/register-bounds.provider.js +72 -45
  38. package/lib/commonjs/shared/providers/register-bounds.provider.js.map +1 -1
  39. package/lib/commonjs/shared/providers/routes.provider.js +48 -0
  40. package/lib/commonjs/shared/providers/routes.provider.js.map +1 -0
  41. package/lib/commonjs/shared/providers/screen-transition.provider.js.map +1 -1
  42. package/lib/commonjs/shared/stores/bounds.store.js +91 -47
  43. package/lib/commonjs/shared/stores/bounds.store.js.map +1 -1
  44. package/lib/commonjs/shared/types/state.types.js +9 -0
  45. package/lib/commonjs/shared/types/state.types.js.map +1 -0
  46. package/lib/commonjs/shared/utils/animation/compute-stack-progress.js +20 -0
  47. package/lib/commonjs/shared/utils/animation/compute-stack-progress.js.map +1 -0
  48. package/lib/commonjs/shared/utils/animation/derivations.js +1 -1
  49. package/lib/commonjs/shared/utils/animation/start-screen-transition.js +11 -11
  50. package/lib/commonjs/shared/utils/animation/start-screen-transition.js.map +1 -1
  51. package/lib/commonjs/shared/utils/bounds/helpers/is-bounds-equal.js +1 -1
  52. package/lib/commonjs/shared/utils/bounds/helpers/is-bounds-equal.js.map +1 -1
  53. package/lib/commonjs/shared/utils/bounds/index.js +4 -5
  54. package/lib/commonjs/shared/utils/bounds/index.js.map +1 -1
  55. package/lib/commonjs/shared/utils/create-provider.js +20 -1
  56. package/lib/commonjs/shared/utils/create-provider.js.map +1 -1
  57. package/lib/commonjs/shared/utils/reset-stores-for-screen.js +2 -0
  58. package/lib/commonjs/shared/utils/reset-stores-for-screen.js.map +1 -1
  59. package/lib/module/blank-stack/components/{Overlay.js → overlay.js} +7 -5
  60. package/lib/module/blank-stack/components/overlay.js.map +1 -0
  61. package/lib/module/blank-stack/components/screens.js +61 -0
  62. package/lib/module/blank-stack/components/screens.js.map +1 -0
  63. package/lib/module/blank-stack/components/stack-view.js +96 -0
  64. package/lib/module/blank-stack/components/stack-view.js.map +1 -0
  65. package/lib/module/blank-stack/index.js +1 -2
  66. package/lib/module/blank-stack/index.js.map +1 -1
  67. package/lib/module/blank-stack/navigators/{createBlankStackNavigator.js → create-blank-stack-navigator.js} +2 -2
  68. package/lib/module/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -0
  69. package/lib/module/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js +1 -11
  70. package/lib/module/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js.map +1 -1
  71. package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js +1 -12
  72. package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js.map +1 -1
  73. package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.js.map +1 -1
  74. package/lib/module/blank-stack/utils/with-stack-navigation/index.js +48 -54
  75. package/lib/module/blank-stack/utils/with-stack-navigation/index.js.map +1 -1
  76. package/lib/module/blank-stack/utils/with-stack-navigation/types.js +4 -0
  77. package/lib/module/blank-stack/utils/with-stack-navigation/types.js.map +1 -0
  78. package/lib/module/native-stack/views/NativeStackView.native.js +109 -102
  79. package/lib/module/native-stack/views/NativeStackView.native.js.map +1 -1
  80. package/lib/module/shared/components/controllers/blank-stack-lifecycle.js +66 -0
  81. package/lib/module/shared/components/controllers/blank-stack-lifecycle.js.map +1 -0
  82. package/lib/module/shared/components/controllers/native-stack-lifecycle.js +73 -0
  83. package/lib/module/shared/components/controllers/native-stack-lifecycle.js.map +1 -0
  84. package/lib/module/shared/hooks/animation/use-screen-animation.js +49 -23
  85. package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
  86. package/lib/module/shared/hooks/gestures/use-build-gestures.js +11 -6
  87. package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  88. package/lib/module/shared/hooks/gestures/use-scroll-registry.js +7 -7
  89. package/lib/module/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  90. package/lib/module/shared/providers/flags.provider.js +19 -0
  91. package/lib/module/shared/providers/flags.provider.js.map +1 -0
  92. package/lib/module/shared/providers/gestures.provider.js +31 -4
  93. package/lib/module/shared/providers/gestures.provider.js.map +1 -1
  94. package/lib/module/shared/providers/register-bounds.provider.js +72 -45
  95. package/lib/module/shared/providers/register-bounds.provider.js.map +1 -1
  96. package/lib/module/shared/providers/routes.provider.js +42 -0
  97. package/lib/module/shared/providers/routes.provider.js.map +1 -0
  98. package/lib/module/shared/providers/screen-transition.provider.js.map +1 -1
  99. package/lib/module/shared/stores/bounds.store.js +91 -47
  100. package/lib/module/shared/stores/bounds.store.js.map +1 -1
  101. package/lib/module/shared/types/state.types.js +5 -0
  102. package/lib/module/shared/types/state.types.js.map +1 -0
  103. package/lib/module/shared/utils/animation/compute-stack-progress.js +15 -0
  104. package/lib/module/shared/utils/animation/compute-stack-progress.js.map +1 -0
  105. package/lib/module/shared/utils/animation/derivations.js +1 -1
  106. package/lib/module/shared/utils/animation/start-screen-transition.js +11 -11
  107. package/lib/module/shared/utils/animation/start-screen-transition.js.map +1 -1
  108. package/lib/module/shared/utils/bounds/helpers/is-bounds-equal.js +1 -1
  109. package/lib/module/shared/utils/bounds/helpers/is-bounds-equal.js.map +1 -1
  110. package/lib/module/shared/utils/bounds/index.js +4 -5
  111. package/lib/module/shared/utils/bounds/index.js.map +1 -1
  112. package/lib/module/shared/utils/create-provider.js +20 -1
  113. package/lib/module/shared/utils/create-provider.js.map +1 -1
  114. package/lib/module/shared/utils/reset-stores-for-screen.js +2 -0
  115. package/lib/module/shared/utils/reset-stores-for-screen.js.map +1 -1
  116. package/lib/typescript/blank-stack/components/{Overlay.d.ts → overlay.d.ts} +1 -1
  117. package/lib/typescript/blank-stack/components/overlay.d.ts.map +1 -0
  118. package/lib/typescript/blank-stack/components/{Screens.d.ts → screens.d.ts} +1 -1
  119. package/lib/typescript/blank-stack/components/screens.d.ts.map +1 -0
  120. package/lib/typescript/blank-stack/components/stack-view.d.ts +3 -0
  121. package/lib/typescript/blank-stack/components/stack-view.d.ts.map +1 -0
  122. package/lib/typescript/blank-stack/index.d.ts +1 -2
  123. package/lib/typescript/blank-stack/index.d.ts.map +1 -1
  124. package/lib/typescript/blank-stack/navigators/{createBlankStackNavigator.d.ts → create-blank-stack-navigator.d.ts} +1 -1
  125. package/lib/typescript/blank-stack/navigators/create-blank-stack-navigator.d.ts.map +1 -0
  126. package/lib/typescript/blank-stack/types.d.ts +5 -39
  127. package/lib/typescript/blank-stack/types.d.ts.map +1 -1
  128. package/lib/typescript/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.d.ts.map +1 -1
  129. package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.d.ts.map +1 -1
  130. package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.d.ts +1 -1
  131. package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.d.ts.map +1 -1
  132. package/lib/typescript/blank-stack/utils/with-stack-navigation/index.d.ts +3 -5
  133. package/lib/typescript/blank-stack/utils/with-stack-navigation/index.d.ts.map +1 -1
  134. package/lib/typescript/blank-stack/utils/with-stack-navigation/{_types.d.ts → types.d.ts} +1 -1
  135. package/lib/typescript/blank-stack/utils/with-stack-navigation/types.d.ts.map +1 -0
  136. package/lib/typescript/native-stack/views/NativeStackView.native.d.ts.map +1 -1
  137. package/lib/typescript/shared/components/controllers/blank-stack-lifecycle.d.ts +8 -0
  138. package/lib/typescript/shared/components/controllers/blank-stack-lifecycle.d.ts.map +1 -0
  139. package/lib/typescript/shared/components/controllers/native-stack-lifecycle.d.ts +8 -0
  140. package/lib/typescript/shared/components/controllers/native-stack-lifecycle.d.ts.map +1 -0
  141. package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
  142. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts +2 -2
  143. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
  144. package/lib/typescript/shared/index.d.ts +20 -20
  145. package/lib/typescript/shared/providers/flags.provider.d.ts +10 -0
  146. package/lib/typescript/shared/providers/flags.provider.d.ts.map +1 -0
  147. package/lib/typescript/shared/providers/gestures.provider.d.ts +8 -2
  148. package/lib/typescript/shared/providers/gestures.provider.d.ts.map +1 -1
  149. package/lib/typescript/shared/providers/register-bounds.provider.d.ts.map +1 -1
  150. package/lib/typescript/shared/providers/routes.provider.d.ts +19 -0
  151. package/lib/typescript/shared/providers/routes.provider.d.ts.map +1 -0
  152. package/lib/typescript/shared/providers/screen-transition.provider.d.ts +2 -2
  153. package/lib/typescript/shared/providers/screen-transition.provider.d.ts.map +1 -1
  154. package/lib/typescript/shared/stores/bounds.store.d.ts +23 -11
  155. package/lib/typescript/shared/stores/bounds.store.d.ts.map +1 -1
  156. package/lib/typescript/shared/types/animation.types.d.ts +12 -0
  157. package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
  158. package/lib/typescript/shared/types/bounds.types.d.ts +2 -2
  159. package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
  160. package/lib/typescript/shared/types/state.types.d.ts +3 -0
  161. package/lib/typescript/shared/types/state.types.d.ts.map +1 -0
  162. package/lib/typescript/shared/utils/animation/compute-stack-progress.d.ts +3 -0
  163. package/lib/typescript/shared/utils/animation/compute-stack-progress.d.ts.map +1 -0
  164. package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts.map +1 -1
  165. package/lib/typescript/shared/utils/bounds/index.d.ts.map +1 -1
  166. package/lib/typescript/shared/utils/create-provider.d.ts +2 -2
  167. package/lib/typescript/shared/utils/create-provider.d.ts.map +1 -1
  168. package/lib/typescript/shared/utils/reset-stores-for-screen.d.ts.map +1 -1
  169. package/package.json +2 -1
  170. package/src/blank-stack/components/{Overlay.tsx → overlay.tsx} +4 -3
  171. package/src/blank-stack/components/{Screens.tsx → screens.tsx} +24 -20
  172. package/src/blank-stack/components/stack-view.tsx +115 -0
  173. package/src/blank-stack/index.ts +1 -2
  174. package/src/blank-stack/navigators/{createBlankStackNavigator.tsx → create-blank-stack-navigator.tsx} +1 -1
  175. package/src/blank-stack/types.ts +6 -31
  176. package/src/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.ts +1 -8
  177. package/src/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.tsx +1 -12
  178. package/src/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.tsx +1 -1
  179. package/src/blank-stack/utils/with-stack-navigation/index.tsx +42 -62
  180. package/src/native-stack/views/NativeStackView.native.tsx +121 -112
  181. package/src/shared/__tests__/bounds.store.test.ts +376 -167
  182. package/src/shared/__tests__/determine-dismissal.test.ts +2 -12
  183. package/src/shared/__tests__/geometry.test.ts +1 -1
  184. package/src/shared/__tests__/gesture.velocity.test.ts +2 -10
  185. package/src/shared/components/controllers/blank-stack-lifecycle.tsx +70 -0
  186. package/src/shared/components/controllers/native-stack-lifecycle.tsx +87 -0
  187. package/src/shared/hooks/animation/use-screen-animation.tsx +61 -30
  188. package/src/shared/hooks/gestures/use-build-gestures.tsx +16 -7
  189. package/src/shared/hooks/gestures/use-scroll-registry.tsx +7 -7
  190. package/src/shared/providers/flags.provider.tsx +21 -0
  191. package/src/shared/providers/gestures.provider.tsx +34 -5
  192. package/src/shared/providers/register-bounds.provider.tsx +86 -54
  193. package/src/shared/providers/routes.provider.tsx +54 -0
  194. package/src/shared/providers/screen-transition.provider.tsx +2 -2
  195. package/src/shared/stores/bounds.store.ts +90 -54
  196. package/src/shared/types/animation.types.ts +13 -0
  197. package/src/shared/types/bounds.types.ts +2 -2
  198. package/src/shared/types/state.types.ts +2 -0
  199. package/src/shared/utils/animation/compute-stack-progress.ts +16 -0
  200. package/src/shared/utils/animation/derivations.ts +1 -1
  201. package/src/shared/utils/animation/start-screen-transition.ts +13 -10
  202. package/src/shared/utils/bounds/helpers/is-bounds-equal.ts +1 -1
  203. package/src/shared/utils/bounds/index.ts +7 -10
  204. package/src/shared/utils/create-provider.tsx +35 -1
  205. package/src/shared/utils/reset-stores-for-screen.ts +2 -0
  206. package/lib/commonjs/blank-stack/components/Overlay.js.map +0 -1
  207. package/lib/commonjs/blank-stack/components/Screens.js.map +0 -1
  208. package/lib/commonjs/blank-stack/components/StackView.js +0 -93
  209. package/lib/commonjs/blank-stack/components/StackView.js.map +0 -1
  210. package/lib/commonjs/blank-stack/navigators/createBlankStackNavigator.js.map +0 -1
  211. package/lib/commonjs/blank-stack/utils/with-stack-navigation/_types.js.map +0 -1
  212. package/lib/commonjs/shared/components/controllers/screen-lifecycle.js +0 -142
  213. package/lib/commonjs/shared/components/controllers/screen-lifecycle.js.map +0 -1
  214. package/lib/commonjs/shared/hooks/gestures/use-parent-gesture-registry.js +0 -28
  215. package/lib/commonjs/shared/hooks/gestures/use-parent-gesture-registry.js.map +0 -1
  216. package/lib/module/blank-stack/components/Overlay.js.map +0 -1
  217. package/lib/module/blank-stack/components/Screens.js +0 -58
  218. package/lib/module/blank-stack/components/Screens.js.map +0 -1
  219. package/lib/module/blank-stack/components/StackView.js +0 -88
  220. package/lib/module/blank-stack/components/StackView.js.map +0 -1
  221. package/lib/module/blank-stack/navigators/createBlankStackNavigator.js.map +0 -1
  222. package/lib/module/blank-stack/utils/with-stack-navigation/_types.js +0 -4
  223. package/lib/module/blank-stack/utils/with-stack-navigation/_types.js.map +0 -1
  224. package/lib/module/shared/components/controllers/screen-lifecycle.js +0 -136
  225. package/lib/module/shared/components/controllers/screen-lifecycle.js.map +0 -1
  226. package/lib/module/shared/hooks/gestures/use-parent-gesture-registry.js +0 -23
  227. package/lib/module/shared/hooks/gestures/use-parent-gesture-registry.js.map +0 -1
  228. package/lib/typescript/blank-stack/components/Overlay.d.ts.map +0 -1
  229. package/lib/typescript/blank-stack/components/Screens.d.ts.map +0 -1
  230. package/lib/typescript/blank-stack/components/StackView.d.ts +0 -2
  231. package/lib/typescript/blank-stack/components/StackView.d.ts.map +0 -1
  232. package/lib/typescript/blank-stack/navigators/createBlankStackNavigator.d.ts.map +0 -1
  233. package/lib/typescript/blank-stack/utils/with-stack-navigation/_types.d.ts.map +0 -1
  234. package/lib/typescript/shared/components/controllers/screen-lifecycle.d.ts +0 -12
  235. package/lib/typescript/shared/components/controllers/screen-lifecycle.d.ts.map +0 -1
  236. package/lib/typescript/shared/hooks/gestures/use-parent-gesture-registry.d.ts +0 -6
  237. package/lib/typescript/shared/hooks/gestures/use-parent-gesture-registry.d.ts.map +0 -1
  238. package/src/blank-stack/components/StackView.tsx +0 -108
  239. package/src/shared/components/controllers/screen-lifecycle.tsx +0 -154
  240. package/src/shared/hooks/gestures/use-parent-gesture-registry.tsx +0 -18
  241. /package/src/blank-stack/utils/with-stack-navigation/{_types.ts → types.ts} +0 -0
@@ -43,27 +43,26 @@ interface RegisterBoundsContextValue {
43
43
  }
44
44
 
45
45
  /**
46
- * Gets the parent screen's route key for nested navigators.
47
- * Returns undefined if we're not inside a nested navigator.
46
+ * Builds the full ancestor key chain for nested navigators.
47
+ * Returns an array of screen keys from immediate parent to root.
48
+ * [parentKey, grandparentKey, greatGrandparentKey, ...]
48
49
  */
49
- const getParentScreenKey = (current: TransitionDescriptor) => {
50
- const parent = current.navigation.getParent();
51
- if (!parent) return undefined;
52
-
53
- const parentState = parent.getState();
54
- if (!parentState?.routes) return undefined;
55
-
56
- // Check if our route key exists directly in parent's routes
57
- const existsInParent = parentState.routes.some(
58
- (r) => r.key === current.route.key,
59
- );
60
-
61
- // If we don't exist in parent's routes, we're nested inside the focused route
62
- if (!existsInParent && parentState.index !== undefined) {
63
- return parentState.routes[parentState.index]?.key;
50
+ const getAncestorKeys = (current: TransitionDescriptor): string[] => {
51
+ const ancestors: string[] = [];
52
+ let nav = current.navigation.getParent();
53
+
54
+ while (nav) {
55
+ const state = nav.getState();
56
+ if (state?.routes && state.index !== undefined) {
57
+ const focusedRoute = state.routes[state.index];
58
+ if (focusedRoute?.key) {
59
+ ancestors.push(focusedRoute.key);
60
+ }
61
+ }
62
+ nav = nav.getParent();
64
63
  }
65
64
 
66
- return undefined;
65
+ return ancestors;
67
66
  };
68
67
 
69
68
  /**
@@ -73,13 +72,13 @@ const getParentScreenKey = (current: TransitionDescriptor) => {
73
72
  const useInitialLayoutHandler = (params: {
74
73
  sharedBoundTag?: string;
75
74
  currentScreenKey: string;
76
- parentScreenKey?: string;
75
+ ancestorKeys: string[];
77
76
  maybeMeasureAndStore: (options: MaybeMeasureAndStoreParams) => void;
78
77
  }) => {
79
78
  const {
80
79
  sharedBoundTag,
81
80
  currentScreenKey,
82
- parentScreenKey,
81
+ ancestorKeys,
83
82
  maybeMeasureAndStore,
84
83
  } = params;
85
84
 
@@ -87,57 +86,85 @@ const useInitialLayoutHandler = (params: {
87
86
  currentScreenKey,
88
87
  "animating",
89
88
  );
90
- const isParentAnimating = parentScreenKey
91
- ? AnimationStore.getAnimation(parentScreenKey, "animating")
92
- : null;
89
+
90
+ // Check if any ancestor is animating
91
+ const ancestorAnimations = ancestorKeys.map((key) =>
92
+ AnimationStore.getAnimation(key, "animating"),
93
+ );
93
94
 
94
95
  const hasMeasuredOnLayout = useSharedValue(false);
95
96
 
96
97
  return useCallback(() => {
97
98
  "worklet";
98
- if (!sharedBoundTag || hasMeasuredOnLayout.value) return;
99
- if (!isAnimating.value && !isParentAnimating?.value) return;
99
+ if (!sharedBoundTag || hasMeasuredOnLayout.get()) return;
100
+
101
+ // Check if current or any ancestor is animating
102
+ let isAnyAnimating = isAnimating.get();
103
+ for (let i = 0; i < ancestorAnimations.length; i++) {
104
+ if (ancestorAnimations[i].get()) {
105
+ isAnyAnimating = 1;
106
+ break;
107
+ }
108
+ }
109
+
110
+ if (!isAnyAnimating) return;
100
111
 
101
112
  maybeMeasureAndStore({
102
113
  shouldSetSource: false,
103
114
  shouldSetDestination: true,
104
115
  });
105
116
 
106
- hasMeasuredOnLayout.value = true;
117
+ hasMeasuredOnLayout.set(true);
107
118
  }, [
108
119
  sharedBoundTag,
109
120
  hasMeasuredOnLayout,
110
121
  isAnimating,
111
- isParentAnimating,
122
+ ancestorAnimations,
112
123
  maybeMeasureAndStore,
113
124
  ]);
114
125
  };
115
126
 
127
+ /**
128
+ * Measures non-pressable elements when screen becomes blurred.
129
+ * Captures bounds right before transition starts.
130
+ */
116
131
  /**
117
132
  * Measures non-pressable elements when screen becomes blurred.
118
133
  * Captures bounds right before transition starts.
119
134
  */
120
135
  const useBlurMeasurement = (params: {
121
136
  sharedBoundTag?: string;
137
+ ancestorKeys: string[];
122
138
  maybeMeasureAndStore: (options: MaybeMeasureAndStoreParams) => void;
123
139
  }) => {
124
- const { sharedBoundTag, maybeMeasureAndStore } = params;
125
- const isFocused = useRef(true);
140
+ const { current } = useKeys();
141
+ const { sharedBoundTag, ancestorKeys, maybeMeasureAndStore } = params;
126
142
  const hasCapturedSource = useRef(false);
127
143
 
144
+ const ancestorClosing = [current.route.key, ...ancestorKeys].map((key) =>
145
+ AnimationStore.getAnimation(key, "closing"),
146
+ );
147
+
148
+ const maybeMeasureOnBlur = useStableCallbackValue(() => {
149
+ "worklet";
150
+
151
+ //.some doesnt work here apparently... :-(
152
+ for (const closing of ancestorClosing) {
153
+ if (closing.get()) return;
154
+ }
155
+
156
+ maybeMeasureAndStore({ shouldSetSource: true });
157
+ });
158
+
128
159
  useFocusEffect(
129
160
  useCallback(() => {
130
- isFocused.current = true;
131
161
  hasCapturedSource.current = false;
132
162
 
133
163
  return () => {
134
- if (!sharedBoundTag) return;
135
- if (hasCapturedSource.current) return;
136
-
137
- isFocused.current = false;
138
- runOnUI(maybeMeasureAndStore)({ shouldSetSource: true });
164
+ if (!sharedBoundTag || hasCapturedSource.current) return;
165
+ runOnUI(maybeMeasureOnBlur)();
139
166
  };
140
- }, [sharedBoundTag, maybeMeasureAndStore]),
167
+ }, [sharedBoundTag, maybeMeasureOnBlur]),
141
168
  );
142
169
 
143
170
  return {
@@ -157,7 +184,7 @@ const useParentSyncReaction = (params: {
157
184
  const { parentContext, maybeMeasureAndStore } = params;
158
185
 
159
186
  useAnimatedReaction(
160
- () => parentContext?.updateSignal.value,
187
+ () => parentContext?.updateSignal.get(),
161
188
  (value) => {
162
189
  "worklet";
163
190
  if (value === 0 || value === undefined) return;
@@ -173,11 +200,12 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
173
200
  ({ style, onPress, sharedBoundTag, animatedRef, children }) => {
174
201
  const { current } = useKeys();
175
202
  const currentScreenKey = current.route.key;
176
- const parentScreenKey = getParentScreenKey(current);
203
+ const ancestorKeys = useMemo(() => getAncestorKeys(current), [current]);
177
204
 
178
205
  // Context & signals
179
206
  const parentContext: RegisterBoundsContextValue | null =
180
207
  useRegisterBoundsContext();
208
+
181
209
  const ownSignal = useSharedValue(0);
182
210
  const updateSignal: SharedValue<number> =
183
211
  parentContext?.updateSignal ?? ownSignal;
@@ -191,7 +219,7 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
191
219
  const emitUpdate = useStableCallbackValue(() => {
192
220
  "worklet";
193
221
  const isRoot = !parentContext;
194
- if (isRoot) updateSignal.value = updateSignal.value + 1;
222
+ if (isRoot) updateSignal.set(updateSignal.get() + 1);
195
223
  });
196
224
 
197
225
  const maybeMeasureAndStore = useStableCallbackValue(
@@ -208,28 +236,31 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
208
236
 
209
237
  emitUpdate();
210
238
 
211
- // Always register occurrence
212
- BoundStore.registerOccurrence(
239
+ BoundStore.registerSnapshot(
213
240
  sharedBoundTag,
214
241
  currentScreenKey,
215
242
  measured,
216
243
  preparedStyles,
244
+ ancestorKeys,
217
245
  );
218
246
 
219
- // Set as source (on press or blur)
220
247
  if (shouldSetSource) {
221
- if (isAnimating.value) {
222
- const existing = BoundStore.getOccurrence(
223
- sharedBoundTag,
224
- currentScreenKey,
225
- );
226
- BoundStore.setLinkSource(
248
+ if (isAnimating.get()) {
249
+ // If animation is already in progress,
250
+ // lets use the existing measuremenets.
251
+ const existing = BoundStore.getSnapshot(
227
252
  sharedBoundTag,
228
253
  currentScreenKey,
229
- existing.bounds,
230
- preparedStyles,
231
- parentScreenKey,
232
254
  );
255
+ if (existing) {
256
+ BoundStore.setLinkSource(
257
+ sharedBoundTag,
258
+ currentScreenKey,
259
+ existing.bounds,
260
+ preparedStyles,
261
+ ancestorKeys,
262
+ );
263
+ }
233
264
  return;
234
265
  }
235
266
  BoundStore.setLinkSource(
@@ -237,7 +268,7 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
237
268
  currentScreenKey,
238
269
  measured,
239
270
  preparedStyles,
240
- parentScreenKey,
271
+ ancestorKeys,
241
272
  );
242
273
  }
243
274
 
@@ -248,7 +279,7 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
248
279
  currentScreenKey,
249
280
  measured,
250
281
  preparedStyles,
251
- parentScreenKey,
282
+ ancestorKeys,
252
283
  );
253
284
  }
254
285
 
@@ -259,7 +290,7 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
259
290
  const handleInitialLayout = useInitialLayoutHandler({
260
291
  sharedBoundTag,
261
292
  currentScreenKey,
262
- parentScreenKey,
293
+ ancestorKeys,
263
294
  maybeMeasureAndStore,
264
295
  });
265
296
 
@@ -267,6 +298,7 @@ const { RegisterBoundsProvider, useRegisterBoundsContext } = createProvider(
267
298
  const { markSourceCaptured } = useBlurMeasurement({
268
299
  sharedBoundTag,
269
300
  maybeMeasureAndStore,
301
+ ancestorKeys,
270
302
  });
271
303
 
272
304
  useParentSyncReaction({ parentContext, maybeMeasureAndStore });
@@ -0,0 +1,54 @@
1
+ import { useMemo } from "react";
2
+ import {
3
+ AnimationStore,
4
+ type AnimationStoreMap,
5
+ } from "../stores/animation.store";
6
+ import createProvider from "../utils/create-provider";
7
+
8
+ interface RoutesProviderProps {
9
+ children: React.ReactNode;
10
+ routeKeys: string[];
11
+ }
12
+
13
+ interface RoutesContextValue {
14
+ /**
15
+ * Array of route keys for all routes in the stack, in order.
16
+ */
17
+ routeKeys: string[];
18
+ }
19
+
20
+ const { RoutesProvider, useRoutesContext } = createProvider("Routes", {
21
+ guarded: false,
22
+ })<RoutesProviderProps, RoutesContextValue>(({ routeKeys }) => ({
23
+ value: { routeKeys },
24
+ }));
25
+
26
+ export { RoutesProvider, useRoutesContext };
27
+
28
+ /**
29
+ * Hook to get animation values for all screens from a given index onwards.
30
+ * Useful for computing accumulated progress across multiple screens.
31
+ */
32
+ export function useStackAnimationValues(
33
+ currentRouteKey: string | undefined,
34
+ ): AnimationStoreMap[] {
35
+ const routesContext = useRoutesContext();
36
+
37
+ return useMemo(() => {
38
+ if (!currentRouteKey || !routesContext) {
39
+ return [];
40
+ }
41
+
42
+ const { routeKeys } = routesContext;
43
+ const currentIndex = routeKeys.indexOf(currentRouteKey);
44
+
45
+ if (currentIndex === -1) {
46
+ return [];
47
+ }
48
+
49
+ // Get animation values for all screens from current index onwards
50
+ return routeKeys
51
+ .slice(currentIndex)
52
+ .map((key) => AnimationStore.getAll(key));
53
+ }, [currentRouteKey, routesContext]);
54
+ }
@@ -1,7 +1,7 @@
1
1
  import type React from "react";
2
2
  import type { ComponentType } from "react";
3
- import type { ScreenLifecycleProps } from "../components/controllers/screen-lifecycle";
4
3
  import { RootTransitionAware } from "../components/root-transition-aware";
4
+ import type { Any } from "../types/utils.types";
5
5
  import { ScreenGestureProvider } from "./gestures.provider";
6
6
  import { KeysProvider, type TransitionDescriptor } from "./keys.provider";
7
7
  import { TransitionStylesProvider } from "./transition-styles.provider";
@@ -11,7 +11,7 @@ type Props<TDescriptor extends TransitionDescriptor> = {
11
11
  current: TDescriptor;
12
12
  next?: TDescriptor;
13
13
  children: React.ReactNode;
14
- LifecycleController: ComponentType<ScreenLifecycleProps>;
14
+ LifecycleController: ComponentType<Any>;
15
15
  };
16
16
 
17
17
  export function ScreenTransitionProvider<
@@ -8,51 +8,42 @@ import type { Any } from "../types/utils.types";
8
8
  type TagID = string;
9
9
  type ScreenKey = string;
10
10
 
11
- export type TagData = {
11
+ export type Snapshot = {
12
12
  bounds: MeasuredDimensions;
13
13
  styles: StyleProps;
14
14
  };
15
15
 
16
16
  type ScreenIdentifier = {
17
17
  screenKey: ScreenKey;
18
- parentScreenKey?: ScreenKey;
18
+ ancestorKeys?: ScreenKey[];
19
19
  };
20
20
 
21
21
  type TagLink = {
22
- source: ScreenIdentifier & TagData;
23
- destination: (ScreenIdentifier & TagData) | null;
22
+ source: ScreenIdentifier & Snapshot;
23
+ destination: (ScreenIdentifier & Snapshot) | null;
24
24
  };
25
25
 
26
26
  type TagState = {
27
- occurrences: Record<ScreenKey, TagData>;
27
+ snapshots: Record<ScreenKey, Snapshot & { ancestorKeys?: ScreenKey[] }>;
28
28
  linkStack: TagLink[];
29
29
  };
30
30
 
31
- /**
32
- * Note on cleanup: We intentionally skip automatic cleanup of old links.
33
- * The linkStack grows by one entry per navigation, but `getActiveLink`
34
- * finds the correct link via screenKey matching regardless of stack size.
35
- * This is unlikely to cause performance issues in typical apps, but if
36
- * memory becomes a concern in apps with heavy navigation (hundreds of
37
- * transitions), we should consider implementing cleanup on screen unmount using
38
- * screenKey filtering.
39
- */
40
-
41
31
  const registry = makeMutable<Record<TagID, TagState>>({});
42
32
 
43
- function registerOccurrence(
33
+ function registerSnapshot(
44
34
  tag: TagID,
45
35
  screenKey: ScreenKey,
46
36
  bounds: MeasuredDimensions,
47
37
  styles: StyleProps = {},
38
+ ancestorKeys?: ScreenKey[],
48
39
  ) {
49
40
  "worklet";
50
41
  registry.modify((state: Any) => {
51
42
  "worklet";
52
43
  if (!state[tag]) {
53
- state[tag] = { occurrences: {}, linkStack: [] };
44
+ state[tag] = { snapshots: {}, linkStack: [] };
54
45
  }
55
- state[tag].occurrences[screenKey] = { bounds, styles };
46
+ state[tag].snapshots[screenKey] = { bounds, styles, ancestorKeys };
56
47
  return state;
57
48
  });
58
49
  }
@@ -62,16 +53,15 @@ function setLinkSource(
62
53
  screenKey: ScreenKey,
63
54
  bounds: MeasuredDimensions,
64
55
  styles: StyleProps = {},
65
- parentScreenKey?: ScreenKey,
56
+ ancestorKeys?: ScreenKey[],
66
57
  ) {
67
58
  "worklet";
68
59
  registry.modify((state: Any) => {
69
60
  "worklet";
70
- if (!state[tag]) state[tag] = { occurrences: {}, linkStack: [] };
61
+ if (!state[tag]) state[tag] = { snapshots: {}, linkStack: [] };
71
62
 
72
- // Push new link onto stack
73
63
  state[tag].linkStack.push({
74
- source: { screenKey, parentScreenKey, bounds, styles },
64
+ source: { screenKey, ancestorKeys, bounds, styles },
75
65
  destination: null,
76
66
  });
77
67
  return state;
@@ -83,7 +73,7 @@ function setLinkDestination(
83
73
  screenKey: ScreenKey,
84
74
  bounds: MeasuredDimensions,
85
75
  styles: StyleProps = {},
86
- parentScreenKey?: ScreenKey,
76
+ ancestorKeys?: ScreenKey[],
87
77
  ) {
88
78
  "worklet";
89
79
  registry.modify((state: Any) => {
@@ -94,7 +84,7 @@ function setLinkDestination(
94
84
  // Find the topmost link without a destination
95
85
  for (let i = stack.length - 1; i >= 0; i--) {
96
86
  if (stack[i].destination === null) {
97
- stack[i].destination = { screenKey, parentScreenKey, bounds, styles };
87
+ stack[i].destination = { screenKey, ancestorKeys, bounds, styles };
98
88
  break;
99
89
  }
100
90
  }
@@ -102,22 +92,53 @@ function setLinkDestination(
102
92
  });
103
93
  }
104
94
 
105
- function getOccurrence(tag: TagID, key: ScreenKey) {
106
- "worklet";
107
- return registry.value[tag]?.occurrences[key] ?? null;
108
- }
109
-
110
- // Helper to check if a screen identifier matches a given key
95
+ /**
96
+ * Helper to check if a screen identifier matches a given key.
97
+ * Checks both direct screenKey match and ancestor chain.
98
+ */
111
99
  function matchesScreenKey(
112
100
  identifier: ScreenIdentifier | null | undefined,
113
101
  key: ScreenKey,
114
102
  ): boolean {
115
103
  "worklet";
116
104
  if (!identifier) return false;
117
- return identifier.screenKey === key || identifier.parentScreenKey === key;
105
+
106
+ // Direct match
107
+ if (identifier.screenKey === key) return true;
108
+
109
+ // Check ancestor chain
110
+ return identifier.ancestorKeys?.includes(key) ?? false;
111
+ }
112
+
113
+ /**
114
+ * Get snapshot by tag and optional key.
115
+ * If key is provided, supports ancestor matching - if the key matches any ancestor
116
+ * of a stored snapshot, that snapshot will be returned.
117
+ * If key is omitted, returns the most recently registered snapshot.
118
+ */
119
+ function getSnapshot(tag: TagID, key: ScreenKey): Snapshot | null {
120
+ "worklet";
121
+ const tagState = registry.value[tag];
122
+ if (!tagState) return null;
123
+
124
+ // Direct match in occurrences
125
+ if (tagState.snapshots[key]) {
126
+ const snap = tagState.snapshots[key];
127
+ return { bounds: snap.bounds, styles: snap.styles };
128
+ }
129
+
130
+ // Ancestor match
131
+ for (const screenKey in tagState.snapshots) {
132
+ const snap = tagState.snapshots[screenKey];
133
+ if (snap.ancestorKeys?.includes(key)) {
134
+ return { bounds: snap.bounds, styles: snap.styles };
135
+ }
136
+ }
137
+
138
+ return null;
118
139
  }
119
140
 
120
- function getActiveLink(tag: TagID, screenKey?: ScreenKey, isClosing?: boolean) {
141
+ function getActiveLink(tag: TagID, screenKey?: ScreenKey): TagLink | null {
121
142
  "worklet";
122
143
  const stack = registry.value[tag]?.linkStack;
123
144
 
@@ -127,40 +148,55 @@ function getActiveLink(tag: TagID, screenKey?: ScreenKey, isClosing?: boolean) {
127
148
 
128
149
  // If screenKey provided, find link involving that screen
129
150
  if (screenKey) {
130
- // When closing (backward nav), we want the link where this screen is the DESTINATION
131
- // When opening (forward nav), we want the link where this screen is the DESTINATION too
132
- // The source is always the "from" screen, destination is the "to" screen
133
-
134
- if (isClosing) {
135
- // Backward: find link where I am the destination (I'm going back to source)
136
- for (let i = stack.length - 1; i >= 0; i--) {
137
- const link = stack[i];
138
- if (matchesScreenKey(link.destination, screenKey)) {
139
- return link;
140
- }
141
- }
142
- }
143
-
144
- // Forward or fallback: find any link involving this screen
145
151
  for (let i = stack.length - 1; i >= 0; i--) {
146
152
  const link = stack[i];
147
- if (
148
- matchesScreenKey(link.source, screenKey) ||
149
- matchesScreenKey(link.destination, screenKey)
150
- ) {
153
+ if (!link.destination) continue;
154
+
155
+ const isSource = matchesScreenKey(link.source, screenKey);
156
+ const isDestination = matchesScreenKey(link.destination, screenKey);
157
+
158
+ if (isSource || isDestination) {
159
+ // If I match the source, I'm closing (going back to where I came from)
151
160
  return link;
152
161
  }
153
162
  }
154
163
  return null;
155
164
  }
156
165
 
157
- return stack[stack.length - 1] ?? null;
166
+ const lastLink = stack[stack.length - 1];
167
+ return lastLink ? lastLink : null;
168
+ }
169
+
170
+ /**
171
+ * Clear all snapshots and links for a screen across all tags.
172
+ * Called when a screen unmounts.
173
+ */
174
+ function clear(screenKey: ScreenKey) {
175
+ "worklet";
176
+ registry.modify((state: Any) => {
177
+ "worklet";
178
+ for (const tag in state) {
179
+ // Remove snapshot
180
+ if (state[tag].snapshots[screenKey]) {
181
+ delete state[tag].snapshots[screenKey];
182
+ }
183
+
184
+ // Remove links involving this screen
185
+ state[tag].linkStack = state[tag].linkStack.filter((link: TagLink) => {
186
+ const sourceMatches = matchesScreenKey(link.source, screenKey);
187
+ const destMatches = matchesScreenKey(link.destination, screenKey);
188
+ return !sourceMatches && !destMatches;
189
+ });
190
+ }
191
+ return state;
192
+ });
158
193
  }
159
194
 
160
195
  export const BoundStore = {
161
- registerOccurrence,
196
+ registerSnapshot,
162
197
  setLinkSource,
163
198
  setLinkDestination,
164
199
  getActiveLink,
165
- getOccurrence,
200
+ getSnapshot,
201
+ clear,
166
202
  };
@@ -75,6 +75,19 @@ export interface ScreenInterpolationProps {
75
75
  */
76
76
  progress: number;
77
77
 
78
+ /**
79
+ * Accumulated progress from the current screen's position onwards in the stack.
80
+ * Unlike `progress` (0-2), this ranges from 0-N where N is the number of screens
81
+ * above the current screen. Each screen at index I sees stackProgress as the
82
+ * sum of all progress values from index I to the top of the stack.
83
+ *
84
+ * Example: With 4 screens pushed, screen at index 1 would see stackProgress = 3
85
+ * when all screens are fully transitioned.
86
+ *
87
+ * Falls back to `progress` when not in blank-stack.
88
+ */
89
+ stackProgress: number;
90
+
78
91
  /**
79
92
  * Function that provides access to bounds builders for creating shared element transitions.
80
93
  */
@@ -1,5 +1,5 @@
1
1
  import type { MeasuredDimensions, StyleProps } from "react-native-reanimated";
2
- import type { TagData } from "../stores/bounds.store";
2
+ import type { Snapshot } from "../stores/bounds.store";
3
3
  import type {
4
4
  BoundsBuilderOptions,
5
5
  BoundsReturnType,
@@ -21,5 +21,5 @@ export type BoundEntry = {
21
21
 
22
22
  export type BoundsAccessor = {
23
23
  <T extends BoundsBuilderOptions>(options: T): BoundsReturnType<T>;
24
- getOccurrence: (id: string, key: string) => TagData;
24
+ getSnapshot: (id: string, key?: string) => Snapshot | null;
25
25
  };
@@ -0,0 +1,2 @@
1
+ export const TRUE = 1;
2
+ export const FALSE = 0;
@@ -0,0 +1,16 @@
1
+ import type { AnimationStoreMap } from "../../stores/animation.store";
2
+
3
+ export const computeStackProgress = (
4
+ stackAnimationValues: AnimationStoreMap[],
5
+ fallback: number = 0,
6
+ ) => {
7
+ "worklet";
8
+ let computedStackProgress: number | undefined;
9
+ if (stackAnimationValues.length > 0) {
10
+ computedStackProgress = 0;
11
+ for (let i = 0; i < stackAnimationValues.length; i += 1) {
12
+ computedStackProgress += stackAnimationValues[i].progress.value;
13
+ }
14
+ }
15
+ return computedStackProgress ?? fallback;
16
+ };
@@ -11,7 +11,7 @@ interface DerivationsParams {
11
11
  export const derivations = ({ current, next }: DerivationsParams) => {
12
12
  "worklet";
13
13
 
14
- // The combined progress
14
+ // The combined progress (current + next, 0-2 range)
15
15
  const progress = current.progress + (next?.progress ?? 0);
16
16
 
17
17
  // Whether the current screen is focused