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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +105 -10
  2. package/lib/commonjs/blank-stack/components/adjusted-screen.js +2 -2
  3. package/lib/commonjs/blank-stack/components/adjusted-screen.js.map +1 -1
  4. package/lib/commonjs/shared/components/create-transition-aware-component.js +8 -2
  5. package/lib/commonjs/shared/components/create-transition-aware-component.js.map +1 -1
  6. package/lib/commonjs/shared/components/{root-transition-aware.js → screen-container.js} +28 -12
  7. package/lib/commonjs/shared/components/screen-container.js.map +1 -0
  8. package/lib/commonjs/shared/configs/presets.js +3 -3
  9. package/lib/commonjs/shared/configs/presets.js.map +1 -1
  10. package/lib/commonjs/shared/configs/specs.js +6 -1
  11. package/lib/commonjs/shared/configs/specs.js.map +1 -1
  12. package/lib/commonjs/shared/constants.js +36 -10
  13. package/lib/commonjs/shared/constants.js.map +1 -1
  14. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +25 -18
  15. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
  16. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js +25 -202
  17. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  18. package/lib/commonjs/shared/hooks/gestures/use-screen-gesture-handlers.js +342 -0
  19. package/lib/commonjs/shared/hooks/gestures/use-screen-gesture-handlers.js.map +1 -0
  20. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js +47 -4
  21. package/lib/commonjs/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  22. package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js +3 -3
  23. package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js.map +1 -1
  24. package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js +25 -3
  25. package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js.map +1 -1
  26. package/lib/commonjs/shared/hooks/navigation/use-screen-state.js +33 -2
  27. package/lib/commonjs/shared/hooks/navigation/use-screen-state.js.map +1 -1
  28. package/lib/commonjs/shared/hooks/use-backdrop-pointer-events.js +32 -0
  29. package/lib/commonjs/shared/hooks/use-backdrop-pointer-events.js.map +1 -0
  30. package/lib/commonjs/shared/providers/gestures.provider.js +4 -2
  31. package/lib/commonjs/shared/providers/gestures.provider.js.map +1 -1
  32. package/lib/commonjs/shared/providers/screen/screen-composer.js +2 -2
  33. package/lib/commonjs/shared/providers/screen/screen-composer.js.map +1 -1
  34. package/lib/commonjs/shared/utils/animation/{start-screen-transition.js → animate-to-progress.js} +16 -8
  35. package/lib/commonjs/shared/utils/animation/animate-to-progress.js.map +1 -0
  36. package/lib/commonjs/shared/utils/gesture/check-gesture-activation.js +138 -0
  37. package/lib/commonjs/shared/utils/gesture/check-gesture-activation.js.map +1 -1
  38. package/lib/commonjs/shared/utils/gesture/determine-snap-target.js +56 -0
  39. package/lib/commonjs/shared/utils/gesture/determine-snap-target.js.map +1 -0
  40. package/lib/commonjs/shared/utils/gesture/validate-snap-points.js +31 -0
  41. package/lib/commonjs/shared/utils/gesture/validate-snap-points.js.map +1 -0
  42. package/lib/commonjs/shared/utils/gesture/velocity.js +11 -0
  43. package/lib/commonjs/shared/utils/gesture/velocity.js.map +1 -1
  44. package/lib/commonjs/shared/utils/logger.js +22 -0
  45. package/lib/commonjs/shared/utils/logger.js.map +1 -0
  46. package/lib/module/blank-stack/components/adjusted-screen.js +1 -1
  47. package/lib/module/blank-stack/components/adjusted-screen.js.map +1 -1
  48. package/lib/module/shared/components/create-transition-aware-component.js +8 -2
  49. package/lib/module/shared/components/create-transition-aware-component.js.map +1 -1
  50. package/lib/module/shared/components/screen-container.js +64 -0
  51. package/lib/module/shared/components/screen-container.js.map +1 -0
  52. package/lib/module/shared/configs/presets.js +3 -3
  53. package/lib/module/shared/configs/presets.js.map +1 -1
  54. package/lib/module/shared/configs/specs.js +5 -0
  55. package/lib/module/shared/configs/specs.js.map +1 -1
  56. package/lib/module/shared/constants.js +34 -9
  57. package/lib/module/shared/constants.js.map +1 -1
  58. package/lib/module/shared/hooks/animation/use-screen-animation.js +25 -18
  59. package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
  60. package/lib/module/shared/hooks/gestures/use-build-gestures.js +25 -201
  61. package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  62. package/lib/module/shared/hooks/gestures/use-screen-gesture-handlers.js +336 -0
  63. package/lib/module/shared/hooks/gestures/use-screen-gesture-handlers.js.map +1 -0
  64. package/lib/module/shared/hooks/gestures/use-scroll-registry.js +47 -4
  65. package/lib/module/shared/hooks/gestures/use-scroll-registry.js.map +1 -1
  66. package/lib/module/shared/hooks/lifecycle/use-close-transition.js +3 -3
  67. package/lib/module/shared/hooks/lifecycle/use-close-transition.js.map +1 -1
  68. package/lib/module/shared/hooks/lifecycle/use-open-transition.js +25 -3
  69. package/lib/module/shared/hooks/lifecycle/use-open-transition.js.map +1 -1
  70. package/lib/module/shared/hooks/navigation/use-screen-state.js +35 -4
  71. package/lib/module/shared/hooks/navigation/use-screen-state.js.map +1 -1
  72. package/lib/module/shared/hooks/use-backdrop-pointer-events.js +28 -0
  73. package/lib/module/shared/hooks/use-backdrop-pointer-events.js.map +1 -0
  74. package/lib/module/shared/providers/gestures.provider.js +4 -2
  75. package/lib/module/shared/providers/gestures.provider.js.map +1 -1
  76. package/lib/module/shared/providers/screen/screen-composer.js +2 -2
  77. package/lib/module/shared/providers/screen/screen-composer.js.map +1 -1
  78. package/lib/module/shared/utils/animation/{start-screen-transition.js → animate-to-progress.js} +14 -6
  79. package/lib/module/shared/utils/animation/animate-to-progress.js.map +1 -0
  80. package/lib/module/shared/utils/gesture/check-gesture-activation.js +137 -0
  81. package/lib/module/shared/utils/gesture/check-gesture-activation.js.map +1 -1
  82. package/lib/module/shared/utils/gesture/determine-snap-target.js +52 -0
  83. package/lib/module/shared/utils/gesture/determine-snap-target.js.map +1 -0
  84. package/lib/module/shared/utils/gesture/validate-snap-points.js +26 -0
  85. package/lib/module/shared/utils/gesture/validate-snap-points.js.map +1 -0
  86. package/lib/module/shared/utils/gesture/velocity.js +11 -0
  87. package/lib/module/shared/utils/gesture/velocity.js.map +1 -1
  88. package/lib/module/shared/utils/logger.js +17 -0
  89. package/lib/module/shared/utils/logger.js.map +1 -0
  90. package/lib/typescript/blank-stack/components/adjusted-screen.d.ts.map +1 -1
  91. package/lib/typescript/shared/components/create-transition-aware-component.d.ts.map +1 -1
  92. package/lib/typescript/shared/components/screen-container.d.ts +6 -0
  93. package/lib/typescript/shared/components/screen-container.d.ts.map +1 -0
  94. package/lib/typescript/shared/configs/specs.d.ts +1 -0
  95. package/lib/typescript/shared/configs/specs.d.ts.map +1 -1
  96. package/lib/typescript/shared/constants.d.ts +9 -0
  97. package/lib/typescript/shared/constants.d.ts.map +1 -1
  98. package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
  99. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts +1 -1
  100. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
  101. package/lib/typescript/shared/hooks/gestures/use-screen-gesture-handlers.d.ts +19 -0
  102. package/lib/typescript/shared/hooks/gestures/use-screen-gesture-handlers.d.ts.map +1 -0
  103. package/lib/typescript/shared/hooks/gestures/use-scroll-registry.d.ts +5 -1
  104. package/lib/typescript/shared/hooks/gestures/use-scroll-registry.d.ts.map +1 -1
  105. package/lib/typescript/shared/hooks/lifecycle/use-open-transition.d.ts.map +1 -1
  106. package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts +7 -0
  107. package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts.map +1 -1
  108. package/lib/typescript/shared/hooks/use-backdrop-pointer-events.d.ts +15 -0
  109. package/lib/typescript/shared/hooks/use-backdrop-pointer-events.d.ts.map +1 -0
  110. package/lib/typescript/shared/providers/gestures.provider.d.ts +1 -0
  111. package/lib/typescript/shared/providers/gestures.provider.d.ts.map +1 -1
  112. package/lib/typescript/shared/types/animation.types.d.ts +37 -2
  113. package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
  114. package/lib/typescript/shared/types/screen.types.d.ts +26 -0
  115. package/lib/typescript/shared/types/screen.types.d.ts.map +1 -1
  116. package/lib/typescript/shared/utils/animation/animate-to-progress.d.ts +19 -0
  117. package/lib/typescript/shared/utils/animation/animate-to-progress.d.ts.map +1 -0
  118. package/lib/typescript/shared/utils/gesture/check-gesture-activation.d.ts +23 -0
  119. package/lib/typescript/shared/utils/gesture/check-gesture-activation.d.ts.map +1 -1
  120. package/lib/typescript/shared/utils/gesture/determine-snap-target.d.ts +26 -0
  121. package/lib/typescript/shared/utils/gesture/determine-snap-target.d.ts.map +1 -0
  122. package/lib/typescript/shared/utils/gesture/validate-snap-points.d.ts +13 -0
  123. package/lib/typescript/shared/utils/gesture/validate-snap-points.d.ts.map +1 -0
  124. package/lib/typescript/shared/utils/gesture/velocity.d.ts +1 -0
  125. package/lib/typescript/shared/utils/gesture/velocity.d.ts.map +1 -1
  126. package/lib/typescript/shared/utils/logger.d.ts +6 -0
  127. package/lib/typescript/shared/utils/logger.d.ts.map +1 -0
  128. package/package.json +3 -2
  129. package/src/blank-stack/components/adjusted-screen.tsx +1 -1
  130. package/src/shared/__tests__/derivations.test.ts +1 -0
  131. package/src/shared/__tests__/determine-snap-target.test.ts +268 -0
  132. package/src/shared/__tests__/gesture-activation.test.ts +220 -0
  133. package/src/shared/__tests__/validate-snap-points.test.ts +125 -0
  134. package/src/shared/components/create-transition-aware-component.tsx +11 -1
  135. package/src/shared/components/screen-container.tsx +65 -0
  136. package/src/shared/configs/presets.ts +3 -3
  137. package/src/shared/configs/specs.ts +6 -0
  138. package/src/shared/constants.ts +36 -9
  139. package/src/shared/hooks/animation/use-screen-animation.tsx +32 -21
  140. package/src/shared/hooks/gestures/use-build-gestures.tsx +23 -275
  141. package/src/shared/hooks/gestures/use-screen-gesture-handlers.ts +434 -0
  142. package/src/shared/hooks/gestures/use-scroll-registry.tsx +52 -1
  143. package/src/shared/hooks/lifecycle/use-close-transition.ts +3 -3
  144. package/src/shared/hooks/lifecycle/use-open-transition.ts +27 -3
  145. package/src/shared/hooks/navigation/use-screen-state.tsx +59 -2
  146. package/src/shared/hooks/use-backdrop-pointer-events.ts +32 -0
  147. package/src/shared/providers/gestures.provider.tsx +3 -2
  148. package/src/shared/providers/screen/screen-composer.tsx +2 -2
  149. package/src/shared/types/animation.types.ts +39 -2
  150. package/src/shared/types/screen.types.ts +29 -0
  151. package/src/shared/utils/animation/{start-screen-transition.ts → animate-to-progress.ts} +23 -8
  152. package/src/shared/utils/gesture/check-gesture-activation.ts +129 -0
  153. package/src/shared/utils/gesture/determine-snap-target.ts +75 -0
  154. package/src/shared/utils/gesture/validate-snap-points.ts +37 -0
  155. package/src/shared/utils/gesture/velocity.ts +10 -0
  156. package/src/shared/utils/logger.ts +15 -0
  157. package/lib/commonjs/shared/components/root-transition-aware.js.map +0 -1
  158. package/lib/commonjs/shared/hooks/use-stack-pointer-events.js +0 -23
  159. package/lib/commonjs/shared/hooks/use-stack-pointer-events.js.map +0 -1
  160. package/lib/commonjs/shared/utils/animation/start-screen-transition.js.map +0 -1
  161. package/lib/module/shared/components/root-transition-aware.js +0 -48
  162. package/lib/module/shared/components/root-transition-aware.js.map +0 -1
  163. package/lib/module/shared/hooks/use-stack-pointer-events.js +0 -20
  164. package/lib/module/shared/hooks/use-stack-pointer-events.js.map +0 -1
  165. package/lib/module/shared/utils/animation/start-screen-transition.js.map +0 -1
  166. package/lib/typescript/shared/components/root-transition-aware.d.ts +0 -6
  167. package/lib/typescript/shared/components/root-transition-aware.d.ts.map +0 -1
  168. package/lib/typescript/shared/hooks/use-stack-pointer-events.d.ts +0 -10
  169. package/lib/typescript/shared/hooks/use-stack-pointer-events.d.ts.map +0 -1
  170. package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts +0 -13
  171. package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts.map +0 -1
  172. package/src/shared/components/root-transition-aware.tsx +0 -49
  173. package/src/shared/hooks/use-stack-pointer-events.ts +0 -15
@@ -0,0 +1,13 @@
1
+ interface ValidateSnapPointsResult {
2
+ hasSnapPoints: boolean;
3
+ snapPoints: number[];
4
+ minSnapPoint: number;
5
+ maxSnapPoint: number;
6
+ }
7
+ interface ValidateSnapPointsOptions {
8
+ snapPoints?: number[];
9
+ canDismiss?: boolean;
10
+ }
11
+ export declare const validateSnapPoints: ({ snapPoints, canDismiss, }: ValidateSnapPointsOptions) => ValidateSnapPointsResult;
12
+ export {};
13
+ //# sourceMappingURL=validate-snap-points.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-snap-points.d.ts","sourceRoot":"","sources":["../../../../../src/shared/utils/gesture/validate-snap-points.ts"],"names":[],"mappings":"AAAA,UAAU,wBAAwB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,yBAAyB;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,kBAAkB,GAAI,6BAGhC,yBAAyB,KAAG,wBAqB9B,CAAC"}
@@ -17,6 +17,7 @@ interface CalculateProgressProps {
17
17
  }
18
18
  export declare const velocity: {
19
19
  normalize: (velocityPixelsPerSecond: number, screenSize: number) => number;
20
+ normalizeTranslation: (translation: number, dimension: number) => number;
20
21
  calculateRestoreVelocity: (currentValueNormalized: number, baseVelocityNormalized: number) => number;
21
22
  calculateProgressVelocity: ({ animations, shouldDismiss, event, dimensions, directions, }: CalculateProgressProps) => number;
22
23
  shouldPassDismissalThreshold: (translationPixels: number, velocityPixelsPerSecond: number, screenSize: number, velocityWeight: number) => boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"velocity.d.ts","sourceRoot":"","sources":["../../../../../src/shared/utils/gesture/velocity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,uBAAuB,EACvB,6BAA6B,EAC7B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEtE,UAAU,sBAAsB;IAC/B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,uBAAuB,CAAC,6BAA6B,CAAC,CAAC;IAC9D,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,UAAU,EAAE;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,QAAQ,EAAE,OAAO,CAAC;QAClB,gBAAgB,EAAE,OAAO,CAAC;KAC1B,CAAC;CACF;AAuHD,eAAO,MAAM,QAAQ;yCA9GuB,MAAM,cAAc,MAAM;uDAc7C,MAAM,0BACN,MAAM;+FAkB5B,sBAAsB;sDAyDL,MAAM,2BACA,MAAM,cACnB,MAAM,kBACF,MAAM;CAsBtB,CAAC"}
1
+ {"version":3,"file":"velocity.d.ts","sourceRoot":"","sources":["../../../../../src/shared/utils/gesture/velocity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,uBAAuB,EACvB,6BAA6B,EAC7B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEtE,UAAU,sBAAsB;IAC/B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,uBAAuB,CAAC,6BAA6B,CAAC,CAAC;IAC9D,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,UAAU,EAAE;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,QAAQ,EAAE,OAAO,CAAC;QAClB,gBAAgB,EAAE,OAAO,CAAC;KAC1B,CAAC;CACF;AAgID,eAAO,MAAM,QAAQ;yCAvHuB,MAAM,cAAc,MAAM;wCAa3B,MAAM,aAAa,MAAM;uDAU3C,MAAM,0BACN,MAAM;+FAkB5B,sBAAsB;sDAyDL,MAAM,2BACA,MAAM,cACnB,MAAM,kBACF,MAAM;CAuBtB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const logger: {
2
+ error(message: string): void;
3
+ warn(message: string): void;
4
+ };
5
+ export declare const error: (message: string) => Error;
6
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../../src/shared/utils/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;mBACH,MAAM;kBAIP,MAAM;CAIpB,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,SAAS,MAAM,UACK,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-screen-transitions",
3
- "version": "3.2.1",
3
+ "version": "3.3.0-beta.1",
4
4
  "description": "Easy screen transitions for React Native and Expo",
5
5
  "author": "Ed",
6
6
  "license": "MIT",
@@ -44,7 +44,8 @@
44
44
  "lint": "biome check ./src",
45
45
  "typecheck": "tsc --noEmit",
46
46
  "prepublishOnly": "bun run build",
47
- "release": "release-it"
47
+ "release": "release-it",
48
+ "release:beta": "release-it --preRelease=beta"
48
49
  },
49
50
  "keywords": [
50
51
  "react-native",
@@ -9,6 +9,7 @@ import Animated, {
9
9
  useSharedValue,
10
10
  } from "react-native-reanimated";
11
11
  import { Screen as RNSScreen } from "react-native-screens";
12
+ import { EPSILON } from "../../shared/constants";
12
13
  import { useStack } from "../../shared/hooks/navigation/use-stack";
13
14
  import { LayoutAnchorProvider } from "../../shared/providers/layout-anchor.provider";
14
15
  import { useManagedStackContext } from "../../shared/providers/stack/managed.provider";
@@ -28,7 +29,6 @@ enum ScreenActivity {
28
29
  ON_TOP = 2,
29
30
  }
30
31
 
31
- const EPSILON = 1e-5;
32
32
  const POINT_NONE = "none" as const;
33
33
  const POINT_BOX_NONE = "box-none" as const;
34
34
 
@@ -8,6 +8,7 @@ const createMockState = (
8
8
  progress: 1,
9
9
  closing: 0,
10
10
  animating: 0,
11
+ entering: 1,
11
12
  gesture: {
12
13
  isDragging: 0,
13
14
  x: 0,
@@ -0,0 +1,268 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { determineSnapTarget } from "../utils/gesture/determine-snap-target";
3
+
4
+ describe("determineSnapTarget", () => {
5
+ const dimension = 800; // screen height
6
+ const snapPoints = [0.3, 0.6, 1]; // 3 snap points
7
+
8
+ describe("basic snapping without velocity", () => {
9
+ it("snaps to nearest snap point when exactly at a snap point", () => {
10
+ const result = determineSnapTarget({
11
+ currentProgress: 0.6,
12
+ snapPoints,
13
+ velocity: 0,
14
+ dimension,
15
+ });
16
+ expect(result.targetProgress).toBe(0.6);
17
+ expect(result.shouldDismiss).toBe(false);
18
+ });
19
+
20
+ it("snaps to lower snap point when below midpoint", () => {
21
+ // Midpoint between 0.3 and 0.6 is 0.45
22
+ const result = determineSnapTarget({
23
+ currentProgress: 0.4,
24
+ snapPoints,
25
+ velocity: 0,
26
+ dimension,
27
+ });
28
+ expect(result.targetProgress).toBe(0.3);
29
+ });
30
+
31
+ it("snaps to higher snap point when above midpoint", () => {
32
+ // Midpoint between 0.3 and 0.6 is 0.45
33
+ const result = determineSnapTarget({
34
+ currentProgress: 0.5,
35
+ snapPoints,
36
+ velocity: 0,
37
+ dimension,
38
+ });
39
+ expect(result.targetProgress).toBe(0.6);
40
+ });
41
+
42
+ it("snaps to highest point when above all snap points", () => {
43
+ const result = determineSnapTarget({
44
+ currentProgress: 1.2,
45
+ snapPoints,
46
+ velocity: 0,
47
+ dimension,
48
+ });
49
+ expect(result.targetProgress).toBe(1);
50
+ });
51
+ });
52
+
53
+ describe("velocity influence", () => {
54
+ it("velocity toward dismiss pushes toward lower snap point", () => {
55
+ // At 0.5 (above midpoint 0.45), normally would snap to 0.6
56
+ // But positive velocity (toward dismiss) should push toward 0.3
57
+ const result = determineSnapTarget({
58
+ currentProgress: 0.5,
59
+ snapPoints,
60
+ velocity: 800, // Strong velocity toward dismiss
61
+ dimension,
62
+ velocityFactor: 0.15,
63
+ });
64
+ expect(result.targetProgress).toBe(0.3);
65
+ });
66
+
67
+ it("velocity toward expand pushes toward higher snap point", () => {
68
+ // At 0.4 (below midpoint 0.45), normally would snap to 0.3
69
+ // But negative velocity (toward expand) should push toward 0.6
70
+ const result = determineSnapTarget({
71
+ currentProgress: 0.4,
72
+ snapPoints,
73
+ velocity: -800, // Strong velocity toward expand
74
+ dimension,
75
+ velocityFactor: 0.15,
76
+ });
77
+ expect(result.targetProgress).toBe(0.6);
78
+ });
79
+
80
+ it("weak velocity does not override position", () => {
81
+ // At 0.55 (above midpoint), weak velocity shouldn't change target
82
+ const result = determineSnapTarget({
83
+ currentProgress: 0.55,
84
+ snapPoints,
85
+ velocity: 100, // Weak velocity
86
+ dimension,
87
+ velocityFactor: 0.15,
88
+ });
89
+ expect(result.targetProgress).toBe(0.6);
90
+ });
91
+
92
+ it("respects custom velocityFactor", () => {
93
+ // Higher velocityFactor = velocity has more influence
94
+ const result = determineSnapTarget({
95
+ currentProgress: 0.5,
96
+ snapPoints,
97
+ velocity: 400,
98
+ dimension,
99
+ velocityFactor: 0.5, // High factor
100
+ });
101
+ expect(result.targetProgress).toBe(0.3);
102
+ });
103
+ });
104
+
105
+ describe("dismiss behavior", () => {
106
+ it("allows dismiss when canDismiss is true and near 0", () => {
107
+ const result = determineSnapTarget({
108
+ currentProgress: 0.1,
109
+ snapPoints,
110
+ velocity: 500,
111
+ dimension,
112
+ canDismiss: true,
113
+ });
114
+ expect(result.targetProgress).toBe(0);
115
+ expect(result.shouldDismiss).toBe(true);
116
+ });
117
+
118
+ it("prevents dismiss when canDismiss is false", () => {
119
+ const result = determineSnapTarget({
120
+ currentProgress: 0.1,
121
+ snapPoints,
122
+ velocity: 1000, // Strong dismiss velocity
123
+ dimension,
124
+ canDismiss: false,
125
+ });
126
+ expect(result.targetProgress).toBe(0.3); // First snap point
127
+ expect(result.shouldDismiss).toBe(false);
128
+ });
129
+
130
+ it("defaults canDismiss to true", () => {
131
+ const result = determineSnapTarget({
132
+ currentProgress: 0.1,
133
+ snapPoints,
134
+ velocity: 500,
135
+ dimension,
136
+ });
137
+ expect(result.shouldDismiss).toBe(true);
138
+ });
139
+
140
+ it("shouldDismiss is false when snapping to any snap point", () => {
141
+ const result = determineSnapTarget({
142
+ currentProgress: 0.5,
143
+ snapPoints,
144
+ velocity: 0,
145
+ dimension,
146
+ });
147
+ expect(result.shouldDismiss).toBe(false);
148
+ });
149
+ });
150
+
151
+ describe("edge cases", () => {
152
+ it("handles single snap point", () => {
153
+ const result = determineSnapTarget({
154
+ currentProgress: 0.3,
155
+ snapPoints: [0.5],
156
+ velocity: 0,
157
+ dimension,
158
+ });
159
+ expect(result.targetProgress).toBe(0.5);
160
+ });
161
+
162
+ it("handles unsorted snap points", () => {
163
+ const result = determineSnapTarget({
164
+ currentProgress: 0.5,
165
+ snapPoints: [1, 0.3, 0.6], // Unsorted
166
+ velocity: 0,
167
+ dimension,
168
+ });
169
+ // After sorting: [0.3, 0.6, 1], midpoint 0.3-0.6 is 0.45
170
+ expect(result.targetProgress).toBe(0.6);
171
+ });
172
+
173
+ it("handles progress at exactly 0", () => {
174
+ const result = determineSnapTarget({
175
+ currentProgress: 0,
176
+ snapPoints,
177
+ velocity: 0,
178
+ dimension,
179
+ canDismiss: true,
180
+ });
181
+ expect(result.targetProgress).toBe(0);
182
+ expect(result.shouldDismiss).toBe(true);
183
+ });
184
+
185
+ it("handles negative progress", () => {
186
+ const result = determineSnapTarget({
187
+ currentProgress: -0.1,
188
+ snapPoints,
189
+ velocity: 0,
190
+ dimension,
191
+ canDismiss: true,
192
+ });
193
+ expect(result.targetProgress).toBe(0);
194
+ });
195
+
196
+ it("handles very high velocity", () => {
197
+ const result = determineSnapTarget({
198
+ currentProgress: 0.9,
199
+ snapPoints,
200
+ velocity: 5000, // Very high velocity
201
+ dimension,
202
+ canDismiss: true,
203
+ });
204
+ expect(result.targetProgress).toBe(0);
205
+ expect(result.shouldDismiss).toBe(true);
206
+ });
207
+
208
+ it("handles snap points not starting at typical values", () => {
209
+ const result = determineSnapTarget({
210
+ currentProgress: 0.7,
211
+ snapPoints: [0.5, 0.75, 1],
212
+ velocity: 0,
213
+ dimension,
214
+ });
215
+ // Midpoint between 0.5 and 0.75 is 0.625
216
+ // 0.7 is above 0.625, so should snap to 0.75
217
+ expect(result.targetProgress).toBe(0.75);
218
+ });
219
+
220
+ it("handles two snap points", () => {
221
+ const result = determineSnapTarget({
222
+ currentProgress: 0.4,
223
+ snapPoints: [0.3, 1],
224
+ velocity: 0,
225
+ dimension,
226
+ });
227
+ // Midpoint is 0.65, 0.4 is below, so snap to 0.3
228
+ expect(result.targetProgress).toBe(0.3);
229
+ });
230
+ });
231
+
232
+ describe("zone boundaries", () => {
233
+ it("snaps to higher at exact midpoint (uses < not <=)", () => {
234
+ // Midpoint between 0 and 0.3 is 0.15
235
+ // The algorithm uses `<` so at exact midpoint, it snaps to higher
236
+ const result = determineSnapTarget({
237
+ currentProgress: 0.15,
238
+ snapPoints,
239
+ velocity: 0,
240
+ dimension,
241
+ canDismiss: true,
242
+ });
243
+ expect(result.targetProgress).toBe(0.3);
244
+ });
245
+
246
+ it("snaps to lower just below midpoint", () => {
247
+ const result = determineSnapTarget({
248
+ currentProgress: 0.149,
249
+ snapPoints,
250
+ velocity: 0,
251
+ dimension,
252
+ canDismiss: true,
253
+ });
254
+ expect(result.targetProgress).toBe(0);
255
+ });
256
+
257
+ it("snaps to higher just above midpoint", () => {
258
+ const result = determineSnapTarget({
259
+ currentProgress: 0.151,
260
+ snapPoints,
261
+ velocity: 0,
262
+ dimension,
263
+ canDismiss: true,
264
+ });
265
+ expect(result.targetProgress).toBe(0.3);
266
+ });
267
+ });
268
+ });
@@ -1,11 +1,27 @@
1
1
  import { describe, expect, it } from "bun:test";
2
+ import type { ScrollConfig } from "../providers/gestures.provider";
2
3
  import {
3
4
  normalizeSides,
4
5
  computeEdgeConstraints,
5
6
  calculateSwipeDirs,
6
7
  shouldActivateOrFail,
8
+ checkScrollAwareActivation,
7
9
  } from "../utils/gesture/check-gesture-activation";
8
10
 
11
+ /** Helper to create ScrollConfig with sensible defaults */
12
+ const createScrollConfig = (
13
+ overrides: Partial<ScrollConfig> = {},
14
+ ): ScrollConfig => ({
15
+ x: 0,
16
+ y: 0,
17
+ contentWidth: 0,
18
+ contentHeight: 0,
19
+ layoutWidth: 0,
20
+ layoutHeight: 0,
21
+ isTouched: true,
22
+ ...overrides,
23
+ });
24
+
9
25
  describe("normalizeSides", () => {
10
26
  it("returns all sides as 'screen' when no area provided", () => {
11
27
  const result = normalizeSides();
@@ -249,3 +265,207 @@ describe("shouldActivateOrFail", () => {
249
265
  expect(result.shouldActivate).toBe(true);
250
266
  });
251
267
  });
268
+
269
+ describe("checkScrollAwareActivation", () => {
270
+ const baseDirections = {
271
+ vertical: true,
272
+ verticalInverted: false,
273
+ horizontal: false,
274
+ horizontalInverted: false,
275
+ };
276
+
277
+ const noSwipe = {
278
+ isSwipingDown: false,
279
+ isSwipingUp: false,
280
+ isSwipingRight: false,
281
+ isSwipingLeft: false,
282
+ };
283
+
284
+ describe("vertical sheet (dismiss down)", () => {
285
+ it("activates when swiping down at scroll top", () => {
286
+ const result = checkScrollAwareActivation({
287
+ swipeInfo: { ...noSwipe, isSwipingDown: true },
288
+ directions: baseDirections,
289
+ scrollConfig: createScrollConfig({ contentHeight: 500, layoutHeight: 0 }),
290
+ });
291
+ expect(result.shouldActivate).toBe(true);
292
+ expect(result.direction).toBe("vertical");
293
+ });
294
+
295
+ it("does not activate when swiping down but scroll is not at top", () => {
296
+ const result = checkScrollAwareActivation({
297
+ swipeInfo: { ...noSwipe, isSwipingDown: true },
298
+ directions: baseDirections,
299
+ scrollConfig: createScrollConfig({ y: 100, contentHeight: 500, layoutHeight: 0 }),
300
+ });
301
+ expect(result.shouldActivate).toBe(false);
302
+ });
303
+
304
+ it("does not activate when swiping up without snap points", () => {
305
+ const result = checkScrollAwareActivation({
306
+ swipeInfo: { ...noSwipe, isSwipingUp: true },
307
+ directions: baseDirections,
308
+ scrollConfig: createScrollConfig({ contentHeight: 500, layoutHeight: 0 }),
309
+ });
310
+ expect(result.shouldActivate).toBe(false);
311
+ });
312
+ });
313
+
314
+ describe("vertical inverted sheet (dismiss up)", () => {
315
+ const invertedDirections = {
316
+ vertical: false,
317
+ verticalInverted: true,
318
+ horizontal: false,
319
+ horizontalInverted: false,
320
+ };
321
+
322
+ it("activates when swiping up at scroll bottom", () => {
323
+ const result = checkScrollAwareActivation({
324
+ swipeInfo: { ...noSwipe, isSwipingUp: true },
325
+ directions: invertedDirections,
326
+ scrollConfig: createScrollConfig({ y: 500, contentHeight: 500, layoutHeight: 0 }),
327
+ });
328
+ expect(result.shouldActivate).toBe(true);
329
+ expect(result.direction).toBe("vertical-inverted");
330
+ });
331
+
332
+ it("does not activate when swiping up but scroll is not at bottom", () => {
333
+ const result = checkScrollAwareActivation({
334
+ swipeInfo: { ...noSwipe, isSwipingUp: true },
335
+ directions: invertedDirections,
336
+ scrollConfig: createScrollConfig({ y: 200, contentHeight: 500, layoutHeight: 0 }),
337
+ });
338
+ expect(result.shouldActivate).toBe(false);
339
+ });
340
+ });
341
+
342
+ describe("horizontal sheet (dismiss right)", () => {
343
+ const horizontalDirections = {
344
+ vertical: false,
345
+ verticalInverted: false,
346
+ horizontal: true,
347
+ horizontalInverted: false,
348
+ };
349
+
350
+ it("activates when swiping right at scroll left edge", () => {
351
+ const result = checkScrollAwareActivation({
352
+ swipeInfo: { ...noSwipe, isSwipingRight: true },
353
+ directions: horizontalDirections,
354
+ scrollConfig: createScrollConfig({ contentWidth: 500, layoutWidth: 0 }),
355
+ });
356
+ expect(result.shouldActivate).toBe(true);
357
+ expect(result.direction).toBe("horizontal");
358
+ });
359
+
360
+ it("does not activate when scroll is not at left edge", () => {
361
+ const result = checkScrollAwareActivation({
362
+ swipeInfo: { ...noSwipe, isSwipingRight: true },
363
+ directions: horizontalDirections,
364
+ scrollConfig: createScrollConfig({ x: 100, contentWidth: 500, layoutWidth: 0 }),
365
+ });
366
+ expect(result.shouldActivate).toBe(false);
367
+ });
368
+ });
369
+
370
+ describe("horizontal inverted sheet (dismiss left)", () => {
371
+ const invertedHorizontalDirections = {
372
+ vertical: false,
373
+ verticalInverted: false,
374
+ horizontal: false,
375
+ horizontalInverted: true,
376
+ };
377
+
378
+ it("activates when swiping left at scroll right edge", () => {
379
+ const result = checkScrollAwareActivation({
380
+ swipeInfo: { ...noSwipe, isSwipingLeft: true },
381
+ directions: invertedHorizontalDirections,
382
+ scrollConfig: createScrollConfig({ x: 500, contentWidth: 500, layoutWidth: 0 }),
383
+ });
384
+ expect(result.shouldActivate).toBe(true);
385
+ expect(result.direction).toBe("horizontal-inverted");
386
+ });
387
+ });
388
+
389
+ describe("snap points behavior", () => {
390
+ it("activates for swipe up (expand) at scroll top when hasSnapPoints and canExpandMore", () => {
391
+ const result = checkScrollAwareActivation({
392
+ swipeInfo: { ...noSwipe, isSwipingUp: true },
393
+ directions: baseDirections,
394
+ scrollConfig: createScrollConfig({ contentHeight: 500, layoutHeight: 0 }),
395
+ hasSnapPoints: true,
396
+ canExpandMore: true,
397
+ });
398
+ expect(result.shouldActivate).toBe(true);
399
+ expect(result.direction).toBe("vertical-inverted");
400
+ });
401
+
402
+ it("does not activate for swipe up when canExpandMore is false", () => {
403
+ const result = checkScrollAwareActivation({
404
+ swipeInfo: { ...noSwipe, isSwipingUp: true },
405
+ directions: baseDirections,
406
+ scrollConfig: createScrollConfig({ contentHeight: 500, layoutHeight: 0 }),
407
+ hasSnapPoints: true,
408
+ canExpandMore: false,
409
+ });
410
+ expect(result.shouldActivate).toBe(false);
411
+ });
412
+
413
+ it("does not activate for swipe up when scroll is not at top", () => {
414
+ const result = checkScrollAwareActivation({
415
+ swipeInfo: { ...noSwipe, isSwipingUp: true },
416
+ directions: baseDirections,
417
+ scrollConfig: createScrollConfig({ y: 100, contentHeight: 500, layoutHeight: 0 }),
418
+ hasSnapPoints: true,
419
+ canExpandMore: true,
420
+ });
421
+ expect(result.shouldActivate).toBe(false);
422
+ });
423
+
424
+ it("activates for swipe left (expand) on horizontal sheet with snap points", () => {
425
+ const horizontalDirections = {
426
+ vertical: false,
427
+ verticalInverted: false,
428
+ horizontal: true,
429
+ horizontalInverted: false,
430
+ };
431
+
432
+ const result = checkScrollAwareActivation({
433
+ swipeInfo: { ...noSwipe, isSwipingLeft: true },
434
+ directions: horizontalDirections,
435
+ scrollConfig: createScrollConfig({ contentWidth: 500, layoutWidth: 0 }),
436
+ hasSnapPoints: true,
437
+ canExpandMore: true,
438
+ });
439
+ expect(result.shouldActivate).toBe(true);
440
+ expect(result.direction).toBe("horizontal-inverted");
441
+ });
442
+ });
443
+
444
+ describe("no activation cases", () => {
445
+ it("returns false when no swipe is detected", () => {
446
+ const result = checkScrollAwareActivation({
447
+ swipeInfo: noSwipe,
448
+ directions: baseDirections,
449
+ scrollConfig: createScrollConfig({ contentHeight: 500, layoutHeight: 0 }),
450
+ });
451
+ expect(result.shouldActivate).toBe(false);
452
+ expect(result.direction).toBe(null);
453
+ });
454
+
455
+ it("returns false when direction is disabled", () => {
456
+ const disabledDirections = {
457
+ vertical: false,
458
+ verticalInverted: false,
459
+ horizontal: false,
460
+ horizontalInverted: false,
461
+ };
462
+
463
+ const result = checkScrollAwareActivation({
464
+ swipeInfo: { ...noSwipe, isSwipingDown: true },
465
+ directions: disabledDirections,
466
+ scrollConfig: createScrollConfig({ contentHeight: 500, layoutHeight: 0 }),
467
+ });
468
+ expect(result.shouldActivate).toBe(false);
469
+ });
470
+ });
471
+ });