react-native-screen-transitions 3.0.0-rc.2 → 3.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -371
- package/lib/commonjs/blank-stack/components/{Overlay.js → overlay.js} +7 -5
- package/lib/commonjs/blank-stack/components/overlay.js.map +1 -0
- package/lib/commonjs/blank-stack/components/{Screens.js → screens.js} +8 -10
- package/lib/commonjs/blank-stack/components/screens.js.map +1 -0
- package/lib/commonjs/blank-stack/components/stack-view.js +95 -0
- package/lib/commonjs/blank-stack/components/stack-view.js.map +1 -0
- package/lib/commonjs/blank-stack/index.js +1 -8
- package/lib/commonjs/blank-stack/index.js.map +1 -1
- package/lib/commonjs/blank-stack/navigators/{createBlankStackNavigator.js → create-blank-stack-navigator.js} +3 -3
- package/lib/commonjs/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -0
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js +1 -11
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js.map +1 -1
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js +1 -12
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js.map +1 -1
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.js.map +1 -1
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/index.js +49 -55
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/index.js.map +1 -1
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/{_types.js → types.js} +1 -1
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/types.js.map +1 -0
- package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +38 -22
- package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
- package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
- package/lib/commonjs/shared/providers/flags.provider.js +25 -0
- package/lib/commonjs/shared/providers/flags.provider.js.map +1 -0
- package/lib/commonjs/shared/providers/register-bounds.provider.js +71 -45
- package/lib/commonjs/shared/providers/register-bounds.provider.js.map +1 -1
- package/lib/commonjs/shared/stores/bounds.store.js +91 -47
- package/lib/commonjs/shared/stores/bounds.store.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/helpers/is-bounds-equal.js +1 -1
- package/lib/commonjs/shared/utils/bounds/helpers/is-bounds-equal.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/index.js +4 -5
- package/lib/commonjs/shared/utils/bounds/index.js.map +1 -1
- package/lib/commonjs/shared/utils/create-provider.js +20 -1
- package/lib/commonjs/shared/utils/create-provider.js.map +1 -1
- package/lib/commonjs/shared/utils/reset-stores-for-screen.js +2 -0
- package/lib/commonjs/shared/utils/reset-stores-for-screen.js.map +1 -1
- package/lib/module/blank-stack/components/{Overlay.js → overlay.js} +7 -5
- package/lib/module/blank-stack/components/overlay.js.map +1 -0
- package/lib/module/blank-stack/components/{Screens.js → screens.js} +8 -10
- package/lib/module/blank-stack/components/screens.js.map +1 -0
- package/lib/module/blank-stack/components/stack-view.js +90 -0
- package/lib/module/blank-stack/components/stack-view.js.map +1 -0
- package/lib/module/blank-stack/index.js +1 -2
- package/lib/module/blank-stack/index.js.map +1 -1
- package/lib/module/blank-stack/navigators/{createBlankStackNavigator.js → create-blank-stack-navigator.js} +2 -2
- package/lib/module/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -0
- package/lib/module/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js +1 -11
- package/lib/module/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js.map +1 -1
- package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js +1 -12
- package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js.map +1 -1
- package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.js.map +1 -1
- package/lib/module/blank-stack/utils/with-stack-navigation/index.js +48 -54
- package/lib/module/blank-stack/utils/with-stack-navigation/index.js.map +1 -1
- package/lib/module/blank-stack/utils/with-stack-navigation/types.js +4 -0
- package/lib/module/blank-stack/utils/with-stack-navigation/types.js.map +1 -0
- package/lib/module/shared/hooks/animation/use-screen-animation.js +38 -22
- package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
- package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
- package/lib/module/shared/providers/flags.provider.js +19 -0
- package/lib/module/shared/providers/flags.provider.js.map +1 -0
- package/lib/module/shared/providers/register-bounds.provider.js +71 -45
- package/lib/module/shared/providers/register-bounds.provider.js.map +1 -1
- package/lib/module/shared/stores/bounds.store.js +91 -47
- package/lib/module/shared/stores/bounds.store.js.map +1 -1
- package/lib/module/shared/utils/bounds/helpers/is-bounds-equal.js +1 -1
- package/lib/module/shared/utils/bounds/helpers/is-bounds-equal.js.map +1 -1
- package/lib/module/shared/utils/bounds/index.js +4 -5
- package/lib/module/shared/utils/bounds/index.js.map +1 -1
- package/lib/module/shared/utils/create-provider.js +20 -1
- package/lib/module/shared/utils/create-provider.js.map +1 -1
- package/lib/module/shared/utils/reset-stores-for-screen.js +2 -0
- package/lib/module/shared/utils/reset-stores-for-screen.js.map +1 -1
- package/lib/typescript/blank-stack/components/{Overlay.d.ts → overlay.d.ts} +1 -1
- package/lib/typescript/blank-stack/components/overlay.d.ts.map +1 -0
- package/lib/typescript/blank-stack/components/{Screens.d.ts → screens.d.ts} +1 -1
- package/lib/typescript/blank-stack/components/{Screens.d.ts.map → screens.d.ts.map} +1 -1
- package/lib/typescript/blank-stack/components/stack-view.d.ts +3 -0
- package/lib/typescript/blank-stack/components/stack-view.d.ts.map +1 -0
- package/lib/typescript/blank-stack/index.d.ts +1 -2
- package/lib/typescript/blank-stack/index.d.ts.map +1 -1
- package/lib/typescript/blank-stack/navigators/{createBlankStackNavigator.d.ts → create-blank-stack-navigator.d.ts} +1 -1
- package/lib/typescript/blank-stack/navigators/create-blank-stack-navigator.d.ts.map +1 -0
- package/lib/typescript/blank-stack/types.d.ts +4 -0
- package/lib/typescript/blank-stack/types.d.ts.map +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.d.ts.map +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.d.ts.map +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.d.ts +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.d.ts.map +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/index.d.ts +3 -5
- package/lib/typescript/blank-stack/utils/with-stack-navigation/index.d.ts.map +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/{_types.d.ts → types.d.ts} +1 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/types.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
- package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
- package/lib/typescript/shared/index.d.ts +20 -20
- package/lib/typescript/shared/providers/flags.provider.d.ts +10 -0
- package/lib/typescript/shared/providers/flags.provider.d.ts.map +1 -0
- package/lib/typescript/shared/providers/register-bounds.provider.d.ts.map +1 -1
- package/lib/typescript/shared/stores/bounds.store.d.ts +23 -11
- package/lib/typescript/shared/stores/bounds.store.d.ts.map +1 -1
- package/lib/typescript/shared/types/bounds.types.d.ts +2 -2
- package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/index.d.ts.map +1 -1
- package/lib/typescript/shared/utils/create-provider.d.ts +2 -2
- package/lib/typescript/shared/utils/create-provider.d.ts.map +1 -1
- package/lib/typescript/shared/utils/reset-stores-for-screen.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/blank-stack/components/{Overlay.tsx → overlay.tsx} +4 -3
- package/src/blank-stack/components/{Screens.tsx → screens.tsx} +7 -9
- package/src/blank-stack/components/stack-view.tsx +104 -0
- package/src/blank-stack/index.ts +1 -2
- package/src/blank-stack/navigators/{createBlankStackNavigator.tsx → create-blank-stack-navigator.tsx} +1 -1
- package/src/blank-stack/types.ts +5 -7
- package/src/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.ts +1 -8
- package/src/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.tsx +1 -12
- package/src/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.tsx +1 -1
- package/src/blank-stack/utils/with-stack-navigation/index.tsx +42 -62
- package/src/shared/__tests__/bounds.store.test.ts +398 -167
- package/src/shared/__tests__/determine-dismissal.test.ts +2 -12
- package/src/shared/__tests__/geometry.test.ts +1 -1
- package/src/shared/__tests__/gesture.velocity.test.ts +2 -10
- package/src/shared/hooks/animation/use-screen-animation.tsx +55 -29
- package/src/shared/hooks/gestures/use-build-gestures.tsx +4 -1
- package/src/shared/providers/flags.provider.tsx +21 -0
- package/src/shared/providers/register-bounds.provider.tsx +85 -54
- package/src/shared/stores/bounds.store.ts +90 -54
- package/src/shared/types/bounds.types.ts +2 -2
- package/src/shared/utils/bounds/helpers/is-bounds-equal.ts +1 -1
- package/src/shared/utils/bounds/index.ts +7 -10
- package/src/shared/utils/create-provider.tsx +35 -1
- package/src/shared/utils/reset-stores-for-screen.ts +2 -0
- package/lib/commonjs/blank-stack/components/Overlay.js.map +0 -1
- package/lib/commonjs/blank-stack/components/Screens.js.map +0 -1
- package/lib/commonjs/blank-stack/components/StackView.js +0 -93
- package/lib/commonjs/blank-stack/components/StackView.js.map +0 -1
- package/lib/commonjs/blank-stack/navigators/createBlankStackNavigator.js.map +0 -1
- package/lib/commonjs/blank-stack/utils/with-stack-navigation/_types.js.map +0 -1
- package/lib/module/blank-stack/components/Overlay.js.map +0 -1
- package/lib/module/blank-stack/components/Screens.js.map +0 -1
- package/lib/module/blank-stack/components/StackView.js +0 -88
- package/lib/module/blank-stack/components/StackView.js.map +0 -1
- package/lib/module/blank-stack/navigators/createBlankStackNavigator.js.map +0 -1
- package/lib/module/blank-stack/utils/with-stack-navigation/_types.js +0 -4
- package/lib/module/blank-stack/utils/with-stack-navigation/_types.js.map +0 -1
- package/lib/typescript/blank-stack/components/Overlay.d.ts.map +0 -1
- package/lib/typescript/blank-stack/components/StackView.d.ts +0 -2
- package/lib/typescript/blank-stack/components/StackView.d.ts.map +0 -1
- package/lib/typescript/blank-stack/navigators/createBlankStackNavigator.d.ts.map +0 -1
- package/lib/typescript/blank-stack/utils/with-stack-navigation/_types.d.ts.map +0 -1
- package/src/blank-stack/components/StackView.tsx +0 -108
- /package/src/blank-stack/utils/with-stack-navigation/{_types.ts → types.ts} +0 -0
|
@@ -1,185 +1,416 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
x
|
|
7
|
-
y
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
height: number;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const getDimensions = (x = 0, y = 0, w = 100, h = 100): Dim => ({
|
|
2
|
+
import { BoundStore, type Snapshot } from "../stores/bounds.store";
|
|
3
|
+
|
|
4
|
+
// Helper to create mock bounds
|
|
5
|
+
const createBounds = (
|
|
6
|
+
x = 0,
|
|
7
|
+
y = 0,
|
|
8
|
+
width = 100,
|
|
9
|
+
height = 100,
|
|
10
|
+
): Snapshot["bounds"] => ({
|
|
15
11
|
x,
|
|
16
12
|
y,
|
|
17
13
|
pageX: x,
|
|
18
14
|
pageY: y,
|
|
19
|
-
width
|
|
20
|
-
height
|
|
15
|
+
width,
|
|
16
|
+
height,
|
|
21
17
|
});
|
|
22
18
|
|
|
23
|
-
|
|
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>;
|
|
19
|
+
// Reset registry before each test
|
|
54
20
|
beforeEach(() => {
|
|
55
|
-
|
|
56
|
-
|
|
21
|
+
globalThis.resetMutableRegistry();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Unit Tests - registerSnapshot
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
describe("BoundStore.registerSnapshot", () => {
|
|
29
|
+
it("registers new tag with bounds and styles", () => {
|
|
30
|
+
const bounds = createBounds(10, 20, 200, 300);
|
|
31
|
+
const styles = { backgroundColor: "red" };
|
|
32
|
+
|
|
33
|
+
BoundStore.registerSnapshot("card", "screen-a", bounds, styles);
|
|
34
|
+
|
|
35
|
+
const snapshot = BoundStore.getSnapshot("card", "screen-a");
|
|
36
|
+
expect(snapshot).not.toBeNull();
|
|
37
|
+
expect(snapshot?.bounds).toEqual(bounds);
|
|
38
|
+
expect(snapshot?.styles).toEqual(styles);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("adds snapshot to existing tag", () => {
|
|
42
|
+
const boundsA = createBounds(0, 0, 100, 100);
|
|
43
|
+
const boundsB = createBounds(50, 50, 150, 150);
|
|
44
|
+
|
|
45
|
+
BoundStore.registerSnapshot("card", "screen-a", boundsA);
|
|
46
|
+
BoundStore.registerSnapshot("card", "screen-b", boundsB);
|
|
47
|
+
|
|
48
|
+
expect(BoundStore.getSnapshot("card", "screen-a")?.bounds).toEqual(
|
|
49
|
+
boundsA,
|
|
50
|
+
);
|
|
51
|
+
expect(BoundStore.getSnapshot("card", "screen-b")?.bounds).toEqual(
|
|
52
|
+
boundsB,
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("stores ancestorKeys correctly", () => {
|
|
57
|
+
const bounds = createBounds();
|
|
58
|
+
const ancestors = ["stack-a", "tab-nav"];
|
|
59
|
+
|
|
60
|
+
BoundStore.registerSnapshot("card", "screen-a", bounds, {}, ancestors);
|
|
61
|
+
|
|
62
|
+
// Verify ancestor matching works
|
|
63
|
+
const viaAncestor = BoundStore.getSnapshot("card", "stack-a");
|
|
64
|
+
expect(viaAncestor).not.toBeNull();
|
|
65
|
+
expect(viaAncestor?.bounds).toEqual(bounds);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("updates existing snapshot on re-measurement", () => {
|
|
69
|
+
const initialBounds = createBounds(0, 0, 100, 100);
|
|
70
|
+
const updatedBounds = createBounds(10, 10, 200, 200);
|
|
71
|
+
|
|
72
|
+
BoundStore.registerSnapshot("card", "screen-a", initialBounds);
|
|
73
|
+
BoundStore.registerSnapshot("card", "screen-a", updatedBounds);
|
|
74
|
+
|
|
75
|
+
const snapshot = BoundStore.getSnapshot("card", "screen-a");
|
|
76
|
+
expect(snapshot?.bounds).toEqual(updatedBounds);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Unit Tests - setLinkSource / setLinkDestination
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
describe("BoundStore.setLinkSource", () => {
|
|
85
|
+
it("creates new tag if it does not exist", () => {
|
|
86
|
+
const bounds = createBounds();
|
|
87
|
+
|
|
88
|
+
BoundStore.setLinkSource("card", "screen-a", bounds);
|
|
89
|
+
|
|
90
|
+
const link = BoundStore.getActiveLink("card");
|
|
91
|
+
expect(link).not.toBeNull();
|
|
92
|
+
expect(link?.source.screenKey).toBe("screen-a");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("pushes link with source and null destination", () => {
|
|
96
|
+
const bounds = createBounds();
|
|
97
|
+
|
|
98
|
+
BoundStore.setLinkSource("card", "screen-a", bounds);
|
|
99
|
+
|
|
100
|
+
const link = BoundStore.getActiveLink("card");
|
|
101
|
+
expect(link?.source.screenKey).toBe("screen-a");
|
|
102
|
+
expect(link?.destination).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("multiple sources create multiple links", () => {
|
|
106
|
+
const boundsA = createBounds(0, 0);
|
|
107
|
+
const boundsB = createBounds(100, 100);
|
|
108
|
+
|
|
109
|
+
BoundStore.setLinkSource("card", "screen-a", boundsA);
|
|
110
|
+
BoundStore.setLinkSource("card", "screen-b", boundsB);
|
|
111
|
+
|
|
112
|
+
// Most recent link should be from screen-b
|
|
113
|
+
const link = BoundStore.getActiveLink("card");
|
|
114
|
+
expect(link?.source.screenKey).toBe("screen-b");
|
|
115
|
+
});
|
|
57
116
|
});
|
|
58
117
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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();
|
|
118
|
+
describe("BoundStore.setLinkDestination", () => {
|
|
119
|
+
it("fills topmost link with null destination", () => {
|
|
120
|
+
const srcBounds = createBounds(0, 0);
|
|
121
|
+
const dstBounds = createBounds(100, 100);
|
|
122
|
+
|
|
123
|
+
BoundStore.setLinkSource("card", "screen-a", srcBounds);
|
|
124
|
+
BoundStore.setLinkDestination("card", "screen-b", dstBounds);
|
|
125
|
+
|
|
126
|
+
const link = BoundStore.getActiveLink("card");
|
|
127
|
+
expect(link?.source.screenKey).toBe("screen-a");
|
|
128
|
+
expect(link?.destination?.screenKey).toBe("screen-b");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("ignores if no pending links", () => {
|
|
132
|
+
const bounds = createBounds();
|
|
133
|
+
|
|
134
|
+
// No source set, destination should be ignored
|
|
135
|
+
BoundStore.setLinkDestination("card", "screen-b", bounds);
|
|
136
|
+
|
|
137
|
+
const link = BoundStore.getActiveLink("card");
|
|
138
|
+
expect(link).toBeNull();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("fills correct link when multiple pending", () => {
|
|
142
|
+
const boundsA = createBounds(0, 0);
|
|
143
|
+
const boundsB = createBounds(50, 50);
|
|
144
|
+
const boundsC = createBounds(100, 100);
|
|
145
|
+
|
|
146
|
+
// Two sources, one destination
|
|
147
|
+
BoundStore.setLinkSource("card", "screen-a", boundsA);
|
|
148
|
+
BoundStore.setLinkSource("card", "screen-b", boundsB);
|
|
149
|
+
BoundStore.setLinkDestination("card", "screen-c", boundsC);
|
|
150
|
+
|
|
151
|
+
// Most recent link (from screen-b) should have destination
|
|
152
|
+
const link = BoundStore.getActiveLink("card");
|
|
153
|
+
expect(link?.source.screenKey).toBe("screen-b");
|
|
154
|
+
expect(link?.destination?.screenKey).toBe("screen-c");
|
|
105
155
|
});
|
|
106
156
|
});
|
|
107
157
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
expect(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// Unit Tests - getSnapshot
|
|
160
|
+
// =============================================================================
|
|
161
|
+
|
|
162
|
+
describe("BoundStore.getSnapshot", () => {
|
|
163
|
+
it("returns null for unknown tag", () => {
|
|
164
|
+
const result = BoundStore.getSnapshot("unknown", "screen-a");
|
|
165
|
+
expect(result).toBeNull();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("returns bounds and styles for direct key match", () => {
|
|
169
|
+
const bounds = createBounds(10, 20, 300, 400);
|
|
170
|
+
const styles = { borderRadius: 8 };
|
|
171
|
+
|
|
172
|
+
BoundStore.registerSnapshot("card", "screen-a", bounds, styles);
|
|
173
|
+
|
|
174
|
+
const result = BoundStore.getSnapshot("card", "screen-a");
|
|
175
|
+
expect(result).toEqual({ bounds, styles });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("returns bounds via ancestor match", () => {
|
|
179
|
+
const bounds = createBounds();
|
|
180
|
+
const ancestors = ["stack-a", "root"];
|
|
181
|
+
|
|
182
|
+
BoundStore.registerSnapshot("card", "screen-a", bounds, {}, ancestors);
|
|
183
|
+
|
|
184
|
+
// Query by ancestor key
|
|
185
|
+
const result = BoundStore.getSnapshot("card", "stack-a");
|
|
186
|
+
expect(result).not.toBeNull();
|
|
187
|
+
expect(result?.bounds).toEqual(bounds);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("prefers direct match over ancestor match", () => {
|
|
191
|
+
const directBounds = createBounds(0, 0, 100, 100);
|
|
192
|
+
const ancestorBounds = createBounds(200, 200, 50, 50);
|
|
193
|
+
|
|
194
|
+
// Register with ancestor that matches another screen's key
|
|
195
|
+
BoundStore.registerSnapshot(
|
|
196
|
+
"card",
|
|
197
|
+
"screen-a",
|
|
198
|
+
ancestorBounds,
|
|
199
|
+
{},
|
|
200
|
+
["stack-a"],
|
|
201
|
+
);
|
|
202
|
+
BoundStore.registerSnapshot("card", "stack-a", directBounds);
|
|
203
|
+
|
|
204
|
+
// Direct match should win
|
|
205
|
+
const result = BoundStore.getSnapshot("card", "stack-a");
|
|
206
|
+
expect(result?.bounds).toEqual(directBounds);
|
|
150
207
|
});
|
|
151
208
|
});
|
|
152
209
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
210
|
+
// =============================================================================
|
|
211
|
+
// Unit Tests - getActiveLink
|
|
212
|
+
// =============================================================================
|
|
213
|
+
|
|
214
|
+
describe("BoundStore.getActiveLink", () => {
|
|
215
|
+
it("returns null for unknown tag", () => {
|
|
216
|
+
const result = BoundStore.getActiveLink("unknown");
|
|
217
|
+
expect(result).toBeNull();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("returns null for empty linkStack", () => {
|
|
221
|
+
// Register snapshot but no links
|
|
222
|
+
BoundStore.registerSnapshot("card", "screen-a", createBounds());
|
|
223
|
+
|
|
224
|
+
const result = BoundStore.getActiveLink("card");
|
|
225
|
+
expect(result).toBeNull();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("returns most recent link when no screenKey provided", () => {
|
|
229
|
+
BoundStore.setLinkSource("card", "screen-a", createBounds());
|
|
230
|
+
BoundStore.setLinkDestination("card", "screen-b", createBounds());
|
|
231
|
+
BoundStore.setLinkSource("card", "screen-b", createBounds());
|
|
232
|
+
BoundStore.setLinkDestination("card", "screen-c", createBounds());
|
|
233
|
+
|
|
234
|
+
const link = BoundStore.getActiveLink("card");
|
|
235
|
+
expect(link?.source.screenKey).toBe("screen-b");
|
|
236
|
+
expect(link?.destination?.screenKey).toBe("screen-c");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("infers isClosing when screenKey matches source", () => {
|
|
240
|
+
BoundStore.setLinkSource("card", "screen-a", createBounds());
|
|
241
|
+
BoundStore.setLinkDestination("card", "screen-b", createBounds());
|
|
242
|
+
|
|
243
|
+
// Query from source screen = closing (going back)
|
|
244
|
+
const linkFromSource = BoundStore.getActiveLink("card", "screen-a");
|
|
245
|
+
expect(linkFromSource?.isClosing).toBe(true);
|
|
246
|
+
expect(linkFromSource?.source.screenKey).toBe("screen-a");
|
|
247
|
+
|
|
248
|
+
// Query from destination screen = opening
|
|
249
|
+
const linkFromDest = BoundStore.getActiveLink("card", "screen-b");
|
|
250
|
+
expect(linkFromDest?.isClosing).toBe(false);
|
|
251
|
+
expect(linkFromDest?.destination?.screenKey).toBe("screen-b");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("ancestor matching works in link lookup", () => {
|
|
255
|
+
const ancestors = ["stack-a"];
|
|
256
|
+
|
|
257
|
+
BoundStore.setLinkSource(
|
|
258
|
+
"card",
|
|
259
|
+
"screen-a",
|
|
260
|
+
createBounds(),
|
|
261
|
+
{},
|
|
262
|
+
ancestors,
|
|
263
|
+
);
|
|
264
|
+
BoundStore.setLinkDestination("card", "screen-b", createBounds());
|
|
265
|
+
|
|
266
|
+
// Query by ancestor key (matches source)
|
|
267
|
+
const link = BoundStore.getActiveLink("card", "stack-a");
|
|
268
|
+
expect(link).not.toBeNull();
|
|
269
|
+
expect(link?.source.screenKey).toBe("screen-a");
|
|
270
|
+
expect(link?.isClosing).toBe(true); // Ancestor of source = closing
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("returns null when screenKey does not match any link", () => {
|
|
274
|
+
BoundStore.setLinkSource("card", "screen-a", createBounds());
|
|
275
|
+
BoundStore.setLinkDestination("card", "screen-b", createBounds());
|
|
276
|
+
|
|
277
|
+
const link = BoundStore.getActiveLink("card", "screen-x");
|
|
278
|
+
expect(link).toBeNull();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// =============================================================================
|
|
283
|
+
// Scenario Tests - Navigation Flows
|
|
284
|
+
// =============================================================================
|
|
285
|
+
|
|
286
|
+
describe("Scenario: Simple push/pop navigation", () => {
|
|
287
|
+
it("captures source on press, destination on layout, reverses on pop", () => {
|
|
288
|
+
const srcBounds = createBounds(50, 100, 200, 200);
|
|
289
|
+
const dstBounds = createBounds(0, 0, 400, 400);
|
|
290
|
+
|
|
291
|
+
// 1. User presses card on Screen A (source captured)
|
|
292
|
+
BoundStore.setLinkSource("card", "screen-a", srcBounds);
|
|
293
|
+
|
|
294
|
+
// 2. Screen B mounts, measures card (destination captured)
|
|
295
|
+
BoundStore.setLinkDestination("card", "screen-b", dstBounds);
|
|
296
|
+
|
|
297
|
+
// Verify link is complete - query from destination (opening)
|
|
298
|
+
const openingLink = BoundStore.getActiveLink("card", "screen-b");
|
|
299
|
+
expect(openingLink?.isClosing).toBe(false);
|
|
300
|
+
expect(openingLink?.source.bounds).toEqual(srcBounds);
|
|
301
|
+
expect(openingLink?.destination?.bounds).toEqual(dstBounds);
|
|
302
|
+
|
|
303
|
+
// 3. Query from source (closing - going back)
|
|
304
|
+
const closingLink = BoundStore.getActiveLink("card", "screen-a");
|
|
305
|
+
expect(closingLink?.isClosing).toBe(true);
|
|
306
|
+
expect(closingLink?.source.screenKey).toBe("screen-a");
|
|
307
|
+
expect(closingLink?.destination?.screenKey).toBe("screen-b");
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe("Scenario: Multiple bounds, only one matches", () => {
|
|
312
|
+
it("establishes link only for matching bound", () => {
|
|
313
|
+
// Screen A has header, card, footer
|
|
314
|
+
BoundStore.registerSnapshot("header", "screen-a", createBounds(0, 0));
|
|
315
|
+
BoundStore.registerSnapshot("card", "screen-a", createBounds(0, 100));
|
|
316
|
+
BoundStore.registerSnapshot("footer", "screen-a", createBounds(0, 500));
|
|
317
|
+
|
|
318
|
+
// Only card triggers navigation
|
|
319
|
+
BoundStore.setLinkSource("card", "screen-a", createBounds(0, 100));
|
|
320
|
+
|
|
321
|
+
// Screen B only has card
|
|
322
|
+
BoundStore.setLinkDestination("card", "screen-b", createBounds(0, 0));
|
|
323
|
+
|
|
324
|
+
// Card link exists
|
|
325
|
+
expect(BoundStore.getActiveLink("card")).not.toBeNull();
|
|
326
|
+
|
|
327
|
+
// Header and footer have no links (only snapshots)
|
|
328
|
+
expect(BoundStore.getActiveLink("header")).toBeNull();
|
|
329
|
+
expect(BoundStore.getActiveLink("footer")).toBeNull();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe("Scenario: Nested navigator with ancestor keys", () => {
|
|
334
|
+
it("supports cross-stack bounds via ancestor matching", () => {
|
|
335
|
+
// Tab Navigator structure:
|
|
336
|
+
// - Stack A (key: "stack-a") -> Screen A1 (key: "a1", ancestors: ["stack-a"])
|
|
337
|
+
// - Stack B (key: "stack-b") -> Screen B1 (key: "b1", ancestors: ["stack-b"])
|
|
338
|
+
|
|
339
|
+
const boundsA = createBounds(10, 10, 80, 80);
|
|
340
|
+
const boundsB = createBounds(20, 20, 100, 100);
|
|
341
|
+
|
|
342
|
+
// Register snapshot in Stack A
|
|
343
|
+
BoundStore.registerSnapshot("profile", "a1", boundsA, {}, ["stack-a"]);
|
|
344
|
+
|
|
345
|
+
// Register snapshot in Stack B
|
|
346
|
+
BoundStore.registerSnapshot("profile", "b1", boundsB, {}, ["stack-b"]);
|
|
347
|
+
|
|
348
|
+
// Query by stack key should return correct bounds
|
|
349
|
+
const fromStackA = BoundStore.getSnapshot("profile", "stack-a");
|
|
350
|
+
expect(fromStackA?.bounds).toEqual(boundsA);
|
|
351
|
+
|
|
352
|
+
const fromStackB = BoundStore.getSnapshot("profile", "stack-b");
|
|
353
|
+
expect(fromStackB?.bounds).toEqual(boundsB);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("getActiveLink respects ancestor chain", () => {
|
|
357
|
+
// Navigation from Stack A to detail screen
|
|
358
|
+
BoundStore.setLinkSource(
|
|
359
|
+
"profile",
|
|
360
|
+
"a1",
|
|
361
|
+
createBounds(10, 10),
|
|
362
|
+
{},
|
|
363
|
+
["stack-a"],
|
|
364
|
+
);
|
|
365
|
+
BoundStore.setLinkDestination("profile", "detail", createBounds(0, 0));
|
|
366
|
+
|
|
367
|
+
// Query by ancestor should find the link
|
|
368
|
+
const link = BoundStore.getActiveLink("profile", "stack-a");
|
|
369
|
+
expect(link?.source.screenKey).toBe("a1");
|
|
370
|
+
expect(link?.isClosing).toBe(true); // Ancestor matches source = closing
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
describe("Scenario: Rapid navigation A → B → C → pop → pop", () => {
|
|
375
|
+
it("link stack grows and getActiveLink finds correct link for each screen", () => {
|
|
376
|
+
// A → B
|
|
377
|
+
BoundStore.setLinkSource("card", "screen-a", createBounds(0, 0));
|
|
378
|
+
BoundStore.setLinkDestination("card", "screen-b", createBounds(100, 100));
|
|
379
|
+
|
|
380
|
+
// B → C
|
|
381
|
+
BoundStore.setLinkSource("card", "screen-b", createBounds(100, 100));
|
|
382
|
+
BoundStore.setLinkDestination("card", "screen-c", createBounds(200, 200));
|
|
383
|
+
|
|
384
|
+
// Most recent link is B → C
|
|
385
|
+
const latest = BoundStore.getActiveLink("card");
|
|
386
|
+
expect(latest?.source.screenKey).toBe("screen-b");
|
|
387
|
+
expect(latest?.destination?.screenKey).toBe("screen-c");
|
|
388
|
+
|
|
389
|
+
// Query from C (destination of B→C) = opening
|
|
390
|
+
const fromC = BoundStore.getActiveLink("card", "screen-c");
|
|
391
|
+
expect(fromC?.isClosing).toBe(false);
|
|
392
|
+
expect(fromC?.destination?.screenKey).toBe("screen-c");
|
|
393
|
+
|
|
394
|
+
// Query from B - B is source of B→C link, so isClosing=true
|
|
395
|
+
const fromB = BoundStore.getActiveLink("card", "screen-b");
|
|
396
|
+
expect(fromB?.isClosing).toBe(true);
|
|
397
|
+
expect(fromB?.source.screenKey).toBe("screen-b");
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
describe("Scenario: Global bounds (fullscreen target)", () => {
|
|
402
|
+
it("getActiveLink with no screenKey returns most recent for fullscreen", () => {
|
|
403
|
+
// Source exists, destination will be fullscreen (no specific screenKey needed)
|
|
404
|
+
BoundStore.setLinkSource("image", "gallery", createBounds(50, 50, 100, 100));
|
|
405
|
+
BoundStore.setLinkDestination(
|
|
406
|
+
"image",
|
|
407
|
+
"fullscreen-viewer",
|
|
408
|
+
createBounds(0, 0, 400, 800),
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
// Fullscreen target can get link without knowing screenKey
|
|
412
|
+
const link = BoundStore.getActiveLink("image");
|
|
413
|
+
expect(link).not.toBeNull();
|
|
414
|
+
expect(link?.destination?.screenKey).toBe("fullscreen-viewer");
|
|
184
415
|
});
|
|
185
416
|
});
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import { describe, expect, it
|
|
2
|
-
|
|
3
|
-
mock.module("react-native", () => ({}));
|
|
4
|
-
mock.module("react-native-gesture-handler", () => ({}));
|
|
5
|
-
mock.module("react-native-reanimated", () => ({
|
|
6
|
-
clamp: (value: number, lower: number, upper: number) =>
|
|
7
|
-
Math.min(Math.max(value, lower), upper),
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
const { determineDismissal } = await import(
|
|
11
|
-
"../utils/gesture/determine-dismissal"
|
|
12
|
-
);
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { determineDismissal } from "../utils/gesture/determine-dismissal";
|
|
13
3
|
|
|
14
4
|
describe("determineDismissal", () => {
|
|
15
5
|
const dimensions = { width: 320, height: 640 };
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
|
|
2
2
|
import {
|
|
3
3
|
computeContentTransformGeometry,
|
|
4
4
|
computeRelativeGeometry,
|
|
5
|
-
} from "../utils/bounds/
|
|
5
|
+
} from "../utils/bounds/helpers/geometry";
|
|
6
6
|
|
|
7
7
|
describe("computeRelativeGeometry", () => {
|
|
8
8
|
it("calculates correct relative geometry when entering", () => {
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import { describe, expect, it
|
|
2
|
-
|
|
3
|
-
mock.module("react-native", () => ({}));
|
|
4
|
-
mock.module("react-native-gesture-handler", () => ({}));
|
|
5
|
-
mock.module("react-native-reanimated", () => ({
|
|
6
|
-
clamp: (value: number, lower: number, upper: number) =>
|
|
7
|
-
Math.min(Math.max(value, lower), upper),
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
const { velocity } = await import("../utils/gesture/velocity");
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { velocity } from "../utils/gesture/velocity";
|
|
11
3
|
|
|
12
4
|
type Directions = {
|
|
13
5
|
horizontal: boolean;
|