react-native-screen-transitions 3.2.0-beta.2 → 3.2.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.
- package/README.md +346 -620
- package/lib/commonjs/blank-stack/components/stack-view.js +0 -2
- package/lib/commonjs/blank-stack/components/stack-view.js.map +1 -1
- package/lib/commonjs/blank-stack/components/stack-view.native.js +0 -2
- package/lib/commonjs/blank-stack/components/stack-view.native.js.map +1 -1
- package/lib/commonjs/component-stack/components/stack-view.js +16 -20
- package/lib/commonjs/component-stack/components/stack-view.js.map +1 -1
- package/lib/commonjs/native-stack/views/NativeStackView.native.js +3 -3
- package/lib/commonjs/native-stack/views/NativeStackView.native.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/float-overlay.js +1 -6
- package/lib/commonjs/shared/components/overlay/variations/float-overlay.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/overlay-host.js +16 -64
- package/lib/commonjs/shared/components/overlay/variations/overlay-host.js.map +1 -1
- package/lib/commonjs/shared/components/overlay/variations/screen-overlay.js +5 -8
- package/lib/commonjs/shared/components/overlay/variations/screen-overlay.js.map +1 -1
- package/lib/commonjs/shared/components/screen-lifecycle.js +29 -0
- package/lib/commonjs/shared/components/screen-lifecycle.js.map +1 -0
- package/lib/commonjs/shared/constants.js +4 -2
- package/lib/commonjs/shared/constants.js.map +1 -1
- package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +3 -0
- 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/hooks/lifecycle/use-close-transition.js +127 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-close-transition.js.map +1 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js +35 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-open-transition.js.map +1 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-screen-events.js +58 -0
- package/lib/commonjs/shared/hooks/lifecycle/use-screen-events.js.map +1 -0
- package/lib/commonjs/shared/hooks/navigation/use-history.js +24 -0
- package/lib/commonjs/shared/hooks/navigation/use-history.js.map +1 -0
- package/lib/commonjs/shared/hooks/navigation/use-screen-state.js +45 -0
- package/lib/commonjs/shared/hooks/navigation/use-screen-state.js.map +1 -0
- package/lib/commonjs/shared/hooks/use-stable-callback-value.js +0 -2
- package/lib/commonjs/shared/hooks/use-stable-callback-value.js.map +1 -1
- package/lib/commonjs/shared/index.js +14 -0
- package/lib/commonjs/shared/index.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/keys.provider.js +0 -6
- package/lib/commonjs/shared/providers/screen/keys.provider.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/screen-composer.js +9 -7
- package/lib/commonjs/shared/providers/screen/screen-composer.js.map +1 -1
- package/lib/commonjs/shared/providers/screen/styles.provider.js +1 -1
- package/lib/commonjs/shared/providers/screen/styles.provider.js.map +1 -1
- package/lib/commonjs/shared/providers/stack/direct.provider.js +9 -0
- package/lib/commonjs/shared/providers/stack/direct.provider.js.map +1 -1
- package/lib/commonjs/shared/providers/stack/managed.provider.js +9 -0
- package/lib/commonjs/shared/providers/stack/managed.provider.js.map +1 -1
- package/lib/commonjs/shared/stores/animation.store.js +3 -13
- package/lib/commonjs/shared/stores/animation.store.js.map +1 -1
- package/lib/commonjs/shared/stores/history.store.js +185 -0
- package/lib/commonjs/shared/stores/history.store.js.map +1 -0
- package/lib/commonjs/shared/types/index.js +3 -3
- package/lib/commonjs/shared/types/index.js.map +1 -1
- package/lib/commonjs/shared/types/stack.types.js +4 -0
- package/lib/commonjs/shared/types/stack.types.js.map +1 -1
- package/lib/commonjs/shared/utils/animation/start-screen-transition.js +10 -6
- package/lib/commonjs/shared/utils/animation/start-screen-transition.js.map +1 -1
- package/lib/commonjs/shared/utils/bounds/index.js +19 -4
- package/lib/commonjs/shared/utils/bounds/index.js.map +1 -1
- package/lib/module/blank-stack/components/stack-view.js +0 -2
- package/lib/module/blank-stack/components/stack-view.js.map +1 -1
- package/lib/module/blank-stack/components/stack-view.native.js +0 -2
- package/lib/module/blank-stack/components/stack-view.native.js.map +1 -1
- package/lib/module/component-stack/components/stack-view.js +16 -20
- package/lib/module/component-stack/components/stack-view.js.map +1 -1
- package/lib/module/native-stack/views/NativeStackView.native.js +3 -3
- package/lib/module/native-stack/views/NativeStackView.native.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/float-overlay.js +1 -6
- package/lib/module/shared/components/overlay/variations/float-overlay.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/overlay-host.js +16 -64
- package/lib/module/shared/components/overlay/variations/overlay-host.js.map +1 -1
- package/lib/module/shared/components/overlay/variations/screen-overlay.js +5 -8
- package/lib/module/shared/components/overlay/variations/screen-overlay.js.map +1 -1
- package/lib/module/shared/components/screen-lifecycle.js +24 -0
- package/lib/module/shared/components/screen-lifecycle.js.map +1 -0
- package/lib/module/shared/constants.js +3 -1
- package/lib/module/shared/constants.js.map +1 -1
- package/lib/module/shared/hooks/animation/use-screen-animation.js +3 -0
- 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/hooks/lifecycle/use-close-transition.js +122 -0
- package/lib/module/shared/hooks/lifecycle/use-close-transition.js.map +1 -0
- package/lib/module/shared/hooks/lifecycle/use-open-transition.js +32 -0
- package/lib/module/shared/hooks/lifecycle/use-open-transition.js.map +1 -0
- package/lib/module/shared/hooks/lifecycle/use-screen-events.js +54 -0
- package/lib/module/shared/hooks/lifecycle/use-screen-events.js.map +1 -0
- package/lib/module/shared/hooks/navigation/use-history.js +20 -0
- package/lib/module/shared/hooks/navigation/use-history.js.map +1 -0
- package/lib/module/shared/hooks/navigation/use-screen-state.js +41 -0
- package/lib/module/shared/hooks/navigation/use-screen-state.js.map +1 -0
- package/lib/module/shared/hooks/use-stable-callback-value.js +0 -3
- package/lib/module/shared/hooks/use-stable-callback-value.js.map +1 -1
- package/lib/module/shared/index.js +2 -0
- package/lib/module/shared/index.js.map +1 -1
- package/lib/module/shared/providers/screen/keys.provider.js +0 -6
- package/lib/module/shared/providers/screen/keys.provider.js.map +1 -1
- package/lib/module/shared/providers/screen/screen-composer.js +9 -7
- package/lib/module/shared/providers/screen/screen-composer.js.map +1 -1
- package/lib/module/shared/providers/screen/styles.provider.js +1 -1
- package/lib/module/shared/providers/screen/styles.provider.js.map +1 -1
- package/lib/module/shared/providers/stack/direct.provider.js +10 -1
- package/lib/module/shared/providers/stack/direct.provider.js.map +1 -1
- package/lib/module/shared/providers/stack/managed.provider.js +10 -1
- package/lib/module/shared/providers/stack/managed.provider.js.map +1 -1
- package/lib/module/shared/stores/animation.store.js +4 -14
- package/lib/module/shared/stores/animation.store.js.map +1 -1
- package/lib/module/shared/stores/history.store.js +181 -0
- package/lib/module/shared/stores/history.store.js.map +1 -0
- package/lib/module/shared/types/index.js +1 -1
- package/lib/module/shared/types/index.js.map +1 -1
- package/lib/module/shared/types/stack.types.js +5 -0
- package/lib/module/shared/types/stack.types.js.map +1 -1
- package/lib/module/shared/utils/animation/start-screen-transition.js +6 -2
- package/lib/module/shared/utils/animation/start-screen-transition.js.map +1 -1
- package/lib/module/shared/utils/bounds/index.js +19 -4
- package/lib/module/shared/utils/bounds/index.js.map +1 -1
- package/lib/typescript/blank-stack/components/stack-view.d.ts.map +1 -1
- package/lib/typescript/blank-stack/components/stack-view.native.d.ts.map +1 -1
- package/lib/typescript/blank-stack/types.d.ts +1 -8
- package/lib/typescript/blank-stack/types.d.ts.map +1 -1
- package/lib/typescript/component-stack/components/stack-view.d.ts.map +1 -1
- package/lib/typescript/component-stack/types.d.ts +1 -8
- package/lib/typescript/component-stack/types.d.ts.map +1 -1
- package/lib/typescript/native-stack/types.d.ts +2 -3
- package/lib/typescript/native-stack/types.d.ts.map +1 -1
- package/lib/typescript/native-stack/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/shared/components/overlay/variations/float-overlay.d.ts.map +1 -1
- package/lib/typescript/shared/components/overlay/variations/overlay-host.d.ts +1 -6
- package/lib/typescript/shared/components/overlay/variations/overlay-host.d.ts.map +1 -1
- package/lib/typescript/shared/components/overlay/variations/screen-overlay.d.ts +4 -0
- package/lib/typescript/shared/components/overlay/variations/screen-overlay.d.ts.map +1 -1
- package/lib/typescript/shared/components/screen-lifecycle.d.ts +12 -0
- package/lib/typescript/shared/components/screen-lifecycle.d.ts.map +1 -0
- package/lib/typescript/shared/constants.d.ts +2 -1
- package/lib/typescript/shared/constants.d.ts.map +1 -1
- 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/hooks/lifecycle/use-close-transition.d.ts +13 -0
- package/lib/typescript/shared/hooks/lifecycle/use-close-transition.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/lifecycle/use-open-transition.d.ts +11 -0
- package/lib/typescript/shared/hooks/lifecycle/use-open-transition.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/lifecycle/use-screen-events.d.ts +7 -0
- package/lib/typescript/shared/hooks/lifecycle/use-screen-events.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/navigation/use-history.d.ts +37 -0
- package/lib/typescript/shared/hooks/navigation/use-history.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts +40 -0
- package/lib/typescript/shared/hooks/navigation/use-screen-state.d.ts.map +1 -0
- package/lib/typescript/shared/hooks/use-stable-callback-value.d.ts.map +1 -1
- package/lib/typescript/shared/index.d.ts +22 -20
- package/lib/typescript/shared/index.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/keys.provider.d.ts +2 -9
- package/lib/typescript/shared/providers/screen/keys.provider.d.ts.map +1 -1
- package/lib/typescript/shared/providers/screen/screen-composer.d.ts +1 -3
- package/lib/typescript/shared/providers/screen/screen-composer.d.ts.map +1 -1
- package/lib/typescript/shared/providers/stack/direct.provider.d.ts.map +1 -1
- package/lib/typescript/shared/providers/stack/managed.provider.d.ts.map +1 -1
- package/lib/typescript/shared/stores/animation.store.d.ts +3 -4
- package/lib/typescript/shared/stores/animation.store.d.ts.map +1 -1
- package/lib/typescript/shared/stores/history.store.d.ts +82 -0
- package/lib/typescript/shared/stores/history.store.d.ts.map +1 -0
- package/lib/typescript/shared/types/animation.types.d.ts +10 -6
- package/lib/typescript/shared/types/animation.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/bounds.types.d.ts +1 -1
- package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/index.d.ts +2 -2
- package/lib/typescript/shared/types/index.d.ts.map +1 -1
- package/lib/typescript/shared/types/overlay.types.d.ts +21 -0
- package/lib/typescript/shared/types/overlay.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/screen.types.d.ts +5 -4
- package/lib/typescript/shared/types/screen.types.d.ts.map +1 -1
- package/lib/typescript/shared/types/stack.types.d.ts +10 -0
- package/lib/typescript/shared/types/stack.types.d.ts.map +1 -1
- package/lib/typescript/shared/utils/animation/start-screen-transition.d.ts.map +1 -1
- package/lib/typescript/shared/utils/bounds/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/blank-stack/components/stack-view.native.tsx +0 -2
- package/src/blank-stack/components/stack-view.tsx +0 -2
- package/src/blank-stack/types.ts +0 -10
- package/src/component-stack/components/stack-view.tsx +19 -23
- package/src/component-stack/types.ts +0 -10
- package/src/native-stack/types.ts +2 -3
- package/src/native-stack/views/NativeStackView.native.tsx +2 -3
- package/src/shared/__tests__/history.store.test.ts +550 -0
- package/src/shared/components/overlay/variations/float-overlay.tsx +2 -8
- package/src/shared/components/overlay/variations/overlay-host.tsx +18 -84
- package/src/shared/components/overlay/variations/screen-overlay.tsx +6 -15
- package/src/shared/components/screen-lifecycle.tsx +32 -0
- package/src/shared/constants.ts +4 -1
- package/src/shared/hooks/animation/use-screen-animation.tsx +7 -3
- package/src/shared/hooks/gestures/use-build-gestures.tsx +0 -2
- package/src/shared/hooks/lifecycle/use-close-transition.ts +147 -0
- package/src/shared/hooks/lifecycle/use-open-transition.ts +30 -0
- package/src/shared/hooks/lifecycle/use-screen-events.ts +62 -0
- package/src/shared/hooks/navigation/use-history.ts +63 -0
- package/src/shared/hooks/navigation/use-screen-state.tsx +99 -0
- package/src/shared/hooks/use-stable-callback-value.ts +0 -1
- package/src/shared/index.ts +5 -0
- package/src/shared/providers/screen/keys.provider.tsx +1 -25
- package/src/shared/providers/screen/screen-composer.tsx +7 -13
- package/src/shared/providers/screen/styles.provider.tsx +1 -1
- package/src/shared/providers/stack/direct.provider.tsx +11 -1
- package/src/shared/providers/stack/managed.provider.tsx +11 -1
- package/src/shared/stores/animation.store.ts +6 -20
- package/src/shared/stores/history.store.ts +201 -0
- package/src/shared/types/animation.types.ts +11 -6
- package/src/shared/types/bounds.types.ts +1 -0
- package/src/shared/types/index.ts +2 -7
- package/src/shared/types/overlay.types.ts +23 -0
- package/src/shared/types/screen.types.ts +5 -4
- package/src/shared/types/stack.types.ts +17 -1
- package/src/shared/utils/animation/start-screen-transition.ts +5 -2
- package/src/shared/utils/bounds/index.ts +35 -4
- package/lib/commonjs/native-stack/controllers/native-stack-lifecycle.js +0 -90
- package/lib/commonjs/native-stack/controllers/native-stack-lifecycle.js.map +0 -1
- package/lib/commonjs/shared/controller/managed-lifecycle.js +0 -78
- package/lib/commonjs/shared/controller/managed-lifecycle.js.map +0 -1
- package/lib/commonjs/shared/types/state.types.js +0 -9
- package/lib/commonjs/shared/types/state.types.js.map +0 -1
- package/lib/commonjs/shared/utils/read-shared-value.js +0 -17
- package/lib/commonjs/shared/utils/read-shared-value.js.map +0 -1
- package/lib/module/native-stack/controllers/native-stack-lifecycle.js +0 -83
- package/lib/module/native-stack/controllers/native-stack-lifecycle.js.map +0 -1
- package/lib/module/shared/controller/managed-lifecycle.js +0 -72
- package/lib/module/shared/controller/managed-lifecycle.js.map +0 -1
- package/lib/module/shared/types/state.types.js +0 -5
- package/lib/module/shared/types/state.types.js.map +0 -1
- package/lib/module/shared/utils/read-shared-value.js +0 -14
- package/lib/module/shared/utils/read-shared-value.js.map +0 -1
- package/lib/typescript/native-stack/controllers/native-stack-lifecycle.d.ts +0 -8
- package/lib/typescript/native-stack/controllers/native-stack-lifecycle.d.ts.map +0 -1
- package/lib/typescript/shared/controller/managed-lifecycle.d.ts +0 -9
- package/lib/typescript/shared/controller/managed-lifecycle.d.ts.map +0 -1
- package/lib/typescript/shared/types/state.types.d.ts +0 -3
- package/lib/typescript/shared/types/state.types.d.ts.map +0 -1
- package/lib/typescript/shared/utils/read-shared-value.d.ts +0 -7
- package/lib/typescript/shared/utils/read-shared-value.d.ts.map +0 -1
- package/src/native-stack/controllers/native-stack-lifecycle.tsx +0 -96
- package/src/shared/controller/managed-lifecycle.tsx +0 -73
- package/src/shared/types/state.types.ts +0 -2
- package/src/shared/utils/read-shared-value.ts +0 -15
|
@@ -1,131 +1,65 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NavigationContext,
|
|
3
3
|
NavigationRouteContext,
|
|
4
|
-
type Route,
|
|
5
4
|
} from "@react-navigation/native";
|
|
6
5
|
import { memo, useMemo } from "react";
|
|
7
6
|
import { Animated, StyleSheet, useWindowDimensions, View } from "react-native";
|
|
8
7
|
import { useDerivedValue } from "react-native-reanimated";
|
|
9
8
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
10
9
|
import { useScreenAnimation } from "../../../hooks/animation/use-screen-animation";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { useScreenState } from "../../../hooks/navigation/use-screen-state";
|
|
11
|
+
import type { StackScene } from "../../../hooks/navigation/use-stack";
|
|
12
|
+
import type { BaseDescriptor } from "../../../providers/screen/keys.provider";
|
|
13
13
|
import type { OverlayInterpolationProps } from "../../../types/animation.types";
|
|
14
14
|
import type { OverlayProps } from "../../../types/overlay.types";
|
|
15
15
|
|
|
16
16
|
type OverlayHostProps = {
|
|
17
17
|
scene: StackScene;
|
|
18
|
-
scenes: StackScene[];
|
|
19
|
-
routes: Route<string>[];
|
|
20
|
-
overlayIndex: number;
|
|
21
|
-
isFloating?: boolean;
|
|
22
18
|
};
|
|
23
19
|
|
|
24
20
|
export const OverlayHost = memo(function OverlayHost({
|
|
25
21
|
scene,
|
|
26
|
-
scenes,
|
|
27
|
-
routes,
|
|
28
|
-
overlayIndex,
|
|
29
|
-
isFloating,
|
|
30
22
|
}: OverlayHostProps) {
|
|
31
23
|
const OverlayComponent = scene.descriptor.options.overlay;
|
|
32
24
|
const screen = useWindowDimensions();
|
|
33
|
-
|
|
34
|
-
const { stackProgress, optimisticFocusedIndex, routeKeys } = useStack();
|
|
35
25
|
const insets = useSafeAreaInsets();
|
|
36
26
|
|
|
27
|
+
const screenAnimation = useScreenAnimation();
|
|
28
|
+
const screenState = useScreenState();
|
|
37
29
|
const relativeProgress = useDerivedValue(() => {
|
|
38
30
|
"worklet";
|
|
39
|
-
return
|
|
31
|
+
return screenAnimation.value.stackProgress;
|
|
40
32
|
});
|
|
41
33
|
|
|
42
|
-
// For float overlays: global focused index (can be any screen in the stack)
|
|
43
|
-
// For screen overlays: relative to overlay position (only screens at or after)
|
|
44
|
-
const optimisticActiveIndex = useSharedValueState(
|
|
45
|
-
useDerivedValue(() => {
|
|
46
|
-
"worklet";
|
|
47
|
-
if (isFloating) {
|
|
48
|
-
// For float overlays, use global optimistic focused index directly
|
|
49
|
-
const globalIndex = optimisticFocusedIndex.get();
|
|
50
|
-
const clampedIndex = Math.max(
|
|
51
|
-
0,
|
|
52
|
-
Math.min(globalIndex, routeKeys.length - 1),
|
|
53
|
-
);
|
|
54
|
-
return clampedIndex;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// For screen overlays, compute relative to overlay position
|
|
58
|
-
const screensAbove = routeKeys.length - 1 - overlayIndex;
|
|
59
|
-
const relativeOptimistic = optimisticFocusedIndex.value - overlayIndex;
|
|
60
|
-
const result = Math.max(0, Math.min(relativeOptimistic, screensAbove));
|
|
61
|
-
|
|
62
|
-
return result;
|
|
63
|
-
}),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
34
|
const overlayAnimation = useDerivedValue<OverlayInterpolationProps>(() => ({
|
|
67
35
|
progress: relativeProgress.value,
|
|
68
36
|
layouts: { screen },
|
|
69
37
|
insets,
|
|
70
38
|
}));
|
|
71
39
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// For screen overlays, optimisticActiveIndex is relative to overlayIndex
|
|
83
|
-
const maxOffset = Math.max(scenes.length - overlayIndex - 1, 0);
|
|
84
|
-
const normalizedIndex = Math.min(
|
|
85
|
-
Math.max(optimisticActiveIndex, 0),
|
|
86
|
-
maxOffset,
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
return scenes[overlayIndex + normalizedIndex] ?? scene;
|
|
90
|
-
}, [overlayIndex, optimisticActiveIndex, scenes, scene, isFloating]);
|
|
91
|
-
|
|
92
|
-
const screenAnimation = useScreenAnimation();
|
|
40
|
+
const overlayProps: OverlayProps<BaseDescriptor["navigation"]> = useMemo(
|
|
41
|
+
() => ({
|
|
42
|
+
...screenState,
|
|
43
|
+
progress: relativeProgress,
|
|
44
|
+
overlayAnimation, //Deprecated
|
|
45
|
+
screenAnimation, //Deprecated
|
|
46
|
+
}),
|
|
47
|
+
[relativeProgress, overlayAnimation, screenAnimation, screenState],
|
|
48
|
+
);
|
|
93
49
|
|
|
94
50
|
if (!OverlayComponent) {
|
|
95
51
|
return null;
|
|
96
52
|
}
|
|
97
53
|
|
|
98
|
-
const overlayProps: OverlayProps<typeof scene.descriptor.navigation> = {
|
|
99
|
-
routes,
|
|
100
|
-
overlayAnimation,
|
|
101
|
-
screenAnimation,
|
|
102
|
-
focusedRoute: focusedScene.route,
|
|
103
|
-
focusedIndex: optimisticActiveIndex,
|
|
104
|
-
meta: focusedScene.descriptor.options.meta,
|
|
105
|
-
navigation: scene.descriptor.navigation,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
// Cast to OverlayProps function - container overlays are handled by ContainerOverlay component
|
|
109
|
-
const renderOverlay = OverlayComponent as (
|
|
110
|
-
props: OverlayProps,
|
|
111
|
-
) => React.ReactNode;
|
|
112
|
-
|
|
113
54
|
return (
|
|
114
55
|
<Animated.View
|
|
115
56
|
pointerEvents="box-none"
|
|
116
|
-
style={[
|
|
117
|
-
styles.container,
|
|
118
|
-
isFloating ? styles.floating : { zIndex: 1 },
|
|
119
|
-
styles.absolute,
|
|
120
|
-
]}
|
|
57
|
+
style={[styles.container, styles.floating, styles.absolute]}
|
|
121
58
|
>
|
|
122
|
-
<NavigationContext.Provider
|
|
123
|
-
// biome-ignore lint/suspicious/noExplicitAny: navigation type varies by stack
|
|
124
|
-
value={scene.descriptor.navigation as any}
|
|
125
|
-
>
|
|
59
|
+
<NavigationContext.Provider value={scene.descriptor.navigation as any}>
|
|
126
60
|
<NavigationRouteContext.Provider value={scene.route}>
|
|
127
61
|
<View pointerEvents="box-none" style={styles.overlay}>
|
|
128
|
-
{
|
|
62
|
+
<OverlayComponent {...overlayProps} />
|
|
129
63
|
</View>
|
|
130
64
|
</NavigationRouteContext.Provider>
|
|
131
65
|
</NavigationContext.Provider>
|
|
@@ -10,10 +10,14 @@ import { OverlayHost } from "./overlay-host";
|
|
|
10
10
|
/**
|
|
11
11
|
* Screen overlay component that renders per-screen.
|
|
12
12
|
* Gets current descriptor from keys context.
|
|
13
|
+
*
|
|
14
|
+
* @deprecated Screen overlays are deprecated. For per-screen overlays, render an
|
|
15
|
+
* absolute-positioned view directly in your screen component and use `useScreenAnimation()`
|
|
16
|
+
* to access animation values. This component will be removed in a future version.
|
|
13
17
|
*/
|
|
14
18
|
export function ScreenOverlay() {
|
|
15
19
|
const { current } = useKeys<StackDescriptor>();
|
|
16
|
-
const {
|
|
20
|
+
const { flags } = useStack();
|
|
17
21
|
|
|
18
22
|
const options = current.options;
|
|
19
23
|
|
|
@@ -25,12 +29,6 @@ export function ScreenOverlay() {
|
|
|
25
29
|
[current],
|
|
26
30
|
);
|
|
27
31
|
|
|
28
|
-
// Find the index of this screen in the stack
|
|
29
|
-
const overlayIndex = useMemo(
|
|
30
|
-
() => routeKeys.indexOf(current.route.key),
|
|
31
|
-
[routeKeys, current.route.key],
|
|
32
|
-
);
|
|
33
|
-
|
|
34
32
|
// Skip screens without enableTransitions (native-stack only)
|
|
35
33
|
if (!flags.TRANSITIONS_ALWAYS_ON && !options.enableTransitions) {
|
|
36
34
|
return null;
|
|
@@ -40,12 +38,5 @@ export function ScreenOverlay() {
|
|
|
40
38
|
return null;
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
return
|
|
44
|
-
<OverlayHost
|
|
45
|
-
scene={scene}
|
|
46
|
-
scenes={[scene]}
|
|
47
|
-
routes={[]}
|
|
48
|
-
overlayIndex={overlayIndex}
|
|
49
|
-
/>
|
|
50
|
-
);
|
|
41
|
+
return <OverlayHost scene={scene} />;
|
|
51
42
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useCloseTransition } from "../hooks/lifecycle/use-close-transition";
|
|
2
|
+
import { useOpenTransition } from "../hooks/lifecycle/use-open-transition";
|
|
3
|
+
import { useScreenEvents } from "../hooks/lifecycle/use-screen-events";
|
|
4
|
+
import type { BaseDescriptor } from "../providers/screen/keys.provider";
|
|
5
|
+
import { AnimationStore } from "../stores/animation.store";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
current: BaseDescriptor;
|
|
10
|
+
previous?: BaseDescriptor;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Unified lifecycle controller for all stack types.
|
|
15
|
+
*/
|
|
16
|
+
export const ScreenLifecycle = ({ children, current, previous }: Props) => {
|
|
17
|
+
const animations = AnimationStore.getAll(current.route.key);
|
|
18
|
+
|
|
19
|
+
const { activateHighRefreshRate, deactivateHighRefreshRate } =
|
|
20
|
+
useOpenTransition(current, animations);
|
|
21
|
+
|
|
22
|
+
useCloseTransition(
|
|
23
|
+
current,
|
|
24
|
+
animations,
|
|
25
|
+
activateHighRefreshRate,
|
|
26
|
+
deactivateHighRefreshRate,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
useScreenEvents(current, previous, animations);
|
|
30
|
+
|
|
31
|
+
return children;
|
|
32
|
+
};
|
package/src/shared/constants.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const DEFAULT_SCREEN_TRANSITION_STATE: ScreenTransitionState =
|
|
|
24
24
|
progress: 0,
|
|
25
25
|
closing: 0,
|
|
26
26
|
animating: 0,
|
|
27
|
+
entering: 1,
|
|
27
28
|
gesture: {
|
|
28
29
|
x: 0,
|
|
29
30
|
y: 0,
|
|
@@ -71,8 +72,10 @@ export const FULLSCREEN_DIMENSIONS = (
|
|
|
71
72
|
*/
|
|
72
73
|
export const GESTURE_VELOCITY_IMPACT = 0.3;
|
|
73
74
|
export const DEFAULT_GESTURE_DIRECTION = "horizontal";
|
|
74
|
-
export const DEFAULT_GESTURE_ENABLED = false;
|
|
75
75
|
export const DEFAULT_GESTURE_DRIVES_PROGRESS = true;
|
|
76
76
|
export const DEFAULT_GESTURE_ACTIVATION_AREA: ActivationArea = "screen";
|
|
77
77
|
|
|
78
78
|
export const IS_WEB = Platform.OS === "web";
|
|
79
|
+
|
|
80
|
+
export const TRUE = 1;
|
|
81
|
+
export const FALSE = 0;
|
|
@@ -6,7 +6,6 @@ import type { NativeStackScreenTransitionConfig } from "../../../native-stack/ty
|
|
|
6
6
|
import { DEFAULT_SCREEN_TRANSITION_STATE } from "../../constants";
|
|
7
7
|
import {
|
|
8
8
|
type BaseDescriptor,
|
|
9
|
-
type BaseRoute,
|
|
10
9
|
useKeys,
|
|
11
10
|
} from "../../providers/screen/keys.provider";
|
|
12
11
|
import { AnimationStore } from "../../stores/animation.store";
|
|
@@ -16,6 +15,7 @@ import type {
|
|
|
16
15
|
ScreenTransitionState,
|
|
17
16
|
} from "../../types/animation.types";
|
|
18
17
|
import type { ScreenTransitionConfig } from "../../types/screen.types";
|
|
18
|
+
import type { BaseStackRoute } from "../../types/stack.types";
|
|
19
19
|
import { derivations } from "../../utils/animation/derivations";
|
|
20
20
|
import { createBounds } from "../../utils/bounds";
|
|
21
21
|
import { useStack } from "../navigation/use-stack";
|
|
@@ -24,19 +24,21 @@ type BuiltState = {
|
|
|
24
24
|
progress: SharedValue<number>;
|
|
25
25
|
closing: SharedValue<number>;
|
|
26
26
|
animating: SharedValue<number>;
|
|
27
|
+
entering: SharedValue<number>;
|
|
27
28
|
gesture: GestureStoreMap;
|
|
28
|
-
route:
|
|
29
|
+
route: BaseStackRoute;
|
|
29
30
|
meta?: Record<string, unknown>;
|
|
30
31
|
unwrapped: ScreenTransitionState;
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
const createScreenTransitionState = (
|
|
34
|
-
route:
|
|
35
|
+
route: BaseStackRoute,
|
|
35
36
|
meta?: Record<string, unknown>,
|
|
36
37
|
): ScreenTransitionState => ({
|
|
37
38
|
progress: 0,
|
|
38
39
|
closing: 0,
|
|
39
40
|
animating: 0,
|
|
41
|
+
entering: 1,
|
|
40
42
|
gesture: {
|
|
41
43
|
x: 0,
|
|
42
44
|
y: 0,
|
|
@@ -55,6 +57,7 @@ const unwrapInto = (s: BuiltState): ScreenTransitionState => {
|
|
|
55
57
|
const out = s.unwrapped;
|
|
56
58
|
out.progress = s.progress.value;
|
|
57
59
|
out.closing = s.closing.value;
|
|
60
|
+
out.entering = s.entering.value;
|
|
58
61
|
out.animating = s.animating.value;
|
|
59
62
|
out.gesture.x = s.gesture.x.value;
|
|
60
63
|
out.gesture.y = s.gesture.y.value;
|
|
@@ -80,6 +83,7 @@ const useBuildScreenTransitionState = (
|
|
|
80
83
|
return {
|
|
81
84
|
progress: AnimationStore.getAnimation(key, "progress"),
|
|
82
85
|
closing: AnimationStore.getAnimation(key, "closing"),
|
|
86
|
+
entering: AnimationStore.getAnimation(key, "entering"),
|
|
83
87
|
animating: AnimationStore.getAnimation(key, "animating"),
|
|
84
88
|
gesture: GestureStore.getRouteGestures(key),
|
|
85
89
|
route: descriptor.route,
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
DEFAULT_GESTURE_ACTIVATION_AREA,
|
|
16
16
|
DEFAULT_GESTURE_DIRECTION,
|
|
17
17
|
DEFAULT_GESTURE_DRIVES_PROGRESS,
|
|
18
|
-
DEFAULT_GESTURE_ENABLED,
|
|
19
18
|
GESTURE_VELOCITY_IMPACT,
|
|
20
19
|
} from "../../constants";
|
|
21
20
|
import type {
|
|
@@ -35,7 +34,6 @@ import { determineDismissal } from "../../utils/gesture/determine-dismissal";
|
|
|
35
34
|
import { mapGestureToProgress } from "../../utils/gesture/map-gesture-to-progress";
|
|
36
35
|
import { resetGestureValues } from "../../utils/gesture/reset-gesture-values";
|
|
37
36
|
import { velocity } from "../../utils/gesture/velocity";
|
|
38
|
-
import { useStack } from "../navigation/use-stack";
|
|
39
37
|
import useStableCallbackValue from "../use-stable-callback-value";
|
|
40
38
|
|
|
41
39
|
interface BuildGesturesHookProps {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useLayoutEffect } from "react";
|
|
2
|
+
import {
|
|
3
|
+
runOnJS,
|
|
4
|
+
useAnimatedReaction,
|
|
5
|
+
useDerivedValue,
|
|
6
|
+
} from "react-native-reanimated";
|
|
7
|
+
import { useGestureContext } from "../../providers/gestures.provider";
|
|
8
|
+
import type { BaseDescriptor } from "../../providers/screen/keys.provider";
|
|
9
|
+
import { useStackCoreContext } from "../../providers/stack/core.provider";
|
|
10
|
+
import { useManagedStackContext } from "../../providers/stack/managed.provider";
|
|
11
|
+
import type { AnimationStoreMap } from "../../stores/animation.store";
|
|
12
|
+
import { StackType } from "../../types/stack.types";
|
|
13
|
+
import { startScreenTransition } from "../../utils/animation/start-screen-transition";
|
|
14
|
+
import { resetStoresForScreen } from "../../utils/reset-stores-for-screen";
|
|
15
|
+
import { useSharedValueState } from "../reanimated/use-shared-value-state";
|
|
16
|
+
import useStableCallback from "../use-stable-callback";
|
|
17
|
+
|
|
18
|
+
export interface CloseHookParams {
|
|
19
|
+
current: BaseDescriptor;
|
|
20
|
+
animations: AnimationStoreMap;
|
|
21
|
+
activate: () => void;
|
|
22
|
+
deactivate: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Managed close - reacts to closingRouteKeysShared from ManagedStackContext.
|
|
27
|
+
* Used by blank-stack and component-stack.
|
|
28
|
+
*/
|
|
29
|
+
const useManagedClose = ({
|
|
30
|
+
current,
|
|
31
|
+
animations,
|
|
32
|
+
activate,
|
|
33
|
+
deactivate,
|
|
34
|
+
}: CloseHookParams) => {
|
|
35
|
+
const { handleCloseRoute, closingRouteKeysShared } = useManagedStackContext();
|
|
36
|
+
|
|
37
|
+
const handleCloseEnd = useStableCallback((finished: boolean) => {
|
|
38
|
+
if (!finished) return;
|
|
39
|
+
handleCloseRoute({ route: current.route });
|
|
40
|
+
requestAnimationFrame(() => {
|
|
41
|
+
deactivate();
|
|
42
|
+
resetStoresForScreen(current);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
useAnimatedReaction(
|
|
47
|
+
() => closingRouteKeysShared.value,
|
|
48
|
+
(keys) => {
|
|
49
|
+
if (!keys?.includes(current.route.key)) return;
|
|
50
|
+
|
|
51
|
+
runOnJS(activate)();
|
|
52
|
+
startScreenTransition({
|
|
53
|
+
target: "close",
|
|
54
|
+
spec: current.options.transitionSpec,
|
|
55
|
+
animations,
|
|
56
|
+
onAnimationFinish: handleCloseEnd,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Native stack close - listens to beforeRemove navigation event.
|
|
64
|
+
*/
|
|
65
|
+
const useNativeStackClose = ({
|
|
66
|
+
current,
|
|
67
|
+
animations,
|
|
68
|
+
activate,
|
|
69
|
+
deactivate,
|
|
70
|
+
}: CloseHookParams) => {
|
|
71
|
+
const gestureCtx = useGestureContext();
|
|
72
|
+
|
|
73
|
+
const isAncestorDismissingViaGesture = useSharedValueState(
|
|
74
|
+
useDerivedValue(() => {
|
|
75
|
+
"worklet";
|
|
76
|
+
return (
|
|
77
|
+
gestureCtx?.ancestorContext?.gestureAnimationValues.isDismissing
|
|
78
|
+
?.value ?? false
|
|
79
|
+
);
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const handleBeforeRemove = useStableCallback((e: any) => {
|
|
84
|
+
const options = current.options as { enableTransitions?: boolean };
|
|
85
|
+
const isEnabled = options.enableTransitions;
|
|
86
|
+
const navigation = current.navigation;
|
|
87
|
+
const isFirstScreen = navigation.getState().index === 0;
|
|
88
|
+
|
|
89
|
+
// If transitions are disabled, ancestor is dismissing, or first screen - let native handle it
|
|
90
|
+
if (!isEnabled || isAncestorDismissingViaGesture || isFirstScreen) {
|
|
91
|
+
animations.closing.set(1);
|
|
92
|
+
resetStoresForScreen(current);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
activate();
|
|
98
|
+
|
|
99
|
+
startScreenTransition({
|
|
100
|
+
target: "close",
|
|
101
|
+
spec: current.options.transitionSpec,
|
|
102
|
+
animations,
|
|
103
|
+
onAnimationFinish: (finished: boolean) => {
|
|
104
|
+
deactivate();
|
|
105
|
+
if (finished) {
|
|
106
|
+
navigation.dispatch(e.data.action);
|
|
107
|
+
requestAnimationFrame(() => {
|
|
108
|
+
resetStoresForScreen(current);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: Only re-subscribe when navigation changes
|
|
116
|
+
useLayoutEffect(() => {
|
|
117
|
+
return current.navigation.addListener?.("beforeRemove", handleBeforeRemove);
|
|
118
|
+
}, [current.navigation]);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Unified close handler that branches on stack type.
|
|
123
|
+
*/
|
|
124
|
+
export function useCloseTransition(
|
|
125
|
+
current: BaseDescriptor,
|
|
126
|
+
animations: AnimationStoreMap,
|
|
127
|
+
activate: () => void,
|
|
128
|
+
deactivate: () => void,
|
|
129
|
+
) {
|
|
130
|
+
const { flags } = useStackCoreContext();
|
|
131
|
+
const isNativeStack = flags.STACK_TYPE === StackType.NATIVE;
|
|
132
|
+
|
|
133
|
+
const closeParams: CloseHookParams = {
|
|
134
|
+
current,
|
|
135
|
+
animations,
|
|
136
|
+
activate,
|
|
137
|
+
deactivate,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (isNativeStack) {
|
|
141
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: STACK_TYPE is stable per screen instance
|
|
142
|
+
useNativeStackClose(closeParams);
|
|
143
|
+
} else {
|
|
144
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: STACK_TYPE is stable per screen instance
|
|
145
|
+
useManagedClose(closeParams);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useLayoutEffect } from "react";
|
|
2
|
+
import type { BaseDescriptor } from "../../providers/screen/keys.provider";
|
|
3
|
+
import type { AnimationStoreMap } from "../../stores/animation.store";
|
|
4
|
+
import { startScreenTransition } from "../../utils/animation/start-screen-transition";
|
|
5
|
+
import { useHighRefreshRate } from "../animation/use-high-refresh-rate";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Handles opening animation on mount.
|
|
9
|
+
* Returns activate/deactivate functions for high refresh rate.
|
|
10
|
+
*/
|
|
11
|
+
export function useOpenTransition(
|
|
12
|
+
current: BaseDescriptor,
|
|
13
|
+
animations: AnimationStoreMap,
|
|
14
|
+
) {
|
|
15
|
+
const { activateHighRefreshRate, deactivateHighRefreshRate } =
|
|
16
|
+
useHighRefreshRate(current);
|
|
17
|
+
|
|
18
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: Must only run once on mount
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
|
+
activateHighRefreshRate();
|
|
21
|
+
startScreenTransition({
|
|
22
|
+
target: "open",
|
|
23
|
+
spec: current.options.transitionSpec,
|
|
24
|
+
animations,
|
|
25
|
+
onAnimationFinish: deactivateHighRefreshRate,
|
|
26
|
+
});
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
return { activateHighRefreshRate, deactivateHighRefreshRate };
|
|
30
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { runOnJS, useAnimatedReaction } from "react-native-reanimated";
|
|
3
|
+
import type { BaseDescriptor } from "../../providers/screen/keys.provider";
|
|
4
|
+
import type { AnimationStoreMap } from "../../stores/animation.store";
|
|
5
|
+
import { HistoryStore } from "../../stores/history.store";
|
|
6
|
+
import useStableCallback from "../use-stable-callback";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if a screen is a leaf (renders visible content) vs a navigator container.
|
|
10
|
+
* Navigator containers have nested state with routes.
|
|
11
|
+
*/
|
|
12
|
+
function isLeafScreen(navigation: BaseDescriptor["navigation"]): boolean {
|
|
13
|
+
const state = navigation.getState();
|
|
14
|
+
const currentRoute = state.routes[state.index];
|
|
15
|
+
return !("state" in currentRoute);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Updates the HistoryStore for navigation history tracking.
|
|
20
|
+
*/
|
|
21
|
+
export function useScreenEvents(
|
|
22
|
+
current: BaseDescriptor,
|
|
23
|
+
previous: BaseDescriptor | undefined,
|
|
24
|
+
animations: AnimationStoreMap,
|
|
25
|
+
) {
|
|
26
|
+
const navigatorKey = current.navigation.getState()?.key ?? "";
|
|
27
|
+
|
|
28
|
+
// Track history via focus listener - waits for nested navigators to initialize
|
|
29
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: Must only run once on mount
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
// Check on mount (after paint, nested navs initialized)
|
|
32
|
+
if (isLeafScreen(current.navigation)) {
|
|
33
|
+
HistoryStore.focus(current, navigatorKey);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Also listen for focus events
|
|
37
|
+
const unsubscribe = current.navigation.addListener?.("focus", () => {
|
|
38
|
+
if (isLeafScreen(current.navigation)) {
|
|
39
|
+
HistoryStore.focus(current, navigatorKey);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return () => unsubscribe?.();
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
// When closing starts, focus previous in history
|
|
47
|
+
const handleBlur = useStableCallback(() => {
|
|
48
|
+
if (previous && isLeafScreen(previous.navigation)) {
|
|
49
|
+
const prevNavigatorKey = previous.navigation.getState()?.key ?? "";
|
|
50
|
+
HistoryStore.focus(previous, prevNavigatorKey);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
useAnimatedReaction(
|
|
55
|
+
() => animations.closing.get(),
|
|
56
|
+
(closing, prevClosing) => {
|
|
57
|
+
if (closing && !prevClosing) {
|
|
58
|
+
runOnJS(handleBlur)();
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useMemo, useSyncExternalStore } from "react";
|
|
2
|
+
import { type HistoryEntry, HistoryStore } from "../../stores/history.store";
|
|
3
|
+
import type { ScreenKey } from "../../types/screen.types";
|
|
4
|
+
|
|
5
|
+
export interface UseHistoryReturn {
|
|
6
|
+
/**
|
|
7
|
+
* The full history map.
|
|
8
|
+
*/
|
|
9
|
+
history: ReadonlyMap<ScreenKey, HistoryEntry>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get N most recent history entries.
|
|
13
|
+
* Most recent first.
|
|
14
|
+
*/
|
|
15
|
+
getRecent: (n: number) => HistoryEntry[];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get history entries for a specific navigator.
|
|
19
|
+
* Most recent first.
|
|
20
|
+
*/
|
|
21
|
+
getByNavigator: (navigatorKey: string) => HistoryEntry[];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the path between two screens (for multi-waypoint interpolation).
|
|
25
|
+
* Returns screen keys in order from 'from' to 'to'.
|
|
26
|
+
*/
|
|
27
|
+
getPath: (fromKey: ScreenKey, toKey: ScreenKey) => ScreenKey[];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get a specific history entry by screen key.
|
|
31
|
+
*/
|
|
32
|
+
get: (screenKey: ScreenKey) => HistoryEntry | undefined;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the most recent history entry (for forward navigation).
|
|
36
|
+
*/
|
|
37
|
+
getMostRecent: () => HistoryEntry | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Subscribe to history store changes.
|
|
42
|
+
* Returns the full history map and helper methods.
|
|
43
|
+
*/
|
|
44
|
+
export function useHistory(): UseHistoryReturn {
|
|
45
|
+
const history = useSyncExternalStore(
|
|
46
|
+
HistoryStore.subscribe,
|
|
47
|
+
HistoryStore.getSnapshot,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return useMemo(
|
|
51
|
+
() => ({
|
|
52
|
+
history,
|
|
53
|
+
getRecent: (n: number) => HistoryStore.getRecent(n),
|
|
54
|
+
getByNavigator: (navigatorKey: string) =>
|
|
55
|
+
HistoryStore.getByNavigator(navigatorKey),
|
|
56
|
+
getPath: (fromKey: ScreenKey, toKey: ScreenKey) =>
|
|
57
|
+
HistoryStore.getPath(fromKey, toKey),
|
|
58
|
+
get: (screenKey: ScreenKey) => HistoryStore.get(screenKey),
|
|
59
|
+
getMostRecent: () => HistoryStore.getMostRecent(),
|
|
60
|
+
}),
|
|
61
|
+
[history],
|
|
62
|
+
);
|
|
63
|
+
}
|