react-native-screen-transitions 2.2.0 → 2.3.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 (96) hide show
  1. package/lib/commonjs/components/bound-capture.js +7 -2
  2. package/lib/commonjs/components/bound-capture.js.map +1 -1
  3. package/lib/commonjs/components/controllers/screen-lifecycle.js +3 -9
  4. package/lib/commonjs/components/controllers/screen-lifecycle.js.map +1 -1
  5. package/lib/commonjs/components/create-transition-aware-component.js +2 -1
  6. package/lib/commonjs/components/create-transition-aware-component.js.map +1 -1
  7. package/lib/commonjs/components/root-transition-aware.js +3 -1
  8. package/lib/commonjs/components/root-transition-aware.js.map +1 -1
  9. package/lib/commonjs/configs/presets.js +10 -16
  10. package/lib/commonjs/configs/presets.js.map +1 -1
  11. package/lib/commonjs/hooks/animation/use-associated-style.js +41 -6
  12. package/lib/commonjs/hooks/animation/use-associated-style.js.map +1 -1
  13. package/lib/commonjs/hooks/animation/use-screen-animation.js +8 -2
  14. package/lib/commonjs/hooks/animation/use-screen-animation.js.map +1 -1
  15. package/lib/commonjs/hooks/bounds/use-bound-registry.js +8 -6
  16. package/lib/commonjs/hooks/bounds/use-bound-registry.js.map +1 -1
  17. package/lib/commonjs/providers/transition-styles.js +5 -1
  18. package/lib/commonjs/providers/transition-styles.js.map +1 -1
  19. package/lib/commonjs/stores/bounds/_utils.js +118 -0
  20. package/lib/commonjs/stores/bounds/_utils.js.map +1 -0
  21. package/lib/commonjs/stores/bounds/index.js +116 -0
  22. package/lib/commonjs/stores/bounds/index.js.map +1 -0
  23. package/lib/commonjs/stores/utils/reset-stores-for-screen.js +1 -4
  24. package/lib/commonjs/stores/utils/reset-stores-for-screen.js.map +1 -1
  25. package/lib/commonjs/utils/animation/derivations.js +14 -1
  26. package/lib/commonjs/utils/animation/derivations.js.map +1 -1
  27. package/lib/module/components/bound-capture.js +7 -2
  28. package/lib/module/components/bound-capture.js.map +1 -1
  29. package/lib/module/components/controllers/screen-lifecycle.js +3 -9
  30. package/lib/module/components/controllers/screen-lifecycle.js.map +1 -1
  31. package/lib/module/components/create-transition-aware-component.js +2 -1
  32. package/lib/module/components/create-transition-aware-component.js.map +1 -1
  33. package/lib/module/components/root-transition-aware.js +3 -1
  34. package/lib/module/components/root-transition-aware.js.map +1 -1
  35. package/lib/module/configs/presets.js +10 -16
  36. package/lib/module/configs/presets.js.map +1 -1
  37. package/lib/module/hooks/animation/use-associated-style.js +42 -7
  38. package/lib/module/hooks/animation/use-associated-style.js.map +1 -1
  39. package/lib/module/hooks/animation/use-screen-animation.js +8 -2
  40. package/lib/module/hooks/animation/use-screen-animation.js.map +1 -1
  41. package/lib/module/hooks/bounds/use-bound-registry.js +8 -6
  42. package/lib/module/hooks/bounds/use-bound-registry.js.map +1 -1
  43. package/lib/module/providers/transition-styles.js +5 -1
  44. package/lib/module/providers/transition-styles.js.map +1 -1
  45. package/lib/module/stores/bounds/_utils.js +113 -0
  46. package/lib/module/stores/bounds/_utils.js.map +1 -0
  47. package/lib/module/stores/bounds/index.js +112 -0
  48. package/lib/module/stores/bounds/index.js.map +1 -0
  49. package/lib/module/stores/utils/reset-stores-for-screen.js +1 -4
  50. package/lib/module/stores/utils/reset-stores-for-screen.js.map +1 -1
  51. package/lib/module/utils/animation/derivations.js +14 -1
  52. package/lib/module/utils/animation/derivations.js.map +1 -1
  53. package/lib/typescript/components/bound-capture.d.ts.map +1 -1
  54. package/lib/typescript/components/create-transition-aware-component.d.ts.map +1 -1
  55. package/lib/typescript/configs/index.d.ts.map +1 -1
  56. package/lib/typescript/configs/presets.d.ts.map +1 -1
  57. package/lib/typescript/hooks/animation/use-associated-style.d.ts +4 -4
  58. package/lib/typescript/hooks/animation/use-associated-style.d.ts.map +1 -1
  59. package/lib/typescript/hooks/animation/use-screen-animation.d.ts.map +1 -1
  60. package/lib/typescript/hooks/bounds/use-bound-registry.d.ts.map +1 -1
  61. package/lib/typescript/index.d.ts.map +1 -1
  62. package/lib/typescript/providers/transition-styles.d.ts +4 -1
  63. package/lib/typescript/providers/transition-styles.d.ts.map +1 -1
  64. package/lib/typescript/stores/bounds/_utils.d.ts +24 -0
  65. package/lib/typescript/stores/bounds/_utils.d.ts.map +1 -0
  66. package/lib/typescript/stores/{bounds.d.ts → bounds/index.d.ts} +3 -13
  67. package/lib/typescript/stores/bounds/index.d.ts.map +1 -0
  68. package/lib/typescript/stores/utils/reset-stores-for-screen.d.ts +1 -3
  69. package/lib/typescript/stores/utils/reset-stores-for-screen.d.ts.map +1 -1
  70. package/lib/typescript/types/animation.d.ts +6 -0
  71. package/lib/typescript/types/animation.d.ts.map +1 -1
  72. package/lib/typescript/utils/animation/derivations.d.ts +3 -0
  73. package/lib/typescript/utils/animation/derivations.d.ts.map +1 -1
  74. package/package.json +3 -3
  75. package/src/__tests__/bounds.store.test.ts +185 -0
  76. package/src/components/bound-capture.tsx +5 -2
  77. package/src/components/controllers/screen-lifecycle.tsx +3 -3
  78. package/src/components/create-transition-aware-component.tsx +1 -0
  79. package/src/components/root-transition-aware.tsx +1 -1
  80. package/src/configs/presets.ts +9 -25
  81. package/src/hooks/animation/use-associated-style.tsx +42 -7
  82. package/src/hooks/animation/use-screen-animation.tsx +12 -1
  83. package/src/hooks/bounds/use-bound-registry.tsx +9 -12
  84. package/src/providers/transition-styles.tsx +8 -2
  85. package/src/stores/bounds/_utils.ts +161 -0
  86. package/src/stores/bounds/index.ts +125 -0
  87. package/src/stores/utils/reset-stores-for-screen.ts +1 -7
  88. package/src/types/animation.ts +6 -0
  89. package/src/utils/animation/derivations.ts +20 -1
  90. package/LICENSE +0 -21
  91. package/lib/commonjs/stores/bounds.js +0 -205
  92. package/lib/commonjs/stores/bounds.js.map +0 -1
  93. package/lib/module/stores/bounds.js +0 -201
  94. package/lib/module/stores/bounds.js.map +0 -1
  95. package/lib/typescript/stores/bounds.d.ts.map +0 -1
  96. package/src/stores/bounds.ts +0 -227
@@ -0,0 +1,24 @@
1
+ import type { ScreenTransitionState } from "../../types/animation";
2
+ type GetCache = (fromKey: string, toKey: string) => string | null;
3
+ type SetCache = (fromKey: string, toKey: string, id: string) => void;
4
+ type GetRouteActive = (routeKey: string) => string | null;
5
+ interface ResolveActiveBoundParams {
6
+ current: ScreenTransitionState;
7
+ next?: ScreenTransitionState;
8
+ previous?: ScreenTransitionState;
9
+ getPairCache: GetCache;
10
+ setPairCache: SetCache;
11
+ getRouteActive: GetRouteActive;
12
+ }
13
+ export declare function pairKey(fromKey?: string, toKey?: string): string;
14
+ /**
15
+ * Util function to get the active bound id for a given transition state.
16
+ *
17
+ * It will check by ( priority from highest to lowest ):
18
+ * 1. Requested id
19
+ * 2. Cache
20
+ * 3. Intersection
21
+ */
22
+ export declare function resolveActiveBound({ current, next, previous, getPairCache, setPairCache, getRouteActive, }: ResolveActiveBoundParams): string;
23
+ export {};
24
+ //# sourceMappingURL=_utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_utils.d.ts","sourceRoot":"","sources":["../../../../src/stores/bounds/_utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,KAAK,QAAQ,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAClE,KAAK,QAAQ,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;AACrE,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAE1D,UAAU,wBAAwB;IACjC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,YAAY,EAAE,QAAQ,CAAC;IACvB,YAAY,EAAE,QAAQ,CAAC;IACvB,cAAc,EAAE,cAAc,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,UAGvD;AA2ED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAClC,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,cAAc,GACd,EAAE,wBAAwB,UAoD1B"}
@@ -1,32 +1,22 @@
1
1
  import { type MeasuredDimensions, type StyleProps } from "react-native-reanimated";
2
- import type { ScreenTransitionState } from "../types/animation";
3
- import type { ScreenKey } from "../types/navigator";
2
+ import type { ScreenTransitionState } from "../../types/animation";
3
+ import type { ScreenKey } from "../../types/navigator";
4
4
  declare function setBounds(screenId: string, boundId: string, bounds?: MeasuredDimensions | null, styles?: StyleProps): void;
5
5
  declare function getBounds(screenId: string): Record<string, {
6
6
  bounds: MeasuredDimensions;
7
7
  styles: StyleProps;
8
8
  }>;
9
- declare function setActiveBoundId(boundId: string): void;
10
- declare function getActiveBoundId(): string | null;
11
9
  declare function setRouteActive(routeKey: string, boundId: string): void;
12
10
  declare function getRouteActive(routeKey: string): string;
13
- declare function setTransitionHint(fromKey: string, toKey: string, boundId: string): void;
14
- declare function getTransitionHint(fromKey: string, toKey: string): string | null;
15
11
  declare function clear(routeKey: ScreenKey): void;
16
- declare function clearActive(): void;
17
12
  declare function getActiveBound(current: ScreenTransitionState, next: ScreenTransitionState | undefined, previous: ScreenTransitionState | undefined): string;
18
13
  export declare const Bounds: {
19
14
  setBounds: typeof setBounds;
20
15
  getBounds: typeof getBounds;
21
- setActiveBoundId: typeof setActiveBoundId;
22
- getActiveBoundId: typeof getActiveBoundId;
23
16
  setRouteActive: typeof setRouteActive;
24
17
  getRouteActive: typeof getRouteActive;
25
- setTransitionHint: typeof setTransitionHint;
26
- getTransitionHint: typeof getTransitionHint;
27
18
  clear: typeof clear;
28
- clearActive: typeof clearActive;
29
19
  getActiveBound: typeof getActiveBound;
30
20
  };
31
21
  export {};
32
- //# sourceMappingURL=bounds.d.ts.map
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/stores/bounds/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EACf,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAavD,iBAAS,SAAS,CACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,kBAAkB,GAAG,IAAW,EACxC,MAAM,GAAE,UAAe,QAYvB;AAED,iBAAS,SAAS,CAAC,QAAQ,EAAE,MAAM;YAzBT,kBAAkB;YAAU,UAAU;GA4B/D;AAED,iBAAS,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAOxD;AAED,iBAAS,cAAc,CAAC,QAAQ,EAAE,MAAM,UAGvC;AAoBD,iBAAS,KAAK,CAAC,QAAQ,EAAE,SAAS,QAyBjC;AAED,iBAAS,cAAc,CACtB,OAAO,EAAE,qBAAqB,EAC9B,IAAI,EAAE,qBAAqB,GAAG,SAAS,EACvC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,UAW3C;AAED,eAAO,MAAM,MAAM;;;;;;;CAOlB,CAAC"}
@@ -2,7 +2,5 @@ import type { NativeStackDescriptor } from "../../types/navigator";
2
2
  /**
3
3
  * Reset all stores for a given screen
4
4
  */
5
- export declare const resetStoresForScreen: (current: NativeStackDescriptor, options?: {
6
- clearActive?: boolean;
7
- }) => void;
5
+ export declare const resetStoresForScreen: (current: NativeStackDescriptor) => void;
8
6
  //# sourceMappingURL=reset-stores-for-screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reset-stores-for-screen.d.ts","sourceRoot":"","sources":["../../../../src/stores/utils/reset-stores-for-screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAKnE;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,qBAAqB,EAC9B,UAAS;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,SAQvC,CAAC"}
1
+ {"version":3,"file":"reset-stores-for-screen.d.ts","sourceRoot":"","sources":["../../../../src/stores/utils/reset-stores-for-screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAKnE;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,qBAAqB,SAIlE,CAAC"}
@@ -35,6 +35,12 @@ export interface ScreenInterpolationProps {
35
35
  progress: number;
36
36
  /** A function that returns a bounds builder for the screen. */
37
37
  bounds: BoundsAccessor;
38
+ /** The active screen between current and next. */
39
+ active: ScreenTransitionState;
40
+ /** Whether the active screen is transitioning. */
41
+ isActiveTransitioning: boolean;
42
+ /** Whether the active screen is dismissing. */
43
+ isDismissing: boolean;
38
44
  }
39
45
  export type ScreenStyleInterpolator = (props: ScreenInterpolationProps) => TransitionInterpolatedStyle;
40
46
  export type TransitionInterpolatedStyle = {
@@ -1 +1 @@
1
- {"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../../../src/types/animation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,KAAK,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACxC,yFAAyF;IACzF,QAAQ,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC5C,OAAO,EAAE,qBAAqB,CAAC;IAC/B,8EAA8E;IAC9E,IAAI,EAAE,qBAAqB,GAAG,SAAS,CAAC;IACxC,0CAA0C;IAC1C,OAAO,EAAE;QACR,wDAAwD;QACxD,MAAM,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;SACf,CAAC;KACF,CAAC;IACF,2CAA2C;IAC3C,MAAM,EAAE,UAAU,CAAC;IACnB,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,MAAM,uBAAuB,GAAG,CACrC,KAAK,EAAE,wBAAwB,KAC3B,2BAA2B,CAAC;AAEjC,MAAM,MAAM,2BAA2B,GAAG;IACzC;;OAEG;IACH,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;OAEG;IACH,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,eAAe,CAAC;CACxB"}
1
+ {"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../../../src/types/animation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,KAAK,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACxC,yFAAyF;IACzF,QAAQ,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC5C,OAAO,EAAE,qBAAqB,CAAC;IAC/B,8EAA8E;IAC9E,IAAI,EAAE,qBAAqB,GAAG,SAAS,CAAC;IACxC,0CAA0C;IAC1C,OAAO,EAAE;QACR,wDAAwD;QACxD,MAAM,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,EAAE,MAAM,CAAC;SACf,CAAC;KACF,CAAC;IACF,2CAA2C;IAC3C,MAAM,EAAE,UAAU,CAAC;IACnB,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,MAAM,EAAE,cAAc,CAAC;IACvB,kDAAkD;IAClD,MAAM,EAAE,qBAAqB,CAAC;IAC9B,kDAAkD;IAClD,qBAAqB,EAAE,OAAO,CAAC;IAC/B,+CAA+C;IAC/C,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,uBAAuB,GAAG,CACrC,KAAK,EAAE,wBAAwB,KAC3B,2BAA2B,CAAC;AAEjC,MAAM,MAAM,2BAA2B,GAAG;IACzC;;OAEG;IACH,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;OAEG;IACH,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,CAAC,EAAE,eAAe,CAAC;CACxB"}
@@ -14,6 +14,9 @@ export declare const derivations: ({ current, next, previous, dimensions, }: Der
14
14
  focused: boolean;
15
15
  activeBoundId: string;
16
16
  bounds: import("../../types/bounds").BoundsAccessor;
17
+ active: ScreenTransitionState;
18
+ isActiveTransitioning: boolean;
19
+ isDismissing: boolean;
17
20
  };
18
21
  export {};
19
22
  //# sourceMappingURL=derivations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"derivations.d.ts","sourceRoot":"","sources":["../../../../src/utils/animation/derivations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,UAAU,iBAAiB;IAC1B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,UAAU,EAAE,UAAU,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,0CAKzB,iBAAiB;;;;;CAiBnB,CAAC"}
1
+ {"version":3,"file":"derivations.d.ts","sourceRoot":"","sources":["../../../../src/utils/animation/derivations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,UAAU,iBAAiB;IAC1B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,UAAU,EAAE,UAAU,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,0CAKzB,iBAAiB;;;;;;;;CAoCnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-screen-transitions",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Easy screen transitions for React Native and Expo",
5
5
  "author": "Ed",
6
6
  "license": "MIT",
@@ -49,7 +49,7 @@
49
49
  "@testing-library/react-native": "^13.2.0",
50
50
  "@types/react": "~19.0.10",
51
51
  "react-native-builder-bob": "0.39.0",
52
- "typescript": "~5.8.3"
52
+ "typescript": "catalog:"
53
53
  },
54
54
  "react-native-builder-bob": {
55
55
  "source": "src",
@@ -65,5 +65,5 @@
65
65
  ]
66
66
  ]
67
67
  },
68
- "gitHead": "ea83a2b1bc369610cde35850de314c43122ba60c"
68
+ "gitHead": "888d4df9936ec0e3b8221bea7cd81115e6301693"
69
69
  }
@@ -0,0 +1,185 @@
1
+ import { beforeEach, describe, expect, it } from "bun:test";
2
+ import { resolveActiveBound } from "../stores/bounds/_utils";
3
+ import type { ScreenTransitionState } from "../types/animation";
4
+
5
+ type Dim = {
6
+ x: number;
7
+ y: number;
8
+ pageX: number;
9
+ pageY: number;
10
+ width: number;
11
+ height: number;
12
+ };
13
+
14
+ const getDimensions = (x = 0, y = 0, w = 100, h = 100): Dim => ({
15
+ x,
16
+ y,
17
+ pageX: x,
18
+ pageY: y,
19
+ width: w,
20
+ height: h,
21
+ });
22
+
23
+ const mockState = (
24
+ routeKey: string,
25
+ ids: string[] = [],
26
+ ): ScreenTransitionState => {
27
+ const bounds: Record<
28
+ string,
29
+ { bounds: Dim; styles: Record<string, unknown> }
30
+ > = {};
31
+ ids.forEach((id, i) => {
32
+ bounds[id] = { bounds: getDimensions(i * 10, i * 10), styles: {} };
33
+ });
34
+ return {
35
+ progress: 1,
36
+ closing: 0,
37
+ animating: 1,
38
+ gesture: {
39
+ x: 0,
40
+ y: 0,
41
+ normalizedX: 0,
42
+ normalizedY: 0,
43
+ isDismissing: 0,
44
+ isDragging: 0,
45
+ },
46
+ bounds,
47
+ // @ts-expect-error partial route
48
+ route: { key: routeKey },
49
+ };
50
+ };
51
+
52
+ let cache: Record<string, string>;
53
+ let lastActiveByRoute: Record<string, string>;
54
+ beforeEach(() => {
55
+ cache = {};
56
+ lastActiveByRoute = {};
57
+ });
58
+
59
+ const getPairCache = (from: string, to: string) =>
60
+ cache[`${from}|${to}`] ?? null;
61
+ const setPairCache = (from: string, to: string, id: string) => {
62
+ cache[`${from}|${to}`] = id;
63
+ };
64
+ const getRouteActive = (routeKey: string) =>
65
+ lastActiveByRoute[routeKey] ?? null;
66
+
67
+ describe("Bounds.getActiveBound - requested id priority and acceptance", () => {
68
+ it("selects requested id when present only on current (opening)", () => {
69
+ const A = "A-1";
70
+ const B = "B-1";
71
+ const current = mockState(A, ["container"]);
72
+ const previous = mockState(B, ["icon"]);
73
+
74
+ // Opening: fromKey is previous.route.key (B)
75
+ lastActiveByRoute[B] = "container";
76
+ const active = resolveActiveBound({
77
+ current,
78
+ previous,
79
+ getPairCache,
80
+ setPairCache,
81
+ getRouteActive,
82
+ });
83
+
84
+ expect(active).toBe("container");
85
+ expect(getPairCache(B, A)).toBeNull();
86
+ });
87
+
88
+ it("selects requested id when present only on other (closing)", () => {
89
+ const A = "A-2";
90
+ const B = "B-2";
91
+ const current = mockState(A, ["icon"]);
92
+ const next = mockState(B, ["container"]);
93
+
94
+ // Closing: fromKey is current.route.key (A)
95
+ lastActiveByRoute[A] = "container";
96
+ const active = resolveActiveBound({
97
+ current,
98
+ next,
99
+ getPairCache,
100
+ setPairCache,
101
+ getRouteActive,
102
+ });
103
+ expect(active).toBe("container");
104
+ expect(getPairCache(A, B)).toBeNull();
105
+ });
106
+ });
107
+
108
+ describe("Bounds.getActiveBound - hint behavior (guarded writes)", () => {
109
+ it("writes cache only when both sides have the id", () => {
110
+ const A = "A-3";
111
+ const B = "B-3";
112
+ // Both have the same id measured
113
+ const current = mockState(A, ["container"]);
114
+ const previous = mockState(B, ["container"]);
115
+
116
+ // Opening: fromKey is previous.route.key (B)
117
+ lastActiveByRoute[B] = "container";
118
+ const active = resolveActiveBound({
119
+ current,
120
+ previous,
121
+ getPairCache,
122
+ setPairCache,
123
+ getRouteActive,
124
+ });
125
+ expect(active).toBe("container");
126
+ expect(getPairCache(B, A)).toBe("container");
127
+ });
128
+
129
+ it("requested id overrides existing cache and updates it when both sides measured", () => {
130
+ const A = "A-4";
131
+ const B = "B-4";
132
+ // Both sides have icon and container
133
+ // Pre-seed a conflicting cache
134
+ setPairCache(B, A, "icon");
135
+
136
+ const current = mockState(A, ["icon", "container"]);
137
+ const previous = mockState(B, ["icon", "container"]);
138
+
139
+ // Opening: fromKey is previous.route.key (B)
140
+ lastActiveByRoute[B] = "container";
141
+ const active = resolveActiveBound({
142
+ current,
143
+ previous,
144
+ getPairCache,
145
+ setPairCache,
146
+ getRouteActive,
147
+ });
148
+ expect(active).toBe("container");
149
+ expect(getPairCache(B, A)).toBe("container");
150
+ });
151
+ });
152
+
153
+ describe("Bounds.getActiveBound - set intersection and fallbacks", () => {
154
+ it("falls back to intersection when no request or cache", () => {
155
+ const A = "A-5";
156
+ const B = "B-5";
157
+ const current = mockState(A, ["alpha", "beta"]);
158
+ const previous = mockState(B, ["beta", "gamma"]);
159
+
160
+ const active = resolveActiveBound({
161
+ current,
162
+ previous,
163
+ getPairCache,
164
+ setPairCache,
165
+ getRouteActive,
166
+ });
167
+ expect(active).toBe("beta");
168
+ });
169
+
170
+ it("when other has a single bound, selects it (no request/cache)", () => {
171
+ const A = "A-6";
172
+ const B = "B-6";
173
+ const current = mockState(A, ["alpha"]);
174
+ const previous = mockState(B, ["only"]);
175
+
176
+ const active = resolveActiveBound({
177
+ current,
178
+ previous,
179
+ getPairCache,
180
+ setPairCache,
181
+ getRouteActive,
182
+ });
183
+ expect(active).toBe("only");
184
+ });
185
+ });
@@ -1,5 +1,6 @@
1
1
  import { useMemo } from "react";
2
2
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
3
+ import { useKeys } from "../providers/keys";
3
4
  import { Bounds } from "../stores/bounds";
4
5
 
5
6
  interface BoundActivatorProps {
@@ -13,15 +14,17 @@ export const BoundCapture = ({
13
14
  children,
14
15
  measure,
15
16
  }: BoundActivatorProps) => {
17
+ const { current } = useKeys();
18
+ const routeKey = current.route.key;
16
19
  const tapGesture = useMemo(() => {
17
20
  return Gesture.Tap().onStart(() => {
18
21
  "worklet";
19
22
  if (sharedBoundTag) {
20
- Bounds.setActiveBoundId(sharedBoundTag);
23
+ Bounds.setRouteActive(routeKey, sharedBoundTag);
21
24
  measure();
22
25
  }
23
26
  });
24
- }, [sharedBoundTag, measure]);
27
+ }, [sharedBoundTag, measure, routeKey]);
25
28
 
26
29
  if (!sharedBoundTag) return children;
27
30
 
@@ -24,13 +24,13 @@ export const ScreenLifecycleController = ({
24
24
 
25
25
  // Don't run e.preventDefault when the dismissal was on the local root
26
26
  if (requestedDismissOnNavigator) {
27
- resetStoresForScreen(current, { clearActive: true });
27
+ resetStoresForScreen(current);
28
28
  return;
29
29
  }
30
30
 
31
31
  // Don't run e.preventDefault when this is the first screen of the stack
32
32
  if (current.navigation.getState().index === 0) {
33
- resetStoresForScreen(current, { clearActive: true });
33
+ resetStoresForScreen(current);
34
34
  return;
35
35
  }
36
36
 
@@ -41,7 +41,7 @@ export const ScreenLifecycleController = ({
41
41
 
42
42
  // we'll ensure the dispatch is complete before resetting stores
43
43
  requestAnimationFrame(() => {
44
- resetStoresForScreen(current, { clearActive: false });
44
+ resetStoresForScreen(current);
45
45
  });
46
46
  }
47
47
  };
@@ -68,6 +68,7 @@ export function createTransitionAwareComponent<P extends object>(
68
68
 
69
69
  const { associatedStyles } = useAssociatedStyles({
70
70
  id: sharedBoundTag || styleId,
71
+ style,
71
72
  });
72
73
 
73
74
  const { measureBounds, handleLayout } = useBoundsRegistry({
@@ -13,7 +13,7 @@ type Props = {
13
13
  const EMPTY_STYLE = Object.freeze({} as StyleProps);
14
14
 
15
15
  export const RootTransitionAware = memo(({ children }: Props) => {
16
- const stylesMap = useTransitionStyles();
16
+ const { stylesMap } = useTransitionStyles();
17
17
 
18
18
  const animatedContentStyle = useAnimatedStyle(() => {
19
19
  "worklet";
@@ -17,16 +17,13 @@ export const SlideFromTop = (
17
17
  gestureEnabled: true,
18
18
  gestureDirection: "vertical-inverted",
19
19
  screenStyleInterpolator: ({
20
- current,
21
- next,
20
+ progress,
22
21
  layouts: {
23
22
  screen: { height },
24
23
  },
25
24
  }) => {
26
25
  "worklet";
27
26
 
28
- const progress = current.progress + (next?.progress ?? 0);
29
-
30
27
  const y = interpolate(progress, [0, 1, 2], [-height, 0, height]);
31
28
 
32
29
  return {
@@ -50,11 +47,9 @@ export const ZoomIn = (
50
47
  return {
51
48
  enableTransitions: true,
52
49
  gestureEnabled: false,
53
- screenStyleInterpolator: ({ current, next }) => {
50
+ screenStyleInterpolator: ({ progress }) => {
54
51
  "worklet";
55
52
 
56
- const progress = current.progress + (next?.progress ?? 0);
57
-
58
53
  const scale = interpolate(
59
54
  progress,
60
55
  [0, 1, 2],
@@ -92,16 +87,13 @@ export const SlideFromBottom = (
92
87
  gestureEnabled: true,
93
88
  gestureDirection: "vertical",
94
89
  screenStyleInterpolator: ({
95
- current,
96
- next,
97
90
  layouts: {
98
91
  screen: { height },
99
92
  },
93
+ progress,
100
94
  }) => {
101
95
  "worklet";
102
96
 
103
- const progress = current.progress + (next?.progress ?? 0);
104
-
105
97
  const y = interpolate(progress, [0, 1, 2], [height, 0, -height]);
106
98
 
107
99
  return {
@@ -242,16 +234,12 @@ export const SharedIGImage = (
242
234
  progress,
243
235
  focused,
244
236
  activeBoundId,
245
- next,
237
+ active,
246
238
  }) => {
247
239
  "worklet";
248
240
 
249
- const normX = focused
250
- ? current.gesture.normalizedX
251
- : (next?.gesture.normalizedX ?? 0);
252
- const normY = focused
253
- ? current.gesture.normalizedY
254
- : (next?.gesture.normalizedY ?? 0);
241
+ const normX = active.gesture.normalizedX;
242
+ const normY = active.gesture.normalizedY;
255
243
 
256
244
  // animations for both bounds
257
245
  const dragX = interpolate(
@@ -369,16 +357,12 @@ export const SharedAppleMusic = (
369
357
  progress,
370
358
  layouts: { screen },
371
359
  current,
372
- next,
360
+ active,
373
361
  }) => {
374
362
  "worklet";
375
363
 
376
- const normX = focused
377
- ? current.gesture.normalizedX
378
- : (next?.gesture.normalizedX ?? 0);
379
- const normY = focused
380
- ? current.gesture.normalizedY
381
- : (next?.gesture.normalizedY ?? 0);
364
+ const normX = active.gesture.normalizedX;
365
+ const normY = active.gesture.normalizedY;
382
366
 
383
367
  /**
384
368
  * ===============================
@@ -1,19 +1,37 @@
1
- import { useAnimatedStyle } from "react-native-reanimated";
1
+ import {
2
+ type StyleProps,
3
+ useAnimatedStyle,
4
+ useDerivedValue,
5
+ useSharedValue,
6
+ } from "react-native-reanimated";
2
7
  import { useTransitionStyles } from "../../providers/transition-styles";
3
8
 
4
9
  type Props = {
5
10
  id?: string;
11
+ style?: StyleProps;
6
12
  };
7
13
 
8
14
  const EMPTY_STYLE = Object.freeze({});
9
15
 
10
16
  /**
11
- * This hook is used to get the associated styles for a given styleId.
12
- * It is used to get the associated styles for a given styleId.
13
- * It is used to get the associated styles for a given styleId.
17
+ * This hook is used to get the associated styles for a given styleId / boundTag.
14
18
  */
15
- export const useAssociatedStyles = ({ id }: Props = {}) => {
16
- const stylesMap = useTransitionStyles();
19
+ export const useAssociatedStyles = ({ id, style }: Props = {}) => {
20
+ const { stylesMap } = useTransitionStyles();
21
+ const showAfterFirstFrame = useSharedValue(false);
22
+
23
+ useDerivedValue(() => {
24
+ "worklet";
25
+ if (!id) {
26
+ showAfterFirstFrame.value = true;
27
+ return;
28
+ }
29
+ if (!showAfterFirstFrame.value) {
30
+ requestAnimationFrame(() => {
31
+ showAfterFirstFrame.value = true;
32
+ });
33
+ }
34
+ });
17
35
 
18
36
  const associatedStyles = useAnimatedStyle(() => {
19
37
  "worklet";
@@ -21,8 +39,25 @@ export const useAssociatedStyles = ({ id }: Props = {}) => {
21
39
  if (!id || !stylesMap) {
22
40
  return EMPTY_STYLE;
23
41
  }
42
+ const base =
43
+ (stylesMap.value[id] as Record<string, unknown>) || EMPTY_STYLE;
44
+
45
+ let opacity = 1;
46
+
47
+ if ("opacity" in base) {
48
+ opacity = base.opacity as number;
49
+ }
50
+ if (style && "opacity" in style) {
51
+ opacity = style.opacity as number;
52
+ }
53
+
54
+ // Only force opacity to 0 during the initial frame; once ready,
55
+ // return base unchanged so we never override user-provided opacity.
56
+ if (!showAfterFirstFrame.value) {
57
+ return { ...base, opacity: 0 } as Record<string, unknown>;
58
+ }
24
59
 
25
- return stylesMap.value[id] || EMPTY_STYLE;
60
+ return { ...base, opacity };
26
61
  });
27
62
 
28
63
  return { associatedStyles };
@@ -106,7 +106,15 @@ export function _useScreenAnimation() {
106
106
  const current =
107
107
  unwrap(currentAnimation, currentDescriptor?.route.key) ?? FALLBACK;
108
108
 
109
- const { progress, focused, activeBoundId, bounds } = derivations({
109
+ const {
110
+ progress,
111
+ focused,
112
+ activeBoundId,
113
+ bounds,
114
+ active,
115
+ isActiveTransitioning,
116
+ isDismissing,
117
+ } = derivations({
110
118
  current,
111
119
  next,
112
120
  previous,
@@ -123,6 +131,9 @@ export function _useScreenAnimation() {
123
131
  activeBoundId,
124
132
  progress,
125
133
  bounds,
134
+ active,
135
+ isActiveTransitioning,
136
+ isDismissing,
126
137
  };
127
138
  },
128
139
  );
@@ -10,7 +10,6 @@ import { useKeys } from "../../providers/keys";
10
10
  import { Bounds } from "../../stores/bounds";
11
11
  import { flattenStyle } from "../../utils/bounds/_utils/flatten-styles";
12
12
  import { isBoundsEqual } from "../../utils/bounds/_utils/is-bounds-equal";
13
- import { useScreenAnimation } from "../animation/use-screen-animation";
14
13
 
15
14
  interface BoundMeasurerHookProps {
16
15
  sharedBoundTag: string;
@@ -26,7 +25,7 @@ export const useBoundsRegistry = ({
26
25
  style,
27
26
  }: BoundMeasurerHookProps) => {
28
27
  const { previous } = useKeys();
29
- const interpolatorProps = useScreenAnimation();
28
+
30
29
  const isMeasured = useSharedValue(false);
31
30
 
32
31
  const measureBounds = useCallback(() => {
@@ -36,12 +35,16 @@ export const useBoundsRegistry = ({
36
35
  if (measured) {
37
36
  const key = current.route.key;
38
37
  if (isBoundsEqual({ measured, key, sharedBoundTag })) {
39
- Bounds.setRouteActive(key, sharedBoundTag);
38
+ if (Bounds.getRouteActive(key) === sharedBoundTag) {
39
+ Bounds.setRouteActive(key, sharedBoundTag);
40
+ }
40
41
  return;
41
42
  }
42
43
 
43
44
  Bounds.setBounds(key, sharedBoundTag, measured, flattenStyle(style));
44
- Bounds.setRouteActive(key, sharedBoundTag);
45
+ if (Bounds.getRouteActive(key) === sharedBoundTag) {
46
+ Bounds.setRouteActive(key, sharedBoundTag);
47
+ }
45
48
  }
46
49
  }, [sharedBoundTag, animatedRef, current.route.key, style]);
47
50
 
@@ -56,17 +59,11 @@ export const useBoundsRegistry = ({
56
59
  const previousBounds = Bounds.getBounds(previousRouteKey);
57
60
  const hasPreviousBoundForTag = previousBounds[sharedBoundTag];
58
61
 
59
- if (interpolatorProps.value.current.animating && hasPreviousBoundForTag) {
62
+ if (hasPreviousBoundForTag) {
60
63
  measureBounds();
61
64
  isMeasured.value = true;
62
65
  }
63
- }, [
64
- measureBounds,
65
- interpolatorProps,
66
- sharedBoundTag,
67
- previous?.route.key,
68
- isMeasured,
69
- ]);
66
+ }, [measureBounds, sharedBoundTag, previous?.route.key, isMeasured]);
70
67
 
71
68
  return {
72
69
  measureBounds,
@@ -10,7 +10,9 @@ type Props = {
10
10
  const EMPTY_MAP = Object.freeze({});
11
11
 
12
12
  const TransitionStylesContext = createContext<ReturnType<
13
- typeof useDerivedValue<TransitionInterpolatedStyle>
13
+ typeof useMemo<{
14
+ stylesMap: ReturnType<typeof useDerivedValue<TransitionInterpolatedStyle>>;
15
+ }>
14
16
  > | null>(null);
15
17
 
16
18
  export function TransitionStylesProvider({ children }: Props) {
@@ -32,7 +34,11 @@ export function TransitionStylesProvider({ children }: Props) {
32
34
  : EMPTY_MAP;
33
35
  });
34
36
 
35
- const value = useMemo(() => stylesMap, [stylesMap]);
37
+ const value = useMemo(() => {
38
+ return {
39
+ stylesMap,
40
+ };
41
+ }, [stylesMap]);
36
42
 
37
43
  return (
38
44
  <TransitionStylesContext.Provider value={value}>