@react-navigation/stack 8.0.0-alpha.6 → 8.0.0-alpha.60
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/lib/module/TransitionConfigs/CardStyleInterpolators.js +280 -84
- package/lib/module/TransitionConfigs/CardStyleInterpolators.js.map +1 -1
- package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js +11 -44
- package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js.map +1 -1
- package/lib/module/TransitionConfigs/TransitionEasings.js +71 -0
- package/lib/module/TransitionConfigs/TransitionEasings.js.map +1 -0
- package/lib/module/TransitionConfigs/TransitionPresets.js +50 -10
- package/lib/module/TransitionConfigs/TransitionPresets.js.map +1 -1
- package/lib/module/TransitionConfigs/TransitionSpecs.js +69 -34
- package/lib/module/TransitionConfigs/TransitionSpecs.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigators/createStackNavigator.js +37 -33
- package/lib/module/navigators/createStackNavigator.js.map +1 -1
- package/lib/module/utils/GestureHandlerContext.js +5 -0
- package/lib/module/utils/GestureHandlerContext.js.map +1 -0
- package/lib/module/utils/gestureActivationCriteria.js +10 -8
- package/lib/module/utils/gestureActivationCriteria.js.map +1 -1
- package/lib/module/utils/useCardAnimation.js +1 -1
- package/lib/module/utils/useCardAnimation.js.map +1 -1
- package/lib/module/utils/useGestureHandler.js +9 -0
- package/lib/module/utils/useGestureHandler.js.map +1 -0
- package/lib/module/utils/useKeyboardManager.js +71 -26
- package/lib/module/utils/useKeyboardManager.js.map +1 -1
- package/lib/module/views/GestureHandler.js +15 -14
- package/lib/module/views/GestureHandler.js.map +1 -1
- package/lib/module/views/GestureHandlerNative.js +16 -10
- package/lib/module/views/GestureHandlerNative.js.map +1 -1
- package/lib/module/views/Header/Header.js +4 -2
- package/lib/module/views/Header/Header.js.map +1 -1
- package/lib/module/views/Header/HeaderContainer.js +60 -21
- package/lib/module/views/Header/HeaderContainer.js.map +1 -1
- package/lib/module/views/Header/HeaderSegment.js +6 -9
- package/lib/module/views/Header/HeaderSegment.js.map +1 -1
- package/lib/module/views/Stack/Card.js +170 -123
- package/lib/module/views/Stack/Card.js.map +1 -1
- package/lib/module/views/Stack/CardContainer.js +96 -98
- package/lib/module/views/Stack/CardContainer.js.map +1 -1
- package/lib/module/views/Stack/CardContent.js +10 -37
- package/lib/module/views/Stack/CardContent.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +211 -147
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/module/views/Stack/StackView.js +106 -57
- package/lib/module/views/Stack/StackView.js.map +1 -1
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts +20 -12
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts +4 -4
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionEasings.d.ts +38 -0
- package/lib/typescript/src/TransitionConfigs/TransitionEasings.d.ts.map +1 -0
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +15 -3
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +44 -15
- package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +12 -12
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createStackNavigator.d.ts +10 -15
- package/lib/typescript/src/navigators/createStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +120 -108
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/CardAnimationContext.d.ts +1 -1
- package/lib/typescript/src/utils/CardAnimationContext.d.ts.map +1 -1
- package/lib/typescript/src/utils/GestureHandlerContext.d.ts +3 -0
- package/lib/typescript/src/utils/GestureHandlerContext.d.ts.map +1 -0
- package/lib/typescript/src/utils/gestureActivationCriteria.d.ts +4 -51
- package/lib/typescript/src/utils/gestureActivationCriteria.d.ts.map +1 -1
- package/lib/typescript/src/utils/getDistanceForDirection.d.ts +1 -1
- package/lib/typescript/src/utils/getDistanceForDirection.d.ts.map +1 -1
- package/lib/typescript/src/utils/getInvertedMultiplier.d.ts +1 -1
- package/lib/typescript/src/utils/getInvertedMultiplier.d.ts.map +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts.map +1 -1
- package/lib/typescript/src/utils/getShadowStyle.d.ts +5 -5
- package/lib/typescript/src/utils/useCardAnimation.d.ts +1 -1
- package/lib/typescript/src/utils/useCardAnimation.d.ts.map +1 -1
- package/lib/typescript/src/utils/useGestureHandler.d.ts +2 -0
- package/lib/typescript/src/utils/useGestureHandler.d.ts.map +1 -0
- package/lib/typescript/src/utils/useKeyboardManager.d.ts +12 -2
- package/lib/typescript/src/utils/useKeyboardManager.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandler.android.d.ts +1 -1
- package/lib/typescript/src/views/GestureHandler.android.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandler.d.ts +7 -11
- package/lib/typescript/src/views/GestureHandler.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandler.ios.d.ts +1 -1
- package/lib/typescript/src/views/GestureHandler.ios.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandlerNative.d.ts +9 -4
- package/lib/typescript/src/views/GestureHandlerNative.d.ts.map +1 -1
- package/lib/typescript/src/views/Header/Header.d.ts +2 -2
- package/lib/typescript/src/views/Header/Header.d.ts.map +1 -1
- package/lib/typescript/src/views/Header/HeaderContainer.d.ts +9 -3
- package/lib/typescript/src/views/Header/HeaderContainer.d.ts.map +1 -1
- package/lib/typescript/src/views/Header/HeaderSegment.d.ts +6 -4
- package/lib/typescript/src/views/Header/HeaderSegment.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/Card.d.ts +14 -8
- package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContainer.d.ts +7 -7
- package/lib/typescript/src/views/Stack/CardContainer.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContent.d.ts +1 -1
- package/lib/typescript/src/views/Stack/CardContent.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardStack.d.ts +10 -6
- package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/StackView.d.ts +14 -17
- package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -1
- package/package.json +23 -24
- package/src/TransitionConfigs/CardStyleInterpolators.tsx +399 -100
- package/src/TransitionConfigs/HeaderStyleInterpolators.tsx +15 -46
- package/src/TransitionConfigs/TransitionEasings.tsx +90 -0
- package/src/TransitionConfigs/TransitionPresets.tsx +56 -10
- package/src/TransitionConfigs/TransitionSpecs.tsx +68 -34
- package/src/index.tsx +3 -2
- package/src/navigators/createStackNavigator.tsx +74 -91
- package/src/types.tsx +149 -123
- package/src/utils/GestureHandlerContext.tsx +5 -0
- package/src/utils/gestureActivationCriteria.tsx +14 -10
- package/src/utils/useCardAnimation.tsx +1 -1
- package/src/utils/useGestureHandler.tsx +9 -0
- package/src/utils/useKeyboardManager.tsx +96 -33
- package/src/views/GestureHandler.tsx +24 -16
- package/src/views/GestureHandlerNative.tsx +24 -13
- package/src/views/Header/Header.tsx +4 -2
- package/src/views/Header/HeaderContainer.tsx +79 -37
- package/src/views/Header/HeaderSegment.tsx +10 -11
- package/src/views/Stack/Card.tsx +241 -154
- package/src/views/Stack/CardContainer.tsx +111 -127
- package/src/views/Stack/CardContent.tsx +13 -51
- package/src/views/Stack/CardStack.tsx +478 -335
- package/src/views/Stack/StackView.tsx +181 -82
- package/lib/module/utils/GestureHandlerRefContext.js +0 -5
- package/lib/module/utils/GestureHandlerRefContext.js.map +0 -1
- package/lib/module/utils/findLastIndex.js +0 -11
- package/lib/module/utils/findLastIndex.js.map +0 -1
- package/lib/module/utils/useGestureHandlerRef.js +0 -12
- package/lib/module/utils/useGestureHandlerRef.js.map +0 -1
- package/lib/module/views/Stack/CardA11yWrapper.js +0 -43
- package/lib/module/views/Stack/CardA11yWrapper.js.map +0 -1
- package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts +0 -3
- package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts.map +0 -1
- package/lib/typescript/src/utils/findLastIndex.d.ts +0 -2
- package/lib/typescript/src/utils/findLastIndex.d.ts.map +0 -1
- package/lib/typescript/src/utils/useGestureHandlerRef.d.ts +0 -3
- package/lib/typescript/src/utils/useGestureHandlerRef.d.ts.map +0 -1
- package/lib/typescript/src/views/Stack/CardA11yWrapper.d.ts +0 -15
- package/lib/typescript/src/views/Stack/CardA11yWrapper.d.ts.map +0 -1
- package/src/utils/GestureHandlerRefContext.tsx +0 -5
- package/src/utils/findLastIndex.tsx +0 -9
- package/src/utils/useGestureHandlerRef.tsx +0 -15
- package/src/views/Stack/CardA11yWrapper.tsx +0 -67
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { getDefaultHeaderHeight } from '@react-navigation/elements';
|
|
4
|
-
import { SafeAreaProviderCompat } from '@react-navigation/elements/internal';
|
|
4
|
+
import { ActivityView, SafeAreaProviderCompat } from '@react-navigation/elements/internal';
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { Animated, Platform, StyleSheet, View } from 'react-native';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { BottomSheetAndroid, DefaultTransition, FadeFromBottomAndroid, FadeFromRightAndroid, ModalFadeTransition, ModalSlideFromBottomIOS, ModalTransition, RevealFromBottomAndroid, ScaleFromCenterAndroid, SlideFromLeftIOS, SlideFromRightIOS } from "../../TransitionConfigs/TransitionPresets.js";
|
|
10
|
-
import { findLastIndex } from "../../utils/findLastIndex.js";
|
|
7
|
+
import { forBottomSheetAndroid, forCrossDissolveIOS, forDialogAndroid, forFadeFromBottomAndroid, forFadeFromRightAndroid, forFlipIOS, forModalPresentationIOS, forNoAnimation as forNoAnimationCard, forRevealFromBottomAndroid, forScaleFromCenterAndroid, forVerticalIOS } from "../../TransitionConfigs/CardStyleInterpolators.js";
|
|
8
|
+
import { DefaultTransition, FadeFromBottomAndroid, FadeFromRightAndroid, ModalFadeTransition, ModalFlipIOS, ModalSlideFromBottomIOS, ModalTransition, RevealFromBottomAndroid, ScaleFromCenterAndroid, SlideFromLeftIOS, SlideFromRightIOS } from "../../TransitionConfigs/TransitionPresets.js";
|
|
11
9
|
import { getDistanceForDirection } from "../../utils/getDistanceForDirection.js";
|
|
12
10
|
import { getModalRouteKeys } from "../../utils/getModalRoutesKeys.js";
|
|
13
11
|
import { CardContainer } from "./CardContainer.js";
|
|
@@ -17,27 +15,30 @@ const NAMED_TRANSITIONS_PRESETS = {
|
|
|
17
15
|
fade: ModalFadeTransition,
|
|
18
16
|
fade_from_bottom: FadeFromBottomAndroid,
|
|
19
17
|
fade_from_right: FadeFromRightAndroid,
|
|
18
|
+
flip: ModalFlipIOS,
|
|
19
|
+
ios_from_left: SlideFromLeftIOS,
|
|
20
|
+
ios_from_right: SlideFromRightIOS,
|
|
20
21
|
none: DefaultTransition,
|
|
21
22
|
reveal_from_bottom: RevealFromBottomAndroid,
|
|
22
23
|
scale_from_center: ScaleFromCenterAndroid,
|
|
23
24
|
slide_from_left: SlideFromLeftIOS,
|
|
24
25
|
slide_from_right: SlideFromRightIOS,
|
|
25
|
-
slide_from_bottom:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
slide_from_bottom: ModalSlideFromBottomIOS
|
|
27
|
+
};
|
|
28
|
+
const GESTURE_DISABLED_INTERPOLATORS = [forCrossDissolveIOS, forDialogAndroid, forFadeFromBottomAndroid, forFadeFromRightAndroid, forFlipIOS, forRevealFromBottomAndroid, forScaleFromCenterAndroid, forVerticalIOS];
|
|
29
|
+
const SCREEN_HEADER_INTERPOLATORS = [...GESTURE_DISABLED_INTERPOLATORS, forBottomSheetAndroid];
|
|
30
|
+
const HIDDEN_HEADER_HEIGHT = {
|
|
31
|
+
measured: false,
|
|
32
|
+
value: 0
|
|
29
33
|
};
|
|
30
|
-
const EPSILON = 1e-5;
|
|
31
|
-
const STATE_INACTIVE = 0;
|
|
32
|
-
const STATE_TRANSITIONING_OR_BELOW_TOP = 1;
|
|
33
|
-
const STATE_ON_TOP = 2;
|
|
34
|
-
const FALLBACK_DESCRIPTOR = Object.freeze({
|
|
35
|
-
options: {}
|
|
36
|
-
});
|
|
37
34
|
const getInterpolationIndex = (scenes, index) => {
|
|
35
|
+
const scene = scenes[index];
|
|
36
|
+
if (scene == null) {
|
|
37
|
+
throw new Error(`Couldn't find a scene at index ${index}.`);
|
|
38
|
+
}
|
|
38
39
|
const {
|
|
39
40
|
cardStyleInterpolator
|
|
40
|
-
} =
|
|
41
|
+
} = scene.descriptor.options;
|
|
41
42
|
|
|
42
43
|
// Start from current card and count backwards the number of cards with same interpolation
|
|
43
44
|
let interpolationIndex = 0;
|
|
@@ -51,11 +52,11 @@ const getInterpolationIndex = (scenes, index) => {
|
|
|
51
52
|
return interpolationIndex;
|
|
52
53
|
};
|
|
53
54
|
const getIsModalPresentation = cardStyleInterpolator => {
|
|
54
|
-
return cardStyleInterpolator === forModalPresentationIOS ||
|
|
55
|
-
// Handle custom modal presentation interpolators as well
|
|
56
|
-
cardStyleInterpolator.name === 'forModalPresentationIOS';
|
|
55
|
+
return cardStyleInterpolator === forModalPresentationIOS || cardStyleInterpolator.name === forModalPresentationIOS.name;
|
|
57
56
|
};
|
|
58
|
-
const
|
|
57
|
+
const getIsGestureEnabledByDefault = cardStyleInterpolator => !GESTURE_DISABLED_INTERPOLATORS.some(interpolator => cardStyleInterpolator === interpolator || cardStyleInterpolator.name === interpolator.name);
|
|
58
|
+
const getIsScreenHeader = cardStyleInterpolator => SCREEN_HEADER_INTERPOLATORS.some(interpolator => cardStyleInterpolator === interpolator || cardStyleInterpolator.name === interpolator.name);
|
|
59
|
+
const getIsInModalPresentation = (scene, interpolationIndex, isParentModal) => {
|
|
59
60
|
if (isParentModal) {
|
|
60
61
|
return true;
|
|
61
62
|
}
|
|
@@ -72,15 +73,33 @@ const getHeaderHeights = (scenes, insets, isParentHeaderShown, isParentModal, la
|
|
|
72
73
|
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
|
|
73
74
|
headerStyle
|
|
74
75
|
} = curr.descriptor.options;
|
|
75
|
-
const style = StyleSheet.flatten(headerStyle
|
|
76
|
-
const height = 'height' in style && typeof style.height === 'number' ? style.height :
|
|
76
|
+
const style = StyleSheet.flatten(headerStyle);
|
|
77
|
+
const height = style != null && 'height' in style && typeof style.height === 'number' ? style.height : undefined;
|
|
77
78
|
const interpolationIndex = getInterpolationIndex(scenes, index);
|
|
78
|
-
const isModal =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
const isModal = getIsInModalPresentation(curr, interpolationIndex, isParentModal);
|
|
80
|
+
if (typeof height === 'number') {
|
|
81
|
+
acc[curr.route.key] = {
|
|
82
|
+
measured: true,
|
|
83
|
+
value: height
|
|
84
|
+
};
|
|
85
|
+
} else {
|
|
86
|
+
const previousHeight = previous[curr.route.key];
|
|
87
|
+
const defaultHeight = getDefaultHeaderHeight({
|
|
88
|
+
landscape: layout.width > layout.height,
|
|
89
|
+
modalPresentation: isModal,
|
|
90
|
+
topInset: headerStatusBarHeight
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// If we hadn't measured before, use re-computed default height
|
|
94
|
+
if (!previousHeight?.measured && defaultHeight !== previousHeight?.value) {
|
|
95
|
+
acc[curr.route.key] = {
|
|
96
|
+
measured: false,
|
|
97
|
+
value: defaultHeight
|
|
98
|
+
};
|
|
99
|
+
} else if (previousHeight) {
|
|
100
|
+
acc[curr.route.key] = previousHeight;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
84
103
|
return acc;
|
|
85
104
|
}, {});
|
|
86
105
|
};
|
|
@@ -118,32 +137,54 @@ function getDefaultAnimation(animation) {
|
|
|
118
137
|
export function getAnimationEnabled(animation) {
|
|
119
138
|
return getDefaultAnimation(animation) !== 'none';
|
|
120
139
|
}
|
|
140
|
+
const getAllRoutes = (routes, state) => {
|
|
141
|
+
const routeKeys = new Set(routes.map(route => route.key));
|
|
142
|
+
const inactiveRoutes = state.routes.slice(state.index + 1);
|
|
143
|
+
|
|
144
|
+
// If a route is moved from active routes to inactive routes,
|
|
145
|
+
// It can still be in the local copy of `routes` until the animation ends
|
|
146
|
+
// So we need to deduplicate the routes to avoid rendering the same route twice
|
|
147
|
+
return [...routes, ...inactiveRoutes.filter(route => !routeKeys.has(route.key))];
|
|
148
|
+
};
|
|
149
|
+
const isInactiveRoute = (route, routes) =>
|
|
150
|
+
// `routes` contains active routes and routes animating during transitions.
|
|
151
|
+
// Any route added by `getAllRoutes` that's missing from this list is inactive.
|
|
152
|
+
!routes.some(currentRoute => currentRoute.key === route.key);
|
|
121
153
|
export class CardStack extends React.Component {
|
|
122
154
|
static getDerivedStateFromProps(props, state) {
|
|
123
155
|
if (props.routes === state.routes && props.descriptors === state.descriptors) {
|
|
124
156
|
return null;
|
|
125
157
|
}
|
|
126
|
-
const
|
|
158
|
+
const allRoutes = getAllRoutes(props.routes, props.state);
|
|
159
|
+
const gestures = allRoutes.reduce((acc, curr) => {
|
|
127
160
|
const descriptor = props.descriptors[curr.key];
|
|
128
161
|
const {
|
|
129
162
|
animation
|
|
130
163
|
} = descriptor?.options || {};
|
|
131
|
-
acc[curr.key] = state.gestures[curr.key] || new Animated.Value(props.openingRouteKeys.includes(curr.key) && getAnimationEnabled(animation) || props.
|
|
164
|
+
acc[curr.key] = state.gestures[curr.key] || new Animated.Value(props.openingRouteKeys.includes(curr.key) && getAnimationEnabled(animation) || isInactiveRoute(curr, props.routes) ? getDistanceFromOptions(state.layout, descriptor?.options, props.direction === 'rtl') : 0);
|
|
132
165
|
return acc;
|
|
133
166
|
}, {});
|
|
134
|
-
const modalRouteKeys = getModalRouteKeys(
|
|
135
|
-
const scenes =
|
|
136
|
-
// For preloaded screens, we don't care about the previous and the next screen
|
|
137
|
-
const
|
|
138
|
-
const previousRoute =
|
|
139
|
-
const nextRoute =
|
|
167
|
+
const modalRouteKeys = getModalRouteKeys(allRoutes, props.descriptors);
|
|
168
|
+
const scenes = allRoutes.map((route, index, self) => {
|
|
169
|
+
// For preloaded or retained screens, we don't care about the previous and the next screen
|
|
170
|
+
const isInactive = isInactiveRoute(route, props.routes);
|
|
171
|
+
const previousRoute = isInactive ? undefined : self[index - 1];
|
|
172
|
+
const nextRoute = isInactive ? undefined : self[index + 1];
|
|
140
173
|
const oldScene = state.scenes[index];
|
|
174
|
+
const previousScene = oldScene?.route.key === route.key ? oldScene : state.scenes.find(scene => scene.route.key === route.key);
|
|
175
|
+
const closing = previousScene?.closing ?? new Animated.Value(props.closingRouteKeys.includes(route.key) ? 1 : 0);
|
|
141
176
|
const currentGesture = gestures[route.key];
|
|
177
|
+
if (currentGesture == null) {
|
|
178
|
+
throw new Error(`Couldn't find a gesture for route '${route.key}'.`);
|
|
179
|
+
}
|
|
142
180
|
const previousGesture = previousRoute ? gestures[previousRoute.key] : undefined;
|
|
143
181
|
const nextGesture = nextRoute ? gestures[nextRoute.key] : undefined;
|
|
144
|
-
const descriptor = props.descriptors[route.key]
|
|
145
|
-
|
|
146
|
-
|
|
182
|
+
const descriptor = props.descriptors[route.key] ?? state.descriptors[route.key] ?? oldScene?.descriptor;
|
|
183
|
+
if (descriptor == null) {
|
|
184
|
+
throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
|
|
185
|
+
}
|
|
186
|
+
const nextOptions = nextRoute ? (props.descriptors[nextRoute.key] ?? state.descriptors[nextRoute.key])?.options : undefined;
|
|
187
|
+
const previousOptions = previousRoute ? (props.descriptors[previousRoute.key] ?? state.descriptors[previousRoute.key])?.options : undefined;
|
|
147
188
|
|
|
148
189
|
// When a screen is not the last, it should use next screen's transition config
|
|
149
190
|
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
|
@@ -160,14 +201,15 @@ export class CardStack extends React.Component {
|
|
|
160
201
|
const isAnimationEnabled = getAnimationEnabled(animation);
|
|
161
202
|
const transitionPreset = animation !== 'default' ? NAMED_TRANSITIONS_PRESETS[animation] : optionsForTransitionConfig.presentation === 'transparentModal' ? ModalFadeTransition : optionsForTransitionConfig.presentation === 'modal' || isModal ? ModalTransition : DefaultTransition;
|
|
162
203
|
const {
|
|
163
|
-
gestureEnabled = Platform.OS === 'ios' && isAnimationEnabled,
|
|
164
204
|
gestureDirection = transitionPreset.gestureDirection,
|
|
165
205
|
transitionSpec = transitionPreset.transitionSpec,
|
|
166
206
|
cardStyleInterpolator = isAnimationEnabled ? transitionPreset.cardStyleInterpolator : forNoAnimationCard,
|
|
207
|
+
gestureEnabled = Platform.OS === 'ios' && isAnimationEnabled && getIsGestureEnabledByDefault(cardStyleInterpolator),
|
|
167
208
|
headerStyleInterpolator = transitionPreset.headerStyleInterpolator,
|
|
168
|
-
cardOverlayEnabled
|
|
209
|
+
cardOverlayEnabled,
|
|
210
|
+
cardShadowEnabled
|
|
169
211
|
} = optionsForTransitionConfig;
|
|
170
|
-
const headerMode = descriptor.options.headerMode ?? (!(optionsForTransitionConfig.presentation === 'modal' || optionsForTransitionConfig.presentation === 'transparentModal' || nextOptions?.presentation === 'modal' || nextOptions?.presentation === 'transparentModal' || getIsModalPresentation(cardStyleInterpolator)) && Platform.OS === 'ios' && descriptor.options.header === undefined ? 'float' : 'screen');
|
|
212
|
+
const headerMode = descriptor.options.headerMode ?? (!(optionsForTransitionConfig.presentation === 'modal' || optionsForTransitionConfig.presentation === 'transparentModal' || nextOptions?.presentation === 'modal' || nextOptions?.presentation === 'transparentModal' || getIsModalPresentation(cardStyleInterpolator) || getIsScreenHeader(cardStyleInterpolator)) && Platform.OS === 'ios' && descriptor.options.header === undefined ? 'float' : 'screen');
|
|
171
213
|
const isRTL = props.direction === 'rtl';
|
|
172
214
|
const scene = {
|
|
173
215
|
route,
|
|
@@ -177,6 +219,7 @@ export class CardStack extends React.Component {
|
|
|
177
219
|
...descriptor.options,
|
|
178
220
|
animation,
|
|
179
221
|
cardOverlayEnabled,
|
|
222
|
+
cardShadowEnabled,
|
|
180
223
|
cardStyleInterpolator,
|
|
181
224
|
gestureDirection,
|
|
182
225
|
gestureEnabled,
|
|
@@ -190,76 +233,25 @@ export class CardStack extends React.Component {
|
|
|
190
233
|
next: nextGesture && nextOptions?.presentation !== 'transparentModal' ? getProgressFromGesture(nextGesture, state.layout, nextOptions, isRTL) : undefined,
|
|
191
234
|
previous: previousGesture ? getProgressFromGesture(previousGesture, state.layout, previousOptions, isRTL) : undefined
|
|
192
235
|
},
|
|
236
|
+
closing,
|
|
193
237
|
__memo: [state.layout, descriptor, nextOptions, previousOptions, currentGesture, nextGesture, previousGesture]
|
|
194
238
|
};
|
|
195
239
|
if (oldScene && scene.__memo.every((it, i) => {
|
|
196
|
-
// @ts-expect-error: we haven't added __memo to the annotation to prevent usage elsewhere
|
|
197
240
|
return oldScene.__memo[i] === it;
|
|
198
241
|
})) {
|
|
199
242
|
return oldScene;
|
|
200
243
|
}
|
|
201
244
|
return scene;
|
|
202
245
|
});
|
|
203
|
-
let activeStates = state.activeStates;
|
|
204
|
-
if (props.routes.length !== state.routes.length) {
|
|
205
|
-
let activeScreensLimit = 1;
|
|
206
|
-
for (let i = props.routes.length - 1; i >= 0; i--) {
|
|
207
|
-
const {
|
|
208
|
-
options
|
|
209
|
-
} = scenes[i].descriptor;
|
|
210
|
-
const {
|
|
211
|
-
// By default, we don't want to detach the previous screen of the active one for modals
|
|
212
|
-
detachPreviousScreen = options.presentation === 'transparentModal' ? false : getIsModalPresentation(options.cardStyleInterpolator) ? i !== findLastIndex(scenes, scene => {
|
|
213
|
-
const {
|
|
214
|
-
cardStyleInterpolator
|
|
215
|
-
} = scene.descriptor.options;
|
|
216
|
-
return cardStyleInterpolator === forModalPresentationIOS || cardStyleInterpolator?.name === 'forModalPresentationIOS';
|
|
217
|
-
}) : true
|
|
218
|
-
} = options;
|
|
219
|
-
if (detachPreviousScreen === false) {
|
|
220
|
-
activeScreensLimit++;
|
|
221
|
-
} else {
|
|
222
|
-
// Check at least last 2 screens before stopping
|
|
223
|
-
// This will make sure that screen isn't detached when another screen is animating on top of the transparent one
|
|
224
|
-
// e.g. opaque -> transparent -> opaque
|
|
225
|
-
if (i <= props.routes.length - 2) {
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
activeStates = props.routes.map((_, index, self) => {
|
|
231
|
-
// The activity state represents state of the screen:
|
|
232
|
-
// 0 - inactive, the screen is detached
|
|
233
|
-
// 1 - transitioning or below the top screen, the screen is mounted but interaction is disabled
|
|
234
|
-
// 2 - on top of the stack, the screen is mounted and interaction is enabled
|
|
235
|
-
let activityState;
|
|
236
|
-
const lastActiveState = state.activeStates[index];
|
|
237
|
-
const activeAfterTransition = index >= self.length - activeScreensLimit;
|
|
238
|
-
if (lastActiveState === STATE_INACTIVE && !activeAfterTransition) {
|
|
239
|
-
// screen was inactive before and it will still be inactive after the transition
|
|
240
|
-
activityState = STATE_INACTIVE;
|
|
241
|
-
} else {
|
|
242
|
-
const sceneForActivity = scenes[self.length - 1];
|
|
243
|
-
const outputValue = index === self.length - 1 ? STATE_ON_TOP // the screen is on top after the transition
|
|
244
|
-
: activeAfterTransition ? STATE_TRANSITIONING_OR_BELOW_TOP // the screen should stay active after the transition, it is not on top but is in activeLimit
|
|
245
|
-
: STATE_INACTIVE; // the screen should be active only during the transition, it is at the edge of activeLimit
|
|
246
|
-
|
|
247
|
-
activityState = sceneForActivity ? sceneForActivity.progress.current.interpolate({
|
|
248
|
-
inputRange: [0, 1 - EPSILON, 1],
|
|
249
|
-
outputRange: [1, 1, outputValue],
|
|
250
|
-
extrapolate: 'clamp'
|
|
251
|
-
}) : STATE_TRANSITIONING_OR_BELOW_TOP;
|
|
252
|
-
}
|
|
253
|
-
return activityState;
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
246
|
return {
|
|
257
247
|
routes: props.routes,
|
|
258
248
|
scenes,
|
|
259
249
|
gestures,
|
|
260
250
|
descriptors: props.descriptors,
|
|
261
|
-
|
|
262
|
-
|
|
251
|
+
headerHeights: getHeaderHeights(scenes, props.insets, props.isParentHeaderShown, props.isParentModal, state.layout, state.headerHeights),
|
|
252
|
+
// Nested navigators clean up `route.state` when the screen is paused with `Activity`
|
|
253
|
+
// So we remember routes which had state until they are removed
|
|
254
|
+
nestedStateRouteKeys: allRoutes.filter(route => 'state' in route && route.state || state.nestedStateRouteKeys.includes(route.key)).map(route => route.key)
|
|
263
255
|
};
|
|
264
256
|
}
|
|
265
257
|
constructor(props) {
|
|
@@ -270,13 +262,13 @@ export class CardStack extends React.Component {
|
|
|
270
262
|
gestures: {},
|
|
271
263
|
layout: SafeAreaProviderCompat.initialMetrics.frame,
|
|
272
264
|
descriptors: this.props.descriptors,
|
|
273
|
-
activeStates: [],
|
|
274
265
|
// Used when card's header is null and mode is float to make transition
|
|
275
266
|
// between screens with headers and those without headers smooth.
|
|
276
267
|
// This is not a great heuristic here. We don't know synchronously
|
|
277
268
|
// on mount what the header height is so we have just used the most
|
|
278
269
|
// common cases here.
|
|
279
|
-
headerHeights: {}
|
|
270
|
+
headerHeights: {},
|
|
271
|
+
nestedStateRouteKeys: []
|
|
280
272
|
};
|
|
281
273
|
}
|
|
282
274
|
handleLayout = e => {
|
|
@@ -306,13 +298,16 @@ export class CardStack extends React.Component {
|
|
|
306
298
|
headerHeights
|
|
307
299
|
}) => {
|
|
308
300
|
const previousHeight = headerHeights[route.key];
|
|
309
|
-
if (previousHeight === height) {
|
|
301
|
+
if (previousHeight?.value === height) {
|
|
310
302
|
return null;
|
|
311
303
|
}
|
|
312
304
|
return {
|
|
313
305
|
headerHeights: {
|
|
314
306
|
...headerHeights,
|
|
315
|
-
[route.key]:
|
|
307
|
+
[route.key]: {
|
|
308
|
+
measured: true,
|
|
309
|
+
value: height
|
|
310
|
+
}
|
|
316
311
|
}
|
|
317
312
|
};
|
|
318
313
|
});
|
|
@@ -321,7 +316,11 @@ export class CardStack extends React.Component {
|
|
|
321
316
|
const {
|
|
322
317
|
state
|
|
323
318
|
} = this.props;
|
|
324
|
-
|
|
319
|
+
const route = state.routes[state.index];
|
|
320
|
+
if (route == null) {
|
|
321
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
322
|
+
}
|
|
323
|
+
return route;
|
|
325
324
|
};
|
|
326
325
|
getPreviousScene = ({
|
|
327
326
|
route
|
|
@@ -348,27 +347,36 @@ export class CardStack extends React.Component {
|
|
|
348
347
|
routes,
|
|
349
348
|
openingRouteKeys,
|
|
350
349
|
closingRouteKeys,
|
|
350
|
+
replacingRouteKeys,
|
|
351
351
|
onOpenRoute,
|
|
352
352
|
onCloseRoute,
|
|
353
|
+
onGestureStart,
|
|
354
|
+
onGestureEnd,
|
|
355
|
+
onGestureCancel,
|
|
353
356
|
renderHeader,
|
|
354
357
|
isParentHeaderShown,
|
|
355
358
|
isParentModal,
|
|
356
359
|
onTransitionStart,
|
|
357
|
-
onTransitionEnd
|
|
358
|
-
onGestureStart,
|
|
359
|
-
onGestureEnd,
|
|
360
|
-
onGestureCancel,
|
|
361
|
-
detachInactiveScreens = Platform.OS === 'web' || Platform.OS === 'android' || Platform.OS === 'ios'
|
|
360
|
+
onTransitionEnd
|
|
362
361
|
} = this.props;
|
|
363
362
|
const {
|
|
364
363
|
scenes,
|
|
365
364
|
layout,
|
|
366
365
|
gestures,
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
headerHeights,
|
|
367
|
+
nestedStateRouteKeys
|
|
369
368
|
} = this.state;
|
|
369
|
+
const allRoutes = getAllRoutes(routes, state);
|
|
370
370
|
const focusedRoute = state.routes[state.index];
|
|
371
|
-
|
|
371
|
+
if (focusedRoute == null) {
|
|
372
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Render only two top-most active headers as a workaround for
|
|
376
|
+
// https://github.com/react-navigation/react-navigation/issues/12456.
|
|
377
|
+
// If the header is persisted, it might be placed incorrectly when navigating back
|
|
378
|
+
const activeHeaderScenes = scenes.filter(scene => !isInactiveRoute(scene.route, routes)).slice(-2);
|
|
379
|
+
const isFloatHeaderAbsolute = activeHeaderScenes.some(scene => {
|
|
372
380
|
const options = scene.descriptor.options ?? {};
|
|
373
381
|
const {
|
|
374
382
|
headerMode,
|
|
@@ -381,71 +389,129 @@ export class CardStack extends React.Component {
|
|
|
381
389
|
return false;
|
|
382
390
|
});
|
|
383
391
|
const isFloatHeaderFocused = scenes.findLast(scene => scene.route.key === focusedRoute.key)?.descriptor.options.headerMode === 'float';
|
|
392
|
+
const focusedHeaderHeight = headerHeights[focusedRoute.key];
|
|
384
393
|
return /*#__PURE__*/_jsxs(View, {
|
|
385
394
|
style: styles.container,
|
|
386
395
|
children: [renderHeader({
|
|
387
396
|
mode: 'float',
|
|
388
|
-
scenes,
|
|
397
|
+
scenes: activeHeaderScenes,
|
|
389
398
|
getPreviousScene: this.getPreviousScene,
|
|
390
399
|
getFocusedRoute: this.getFocusedRoute,
|
|
400
|
+
contentHeight: headerHeights[focusedRoute.key],
|
|
391
401
|
onContentHeightChange: this.handleHeaderLayout,
|
|
392
402
|
style: [isFloatHeaderAbsolute && [styles.absolute,
|
|
393
403
|
// The header title can't be found with maestro without this
|
|
394
404
|
// In some cases, header buttons may not be touchable as well
|
|
395
405
|
isFloatHeaderFocused && {
|
|
396
|
-
height:
|
|
406
|
+
height: focusedHeaderHeight?.value
|
|
397
407
|
}]]
|
|
398
|
-
}), /*#__PURE__*/_jsx(
|
|
399
|
-
enabled: detachInactiveScreens,
|
|
408
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
400
409
|
style: styles.container,
|
|
401
410
|
onLayout: this.handleLayout,
|
|
402
|
-
children:
|
|
411
|
+
children: allRoutes.map((route, index, self) => {
|
|
403
412
|
const focused = focusedRoute.key === route.key;
|
|
404
413
|
const gesture = gestures[route.key];
|
|
405
414
|
const scene = scenes[index];
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (state.preloadedRoutes.includes(route) && routes.includes(route) && index >= routes.length) {
|
|
412
|
-
return null;
|
|
415
|
+
if (gesture == null) {
|
|
416
|
+
throw new Error(`Couldn't find a gesture for route '${route.key}'.`);
|
|
417
|
+
}
|
|
418
|
+
if (scene == null) {
|
|
419
|
+
throw new Error(`Couldn't find a scene at index ${index}.`);
|
|
413
420
|
}
|
|
421
|
+
const nextScene = scenes[index + 1];
|
|
422
|
+
const nextRoute = self[index + 1];
|
|
414
423
|
const {
|
|
424
|
+
inactiveBehavior = 'pause',
|
|
425
|
+
animation,
|
|
415
426
|
headerShown = true,
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
autoHideHomeIndicator
|
|
427
|
+
headerMode,
|
|
428
|
+
headerTransparent
|
|
419
429
|
} = scene.descriptor.options;
|
|
420
430
|
const safeAreaInsetTop = insets.top;
|
|
421
431
|
const safeAreaInsetRight = insets.right;
|
|
422
432
|
const safeAreaInsetBottom = insets.bottom;
|
|
423
433
|
const safeAreaInsetLeft = insets.left;
|
|
424
|
-
const headerHeight = headerShown !== false ? headerHeights[route.key] :
|
|
434
|
+
const headerHeight = headerShown !== false ? headerHeights[route.key] : HIDDEN_HEADER_HEIGHT;
|
|
425
435
|
|
|
426
436
|
// Start from current card and count backwards the number of cards with same interpolation
|
|
427
437
|
const interpolationIndex = getInterpolationIndex(scenes, index);
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
438
|
+
const isInModalPresentation = getIsInModalPresentation(scene, interpolationIndex, isParentModal);
|
|
439
|
+
const isModal = scene.descriptor.options.presentation === 'modal' || getIsModalPresentation(scene.descriptor.options.cardStyleInterpolator);
|
|
440
|
+
const isNextScreenModal = nextScene != null && (nextScene.descriptor.options.presentation === 'modal' || getIsModalPresentation(nextScene.descriptor.options.cardStyleInterpolator));
|
|
441
|
+
const isNextScreenTransparent = nextScene?.descriptor.options.presentation === 'transparentModal';
|
|
442
|
+
const isAnimationEnabled = getAnimationEnabled(animation) || (
|
|
443
|
+
// Also check next screen's animation,
|
|
444
|
+
// As it will result in both screens being visible
|
|
445
|
+
nextScene ? getAnimationEnabled(nextScene.descriptor.options.animation) : false);
|
|
446
|
+
const isRemoving = replacingRouteKeys.includes(route.key) || closingRouteKeys.includes(route.key);
|
|
447
|
+
const isFocusing = openingRouteKeys.includes(route.key) || nextRoute != null && [...closingRouteKeys, ...replacingRouteKeys].includes(nextRoute.key);
|
|
448
|
+
const isInactive = isInactiveRoute(route, this.props.routes);
|
|
449
|
+
const isRetained = state.retainedRouteKeys?.includes(route.key);
|
|
450
|
+
const isPreloaded = isInactive && !isRetained;
|
|
451
|
+
const isTopmost = index === routes.length - 1;
|
|
452
|
+
const isBeforeLast = index === routes.length - 2;
|
|
453
|
+
|
|
454
|
+
// Keep animating and the last two rendered routes visible for smoother transitions
|
|
455
|
+
const isVisible = focused ||
|
|
456
|
+
// React native destroys the view when hidden
|
|
457
|
+
// So we keep retained screens visible
|
|
458
|
+
// This makes sure any videos being played don't stop etc.
|
|
459
|
+
isRetained || isNextScreenTransparent ||
|
|
460
|
+
// Modal presentation on iOS has multiple screens visible at the same time
|
|
461
|
+
// The base screen before the modals renders the dim, which is always persistent
|
|
462
|
+
// On iPadOS, the base screen is visible while modal is in a smaller area on top
|
|
463
|
+
// The top edge of the last 3 screens can be seen animating behind the modal
|
|
464
|
+
isNextScreenModal && (!isModal || index >= routes.length - 3) ||
|
|
465
|
+
// We only need to keep other screens visible when animation is enabled
|
|
466
|
+
isAnimationEnabled && (isFocusing || isRemoving ||
|
|
467
|
+
// Preloaded screens should stay mounted, but remain hidden until focused
|
|
468
|
+
!isPreloaded && index >= routes.length - 2);
|
|
469
|
+
const activityMode =
|
|
470
|
+
// Keep focused and focusing screens interactive
|
|
471
|
+
focused || isFocusing ? 'normal' : inactiveBehavior === 'none' ||
|
|
472
|
+
// Keep effects running until the closing animation finishes
|
|
473
|
+
isRemoving ||
|
|
474
|
+
// Unpause preloaded or retained screens so updates are visible
|
|
475
|
+
// Handle retained explicitly as isInactive won't be updated until animation end
|
|
476
|
+
isInactive || isRetained ||
|
|
477
|
+
// Keep the screen before transparent screen active
|
|
478
|
+
// This lets the screen under the transparent screen update and animate
|
|
479
|
+
isNextScreenTransparent ||
|
|
480
|
+
// Keep the screen before last screen active
|
|
481
|
+
// Otherwise it breaks animation when going back
|
|
482
|
+
isBeforeLast ? 'inert' : inactiveBehavior === 'unmount' &&
|
|
483
|
+
// Don't unmount screens that needs to stay visible
|
|
484
|
+
!isVisible &&
|
|
485
|
+
// Don't unmount screens with nested navigators
|
|
486
|
+
// So we don't lose their state
|
|
487
|
+
!nestedStateRouteKeys.includes(route.key) ? 'unmounted' : 'paused';
|
|
488
|
+
if (activityMode === 'unmounted') {
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
return /*#__PURE__*/_jsx(ActivityView, {
|
|
492
|
+
mode: activityMode,
|
|
493
|
+
visible: isVisible,
|
|
494
|
+
style: {
|
|
495
|
+
...StyleSheet.absoluteFill,
|
|
496
|
+
marginTop: headerTransparent || headerMode === 'screen' ? 0 :
|
|
497
|
+
// Add offset for non-transparent floating header
|
|
498
|
+
isFloatHeaderAbsolute ?
|
|
499
|
+
// If header is absolute, offset the card by the header height
|
|
500
|
+
headerHeight?.value :
|
|
501
|
+
// Otherwise, the in-flow float header already takes the
|
|
502
|
+
// focused header's space, so offset by the difference
|
|
503
|
+
headerHeight && focusedHeaderHeight ? headerHeight.value - focusedHeaderHeight.value : 0
|
|
504
|
+
},
|
|
441
505
|
children: /*#__PURE__*/_jsx(CardContainer, {
|
|
442
506
|
index: index,
|
|
443
507
|
interpolationIndex: interpolationIndex,
|
|
444
|
-
modal:
|
|
445
|
-
active:
|
|
508
|
+
modal: isInModalPresentation,
|
|
509
|
+
active: isTopmost,
|
|
446
510
|
focused: focused,
|
|
447
511
|
opening: openingRouteKeys.includes(route.key),
|
|
448
512
|
closing: closingRouteKeys.includes(route.key),
|
|
513
|
+
closingAnimated: scene.closing,
|
|
514
|
+
nextClosingAnimated: nextScene?.closing,
|
|
449
515
|
layout: layout,
|
|
450
516
|
gesture: gesture,
|
|
451
517
|
scene: scene,
|
|
@@ -461,15 +527,13 @@ export class CardStack extends React.Component {
|
|
|
461
527
|
onHeaderHeightChange: this.handleHeaderLayout,
|
|
462
528
|
getPreviousScene: this.getPreviousScene,
|
|
463
529
|
getFocusedRoute: this.getFocusedRoute,
|
|
464
|
-
hasAbsoluteFloatHeader: isFloatHeaderAbsolute && !headerTransparent,
|
|
465
530
|
renderHeader: renderHeader,
|
|
466
531
|
onOpenRoute: onOpenRoute,
|
|
467
532
|
onCloseRoute: onCloseRoute,
|
|
468
533
|
onTransitionStart: onTransitionStart,
|
|
469
534
|
onTransitionEnd: onTransitionEnd,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
preloaded: isPreloaded
|
|
535
|
+
preloaded: isInactive,
|
|
536
|
+
children: scene.descriptor.render()
|
|
473
537
|
})
|
|
474
538
|
}, route.key);
|
|
475
539
|
})
|