react-native-screen-transitions 3.0.0-rc.5 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +360 -97
  2. package/lib/commonjs/blank-stack/components/overlay.js +1 -1
  3. package/lib/commonjs/blank-stack/components/overlay.js.map +1 -1
  4. package/lib/commonjs/shared/components/create-transition-aware-component.js +2 -0
  5. package/lib/commonjs/shared/components/create-transition-aware-component.js.map +1 -1
  6. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +9 -4
  7. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
  8. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js +29 -5
  9. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  10. package/lib/commonjs/shared/hooks/gestures/use-screen-gesture.js +26 -0
  11. package/lib/commonjs/shared/hooks/gestures/use-screen-gesture.js.map +1 -0
  12. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js +32 -60
  13. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  14. package/lib/commonjs/shared/index.js +7 -0
  15. package/lib/commonjs/shared/index.js.map +1 -1
  16. package/lib/commonjs/shared/providers/gestures.provider.js +8 -18
  17. package/lib/commonjs/shared/providers/gestures.provider.js.map +1 -1
  18. package/lib/commonjs/shared/utils/animation/derivations.js +7 -0
  19. package/lib/commonjs/shared/utils/animation/derivations.js.map +1 -1
  20. package/lib/commonjs/shared/utils/bounds/helpers/interpolate-style.js +30 -0
  21. package/lib/commonjs/shared/utils/bounds/helpers/interpolate-style.js.map +1 -0
  22. package/lib/commonjs/shared/utils/bounds/index.js +29 -1
  23. package/lib/commonjs/shared/utils/bounds/index.js.map +1 -1
  24. package/lib/commonjs/shared/utils/create-provider.js +16 -0
  25. package/lib/commonjs/shared/utils/create-provider.js.map +1 -1
  26. package/lib/commonjs/shared/utils/gesture/check-gesture-activation.js +4 -0
  27. package/lib/commonjs/shared/utils/gesture/check-gesture-activation.js.map +1 -1
  28. package/lib/module/blank-stack/components/overlay.js +1 -1
  29. package/lib/module/blank-stack/components/overlay.js.map +1 -1
  30. package/lib/module/shared/components/create-transition-aware-component.js +2 -0
  31. package/lib/module/shared/components/create-transition-aware-component.js.map +1 -1
  32. package/lib/module/shared/hooks/animation/use-screen-animation.js +9 -4
  33. package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
  34. package/lib/module/shared/hooks/gestures/use-build-gestures.js +30 -6
  35. package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  36. package/lib/module/shared/hooks/gestures/use-screen-gesture.js +22 -0
  37. package/lib/module/shared/hooks/gestures/use-screen-gesture.js.map +1 -0
  38. package/lib/module/shared/hooks/gestures/use-scroll-registry.js +32 -60
  39. package/lib/module/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  40. package/lib/module/shared/index.js +1 -0
  41. package/lib/module/shared/index.js.map +1 -1
  42. package/lib/module/shared/providers/gestures.provider.js +9 -19
  43. package/lib/module/shared/providers/gestures.provider.js.map +1 -1
  44. package/lib/module/shared/utils/animation/derivations.js +7 -0
  45. package/lib/module/shared/utils/animation/derivations.js.map +1 -1
  46. package/lib/module/shared/utils/bounds/helpers/interpolate-style.js +26 -0
  47. package/lib/module/shared/utils/bounds/helpers/interpolate-style.js.map +1 -0
  48. package/lib/module/shared/utils/bounds/index.js +29 -1
  49. package/lib/module/shared/utils/bounds/index.js.map +1 -1
  50. package/lib/module/shared/utils/create-provider.js +17 -1
  51. package/lib/module/shared/utils/create-provider.js.map +1 -1
  52. package/lib/module/shared/utils/gesture/check-gesture-activation.js +4 -4
  53. package/lib/module/shared/utils/gesture/check-gesture-activation.js.map +1 -1
  54. package/lib/typescript/blank-stack/types.d.ts +2 -14
  55. package/lib/typescript/blank-stack/types.d.ts.map +1 -1
  56. package/lib/typescript/shared/components/create-transition-aware-component.d.ts +1 -0
  57. package/lib/typescript/shared/components/create-transition-aware-component.d.ts.map +1 -1
  58. package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
  59. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts +1 -0
  60. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
  61. package/lib/typescript/shared/hooks/gestures/use-screen-gesture.d.ts +15 -0
  62. package/lib/typescript/shared/hooks/gestures/use-screen-gesture.d.ts.map +1 -0
  63. package/lib/typescript/shared/hooks/gestures/use-scroll-registry.d.ts +1 -0
  64. package/lib/typescript/shared/hooks/gestures/use-scroll-registry.d.ts.map +1 -1
  65. package/lib/typescript/shared/index.d.ts +4 -2
  66. package/lib/typescript/shared/index.d.ts.map +1 -1
  67. package/lib/typescript/shared/providers/gestures.provider.d.ts +2 -6
  68. package/lib/typescript/shared/providers/gestures.provider.d.ts.map +1 -1
  69. package/lib/typescript/shared/types/animation.types.d.ts +51 -0
  70. package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
  71. package/lib/typescript/shared/types/bounds.types.d.ts +6 -0
  72. package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
  73. package/lib/typescript/shared/types/core.types.d.ts +7 -0
  74. package/lib/typescript/shared/types/core.types.d.ts.map +1 -1
  75. package/lib/typescript/shared/utils/animation/derivations.d.ts +3 -1
  76. package/lib/typescript/shared/utils/animation/derivations.d.ts.map +1 -1
  77. package/lib/typescript/shared/utils/bounds/helpers/interpolate-style.d.ts +17 -0
  78. package/lib/typescript/shared/utils/bounds/helpers/interpolate-style.d.ts.map +1 -0
  79. package/lib/typescript/shared/utils/bounds/index.d.ts.map +1 -1
  80. package/lib/typescript/shared/utils/create-provider.d.ts +5 -1
  81. package/lib/typescript/shared/utils/create-provider.d.ts.map +1 -1
  82. package/lib/typescript/shared/utils/gesture/check-gesture-activation.d.ts +49 -1
  83. package/lib/typescript/shared/utils/gesture/check-gesture-activation.d.ts.map +1 -1
  84. package/package.json +1 -1
  85. package/src/blank-stack/components/overlay.tsx +1 -1
  86. package/src/blank-stack/types.ts +2 -15
  87. package/src/shared/__tests__/derivations.test.ts +155 -0
  88. package/src/shared/__tests__/gesture-activation.test.ts +251 -0
  89. package/src/shared/components/create-transition-aware-component.tsx +2 -1
  90. package/src/shared/hooks/animation/use-screen-animation.tsx +9 -2
  91. package/src/shared/hooks/gestures/use-build-gestures.tsx +35 -8
  92. package/src/shared/hooks/gestures/use-screen-gesture.ts +19 -0
  93. package/src/shared/hooks/gestures/use-scroll-registry.tsx +39 -59
  94. package/src/shared/index.ts +2 -0
  95. package/src/shared/providers/gestures.provider.tsx +15 -27
  96. package/src/shared/types/animation.types.ts +57 -0
  97. package/src/shared/types/bounds.types.ts +11 -0
  98. package/src/shared/types/core.types.ts +8 -0
  99. package/src/shared/utils/animation/derivations.ts +8 -1
  100. package/src/shared/utils/bounds/helpers/interpolate-style.ts +38 -0
  101. package/src/shared/utils/bounds/index.ts +31 -1
  102. package/src/shared/utils/create-provider.tsx +31 -1
  103. package/src/shared/utils/gesture/check-gesture-activation.ts +4 -4
@@ -21,10 +21,59 @@ export interface OverlayInterpolationProps {
21
21
  }
22
22
 
23
23
  export type ScreenTransitionState = {
24
+ /**
25
+ * Animation progress for this screen.
26
+ * - `0`: Screen is fully off-screen (entering)
27
+ * - `1`: Screen is fully visible (active)
28
+ *
29
+ * This value animates from 0 to 1 when the screen enters,
30
+ * and from 1 to 0 when it exits.
31
+ */
24
32
  progress: number;
33
+
34
+ /**
35
+ * Whether this screen is in the process of being dismissed.
36
+ * - `0`: Screen is opening or active
37
+ * - `1`: Screen is closing/being dismissed
38
+ *
39
+ * Use this to trigger different animations when navigating back vs forward.
40
+ */
25
41
  closing: number;
42
+
43
+ /**
44
+ * Whether this screen is currently animating.
45
+ * - `0`: No animation in progress
46
+ * - `1`: Animation or gesture is in progress
47
+ */
26
48
  animating: number;
49
+
50
+ /**
51
+ * Live gesture values for this screen.
52
+ * Contains translation (x, y), normalized values (-1 to 1),
53
+ * and flags for dragging/dismissing state.
54
+ */
27
55
  gesture: GestureValues;
56
+
57
+ /**
58
+ * Custom metadata passed from screen options.
59
+ * Use this for conditional animation logic instead of checking route names.
60
+ *
61
+ * @example
62
+ * // In screen options:
63
+ * options={{ meta: { scalesOthers: true } }}
64
+ *
65
+ * // In animation logic:
66
+ * if (props.next?.meta?.scalesOthers) { ... }
67
+ */
68
+ meta?: Record<string, unknown>;
69
+
70
+ /**
71
+ * The route object for this screen.
72
+ *
73
+ * @deprecated Use `meta` instead for conditional animation logic.
74
+ * Pass route params via options: `options={({ route }) => ({ meta: { id: route.params.id } })}`
75
+ * This field may be removed in a future version.
76
+ */
28
77
  route: RouteProp<ParamListBase>;
29
78
  };
30
79
 
@@ -98,13 +147,21 @@ export interface ScreenInterpolationProps {
98
147
  */
99
148
  active: ScreenTransitionState;
100
149
 
150
+ /**
151
+ * The screen state that is NOT driving the transition.
152
+ * When focused, this is the previous screen. When not focused, this is the current screen.
153
+ */
154
+ inactive: ScreenTransitionState | undefined;
155
+
101
156
  /**
102
157
  * Whether the active screen is currently transitioning (either being dragged or animating).
158
+ * @deprecated Use `active.animating` instead.
103
159
  */
104
160
  isActiveTransitioning: boolean;
105
161
 
106
162
  /**
107
163
  * Whether the active screen is in the process of being dismissed/closed.
164
+ * @deprecated Use `active.closing` instead.
108
165
  */
109
166
  isDismissing: boolean;
110
167
  }
@@ -19,7 +19,18 @@ export type BoundEntry = {
19
19
  styles: StyleProps;
20
20
  };
21
21
 
22
+ export type BoundsLink = {
23
+ source: BoundEntry | null;
24
+ destination: BoundEntry | null;
25
+ };
26
+
22
27
  export type BoundsAccessor = {
23
28
  <T extends BoundsBuilderOptions>(options: T): BoundsReturnType<T>;
24
29
  getSnapshot: (id: string, key?: string) => Snapshot | null;
30
+ getLink: (id: string) => BoundsLink | null;
31
+ interpolateStyle: (
32
+ id: string,
33
+ property: keyof StyleProps,
34
+ fallback?: number,
35
+ ) => number;
25
36
  };
@@ -100,4 +100,12 @@ export type ScreenTransitionConfig = {
100
100
  * The area of the screen where the gesture is activated.
101
101
  */
102
102
  gestureActivationArea?: GestureActivationArea;
103
+
104
+ /**
105
+ * Custom metadata passed through to animation props.
106
+ *
107
+ * @example
108
+ * options={{ meta: { scalesOthers: true } }}
109
+ */
110
+ meta?: Record<string, unknown>;
103
111
  };
@@ -1,6 +1,7 @@
1
1
  import type { ScreenTransitionState } from "../../types/animation.types";
2
2
 
3
3
  interface DerivationsParams {
4
+ previous?: ScreenTransitionState;
4
5
  current: ScreenTransitionState;
5
6
  next?: ScreenTransitionState;
6
7
  }
@@ -8,7 +9,7 @@ interface DerivationsParams {
8
9
  /**
9
10
  * Additional values to help make defining animations easier.
10
11
  */
11
- export const derivations = ({ current, next }: DerivationsParams) => {
12
+ export const derivations = ({ previous, current, next }: DerivationsParams) => {
12
13
  "worklet";
13
14
 
14
15
  // The combined progress (current + next, 0-2 range)
@@ -17,7 +18,12 @@ export const derivations = ({ current, next }: DerivationsParams) => {
17
18
  // Whether the current screen is focused
18
19
  const focused = !next;
19
20
 
21
+ // The screen driving the transition
20
22
  const active = focused ? current : (next ?? current);
23
+
24
+ // The screen NOT driving the transition
25
+ const inactive = focused ? previous : current;
26
+
21
27
  const isActiveTransitioning = !!(
22
28
  active.gesture.isDragging || active.animating
23
29
  );
@@ -28,6 +34,7 @@ export const derivations = ({ current, next }: DerivationsParams) => {
28
34
  progress,
29
35
  focused,
30
36
  active,
37
+ inactive,
31
38
  isActiveTransitioning,
32
39
  isDismissing,
33
40
  };
@@ -0,0 +1,38 @@
1
+ import { interpolate } from "react-native-reanimated";
2
+ import { ENTER_RANGE, EXIT_RANGE } from "../../../constants";
3
+ import type { BoundsLink } from "../../../types/bounds.types";
4
+
5
+ type InterpolateStyleOptions = {
6
+ fallback?: number;
7
+ };
8
+
9
+ /**
10
+ * Interpolates a numeric style property between source and destination bounds.
11
+ *
12
+ * @param link - The bounds link containing source and destination styles
13
+ * @param property - The style property to interpolate (e.g., "borderRadius", "opacity")
14
+ * @param progress - Animation progress value
15
+ * @param entering - Whether the screen is entering (focused) or exiting (unfocused)
16
+ * @param options - Optional configuration
17
+ * @returns The interpolated value
18
+ */
19
+ export function interpolateLinkStyle(
20
+ link: BoundsLink | null,
21
+ property: string,
22
+ progress: number,
23
+ entering: boolean,
24
+ options: InterpolateStyleOptions = {},
25
+ ): number {
26
+ "worklet";
27
+
28
+ const { fallback = 0 } = options;
29
+
30
+ const sourceValue =
31
+ (link?.source?.styles?.[property] as number | undefined) ?? fallback;
32
+ const destValue =
33
+ (link?.destination?.styles?.[property] as number | undefined) ?? fallback;
34
+
35
+ const range = entering ? ENTER_RANGE : EXIT_RANGE;
36
+
37
+ return interpolate(progress, range, [sourceValue, destValue], "clamp");
38
+ }
@@ -11,12 +11,13 @@ import type {
11
11
  ScreenInterpolationProps,
12
12
  ScreenTransitionState,
13
13
  } from "../../types/animation.types";
14
- import type { BoundsAccessor } from "../../types/bounds.types";
14
+ import type { BoundsAccessor, BoundsLink } from "../../types/bounds.types";
15
15
  import type { Layout } from "../../types/core.types";
16
16
  import {
17
17
  computeContentTransformGeometry,
18
18
  computeRelativeGeometry,
19
19
  } from "./helpers/geometry";
20
+ import { interpolateLinkStyle } from "./helpers/interpolate-style";
20
21
  import {
21
22
  composeContentStyle,
22
23
  composeSizeAbsolute,
@@ -207,7 +208,36 @@ export const createBounds = (
207
208
  return BoundStore.getSnapshot(tag, key);
208
209
  };
209
210
 
211
+ const getLink = (tag: string): BoundsLink | null => {
212
+ "worklet";
213
+ const link = BoundStore.getActiveLink(tag, props.current?.route.key);
214
+ if (!link) return null;
215
+ return {
216
+ source: link.source
217
+ ? { bounds: link.source.bounds, styles: link.source.styles }
218
+ : null,
219
+ destination: link.destination
220
+ ? { bounds: link.destination.bounds, styles: link.destination.styles }
221
+ : null,
222
+ };
223
+ };
224
+
225
+ const interpolateStyle = (
226
+ tag: string,
227
+ property: string,
228
+ fallback?: number,
229
+ ): number => {
230
+ "worklet";
231
+ const link = getLink(tag);
232
+ const entering = !props.next;
233
+ return interpolateLinkStyle(link, property, props.progress, entering, {
234
+ fallback,
235
+ });
236
+ };
237
+
210
238
  return Object.assign(boundsFunction, {
211
239
  getSnapshot,
240
+ getLink,
241
+ interpolateStyle,
212
242
  }) as BoundsAccessor;
213
243
  };
@@ -9,8 +9,11 @@ import {
9
9
  type ReactNode,
10
10
  useContext,
11
11
  useMemo,
12
+ useRef,
12
13
  } from "react";
13
14
 
15
+ type InnerProviderComponent = (props: { children: ReactNode }) => ReactNode;
16
+
14
17
  export default function createProvider<
15
18
  ProviderName extends string,
16
19
  Guarded extends boolean = true,
@@ -19,7 +22,13 @@ export default function createProvider<
19
22
  factory: (props: ProviderProps) => {
20
23
  value?: ContextValue;
21
24
  enabled?: boolean;
22
- children?: ReactNode;
25
+ children?:
26
+ | ReactNode
27
+ | ((
28
+ innerProvider: {
29
+ [K in ProviderName as `${K}Provider`]: InnerProviderComponent;
30
+ },
31
+ ) => ReactNode);
23
32
  },
24
33
  ) => {
25
34
  const { guarded = true } = options ?? {};
@@ -45,6 +54,27 @@ export default function createProvider<
45
54
  [enabled, value],
46
55
  );
47
56
 
57
+ // Per-instance ref ensures InnerProvider reads latest value while keeping
58
+ // a stable component reference.
59
+ const valueRef = useRef<ContextValue | null>(memoValue);
60
+ valueRef.current = memoValue;
61
+
62
+ const InnerProvider = useMemo(
63
+ (): InnerProviderComponent =>
64
+ ({ children }) => (
65
+ <Context.Provider value={valueRef.current}>
66
+ {children}
67
+ </Context.Provider>
68
+ ),
69
+ [],
70
+ );
71
+
72
+ if (typeof children === "function") {
73
+ return children({
74
+ [`${name}Provider`]: InnerProvider,
75
+ } as { [K in ProviderName as `${K}Provider`]: InnerProviderComponent });
76
+ }
77
+
48
78
  return <Context.Provider value={memoValue}>{children}</Context.Provider>;
49
79
  };
50
80
 
@@ -72,7 +72,7 @@ const DEFAULT_EDGE_DISTANCE_HORIZONTAL = 50;
72
72
  const DEFAULT_EDGE_DISTANCE_VERTICAL = 135;
73
73
  const DEFAULT_ACTIVATION_AREA = "screen" as const;
74
74
 
75
- function normalizeSides(area?: GestureActivationArea): NormalizedSides {
75
+ export function normalizeSides(area?: GestureActivationArea): NormalizedSides {
76
76
  "worklet";
77
77
  if (!area || typeof area === "string") {
78
78
  const mode: ActivationArea = area ?? DEFAULT_ACTIVATION_AREA;
@@ -88,7 +88,7 @@ function normalizeSides(area?: GestureActivationArea): NormalizedSides {
88
88
  };
89
89
  }
90
90
 
91
- function computeEdgeConstraints(
91
+ export function computeEdgeConstraints(
92
92
  initialTouch: { x: number; y: number },
93
93
  dimensions: Layout,
94
94
  sides: NormalizedSides,
@@ -108,7 +108,7 @@ function computeEdgeConstraints(
108
108
  return { horizontalRight, horizontalLeft, verticalDown, verticalUp } as const;
109
109
  }
110
110
 
111
- function calculateSwipeDirs(deltaX: number, deltaY: number) {
111
+ export function calculateSwipeDirs(deltaX: number, deltaY: number) {
112
112
  "worklet";
113
113
 
114
114
  const isVerticalSwipe = Math.abs(deltaY) > Math.abs(deltaX);
@@ -129,7 +129,7 @@ function calculateSwipeDirs(deltaX: number, deltaY: number) {
129
129
  };
130
130
  }
131
131
 
132
- function shouldActivateOrFail(params: ShouldActivateOrFailProps) {
132
+ export function shouldActivateOrFail(params: ShouldActivateOrFailProps) {
133
133
  "worklet";
134
134
 
135
135
  const {