@react-navigation/stack 8.0.0-alpha.4 → 8.0.0-alpha.40
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/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigators/createStackNavigator.js +3 -7
- 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 +2 -2
- package/lib/module/views/Header/Header.js.map +1 -1
- package/lib/module/views/Header/HeaderContainer.js +52 -18
- package/lib/module/views/Header/HeaderContainer.js.map +1 -1
- package/lib/module/views/Header/HeaderSegment.js +2 -2
- package/lib/module/views/Header/HeaderSegment.js.map +1 -1
- package/lib/module/views/Stack/Card.js +90 -87
- package/lib/module/views/Stack/Card.js.map +1 -1
- package/lib/module/views/Stack/CardContainer.js +72 -98
- package/lib/module/views/Stack/CardContainer.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +171 -126
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/module/views/Stack/StackView.js +103 -53
- package/lib/module/views/Stack/StackView.js.map +1 -1
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts +1 -1
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts +1 -1
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +1 -1
- 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 +46 -53
- 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/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 +5 -4
- package/lib/typescript/src/views/Header/HeaderSegment.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/Card.d.ts +5 -5
- package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContainer.d.ts +5 -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 +6 -5
- package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/StackView.d.ts +10 -13
- package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -1
- package/package.json +21 -23
- package/src/index.tsx +3 -2
- package/src/navigators/createStackNavigator.tsx +9 -40
- package/src/types.tsx +73 -73
- 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 +27 -16
- package/src/views/GestureHandlerNative.tsx +26 -12
- package/src/views/Header/Header.tsx +2 -2
- package/src/views/Header/HeaderContainer.tsx +67 -24
- package/src/views/Header/HeaderSegment.tsx +4 -4
- package/src/views/Stack/Card.tsx +134 -115
- package/src/views/Stack/CardContainer.tsx +86 -127
- package/src/views/Stack/CardStack.tsx +380 -323
- package/src/views/Stack/StackView.tsx +173 -71
- 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,5 +1,8 @@
|
|
|
1
1
|
import { getDefaultHeaderHeight } from '@react-navigation/elements';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ActivityView,
|
|
4
|
+
SafeAreaProviderCompat,
|
|
5
|
+
} from '@react-navigation/elements/internal';
|
|
3
6
|
import type {
|
|
4
7
|
LocaleDirection,
|
|
5
8
|
ParamListBase,
|
|
@@ -15,7 +18,6 @@ import {
|
|
|
15
18
|
View,
|
|
16
19
|
} from 'react-native';
|
|
17
20
|
import type { EdgeInsets } from 'react-native-safe-area-context';
|
|
18
|
-
import { Screen, ScreenContainer } from 'react-native-screens';
|
|
19
21
|
|
|
20
22
|
import {
|
|
21
23
|
forModalPresentationIOS,
|
|
@@ -44,16 +46,20 @@ import type {
|
|
|
44
46
|
StackNavigationOptions,
|
|
45
47
|
TransitionPreset,
|
|
46
48
|
} from '../../types';
|
|
47
|
-
import { findLastIndex } from '../../utils/findLastIndex';
|
|
48
49
|
import { getDistanceForDirection } from '../../utils/getDistanceForDirection';
|
|
49
50
|
import { getModalRouteKeys } from '../../utils/getModalRoutesKeys';
|
|
50
|
-
import type {
|
|
51
|
+
import type {
|
|
52
|
+
HeaderHeight,
|
|
53
|
+
Props as HeaderContainerProps,
|
|
54
|
+
} from '../Header/HeaderContainer';
|
|
51
55
|
import { CardContainer } from './CardContainer';
|
|
52
56
|
|
|
53
57
|
type GestureValues = {
|
|
54
58
|
[key: string]: Animated.Value;
|
|
55
59
|
};
|
|
56
60
|
|
|
61
|
+
type HeaderHeights = Record<string, HeaderHeight>;
|
|
62
|
+
|
|
57
63
|
type Props = {
|
|
58
64
|
direction: LocaleDirection;
|
|
59
65
|
insets: EdgeInsets;
|
|
@@ -62,6 +68,7 @@ type Props = {
|
|
|
62
68
|
routes: Route<string>[];
|
|
63
69
|
openingRouteKeys: string[];
|
|
64
70
|
closingRouteKeys: string[];
|
|
71
|
+
replacingRouteKeys: string[];
|
|
65
72
|
onOpenRoute: (props: { route: Route<string> }) => void;
|
|
66
73
|
onCloseRoute: (props: { route: Route<string> }) => void;
|
|
67
74
|
getPreviousRoute: (props: {
|
|
@@ -87,8 +94,7 @@ type State = {
|
|
|
87
94
|
scenes: Scene[];
|
|
88
95
|
gestures: GestureValues;
|
|
89
96
|
layout: Layout;
|
|
90
|
-
|
|
91
|
-
headerHeights: Record<string, number>;
|
|
97
|
+
headerHeights: HeaderHeights;
|
|
92
98
|
};
|
|
93
99
|
|
|
94
100
|
const NAMED_TRANSITIONS_PRESETS = {
|
|
@@ -107,16 +113,16 @@ const NAMED_TRANSITIONS_PRESETS = {
|
|
|
107
113
|
}),
|
|
108
114
|
} as const satisfies Record<StackAnimationName, TransitionPreset>;
|
|
109
115
|
|
|
110
|
-
const
|
|
116
|
+
const HIDDEN_HEADER_HEIGHT = { measured: false, value: 0 };
|
|
111
117
|
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const STATE_ON_TOP = 2;
|
|
118
|
+
const getInterpolationIndex = (scenes: Scene[], index: number) => {
|
|
119
|
+
const scene = scenes[index];
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
if (scene == null) {
|
|
122
|
+
throw new Error(`Couldn't find a scene at index ${index}.`);
|
|
123
|
+
}
|
|
117
124
|
|
|
118
|
-
const
|
|
119
|
-
const { cardStyleInterpolator } = scenes[index].descriptor.options;
|
|
125
|
+
const { cardStyleInterpolator } = scene.descriptor.options;
|
|
120
126
|
|
|
121
127
|
// Start from current card and count backwards the number of cards with same interpolation
|
|
122
128
|
let interpolationIndex = 0;
|
|
@@ -167,9 +173,9 @@ const getHeaderHeights = (
|
|
|
167
173
|
isParentHeaderShown: boolean,
|
|
168
174
|
isParentModal: boolean,
|
|
169
175
|
layout: Layout,
|
|
170
|
-
previous:
|
|
176
|
+
previous: HeaderHeights
|
|
171
177
|
) => {
|
|
172
|
-
return scenes.reduce<
|
|
178
|
+
return scenes.reduce<HeaderHeights>((acc, curr, index) => {
|
|
173
179
|
const {
|
|
174
180
|
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top,
|
|
175
181
|
headerStyle,
|
|
@@ -180,19 +186,34 @@ const getHeaderHeights = (
|
|
|
180
186
|
const height =
|
|
181
187
|
'height' in style && typeof style.height === 'number'
|
|
182
188
|
? style.height
|
|
183
|
-
:
|
|
189
|
+
: undefined;
|
|
184
190
|
|
|
185
191
|
const interpolationIndex = getInterpolationIndex(scenes, index);
|
|
186
192
|
const isModal = getIsModal(curr, interpolationIndex, isParentModal);
|
|
187
193
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
if (typeof height === 'number') {
|
|
195
|
+
acc[curr.route.key] = { measured: true, value: height };
|
|
196
|
+
} else {
|
|
197
|
+
const previousHeight = previous[curr.route.key];
|
|
198
|
+
const defaultHeight = getDefaultHeaderHeight({
|
|
199
|
+
landscape: layout.width > layout.height,
|
|
200
|
+
modalPresentation: isModal,
|
|
201
|
+
topInset: headerStatusBarHeight,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// If we hadn't measured before, use re-computed default height
|
|
205
|
+
if (
|
|
206
|
+
!previousHeight?.measured &&
|
|
207
|
+
defaultHeight !== previousHeight?.value
|
|
208
|
+
) {
|
|
209
|
+
acc[curr.route.key] = {
|
|
210
|
+
measured: false,
|
|
211
|
+
value: defaultHeight,
|
|
212
|
+
};
|
|
213
|
+
} else if (previousHeight) {
|
|
214
|
+
acc[curr.route.key] = previousHeight;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
196
217
|
|
|
197
218
|
return acc;
|
|
198
219
|
}, {});
|
|
@@ -263,6 +284,27 @@ export function getAnimationEnabled(animation: StackAnimationName | undefined) {
|
|
|
263
284
|
return getDefaultAnimation(animation) !== 'none';
|
|
264
285
|
}
|
|
265
286
|
|
|
287
|
+
const getAllRoutes = (
|
|
288
|
+
routes: Route<string>[],
|
|
289
|
+
state: StackNavigationState<ParamListBase>
|
|
290
|
+
) => {
|
|
291
|
+
const routeKeys = new Set(routes.map((route) => route.key));
|
|
292
|
+
const inactiveRoutes = state.routes.slice(state.index + 1);
|
|
293
|
+
|
|
294
|
+
// If a route is moved from active routes to inactive routes,
|
|
295
|
+
// It can still be in the local copy of `routes` until the animation ends
|
|
296
|
+
// So we need to deduplicate the routes to avoid rendering the same route twice
|
|
297
|
+
return [
|
|
298
|
+
...routes,
|
|
299
|
+
...inactiveRoutes.filter((route) => !routeKeys.has(route.key)),
|
|
300
|
+
];
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const isInactiveRoute = (route: Route<string>, routes: Route<string>[]) =>
|
|
304
|
+
// `routes` contains active routes and routes animating during transitions.
|
|
305
|
+
// Any route added by `getAllRoutes` that's missing from this list is inactive.
|
|
306
|
+
!routes.some((currentRoute) => currentRoute.key === route.key);
|
|
307
|
+
|
|
266
308
|
export class CardStack extends React.Component<Props, State> {
|
|
267
309
|
static getDerivedStateFromProps(
|
|
268
310
|
props: Props,
|
|
@@ -275,10 +317,9 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
275
317
|
return null;
|
|
276
318
|
}
|
|
277
319
|
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
].reduce<GestureValues>((acc, curr) => {
|
|
320
|
+
const allRoutes = getAllRoutes(props.routes, props.state);
|
|
321
|
+
|
|
322
|
+
const gestures = allRoutes.reduce<GestureValues>((acc, curr) => {
|
|
282
323
|
const descriptor = props.descriptors[curr.key];
|
|
283
324
|
const { animation } = descriptor?.options || {};
|
|
284
325
|
|
|
@@ -287,7 +328,7 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
287
328
|
new Animated.Value(
|
|
288
329
|
(props.openingRouteKeys.includes(curr.key) &&
|
|
289
330
|
getAnimationEnabled(animation)) ||
|
|
290
|
-
|
|
331
|
+
isInactiveRoute(curr, props.routes)
|
|
291
332
|
? getDistanceFromOptions(
|
|
292
333
|
state.layout,
|
|
293
334
|
descriptor?.options,
|
|
@@ -299,254 +340,179 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
299
340
|
return acc;
|
|
300
341
|
}, {});
|
|
301
342
|
|
|
302
|
-
const modalRouteKeys = getModalRouteKeys(
|
|
303
|
-
[...props.routes, ...props.state.preloadedRoutes],
|
|
304
|
-
props.descriptors
|
|
305
|
-
);
|
|
343
|
+
const modalRouteKeys = getModalRouteKeys(allRoutes, props.descriptors);
|
|
306
344
|
|
|
307
|
-
const scenes =
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const nextRoute = isPreloaded ? undefined : self[index + 1];
|
|
313
|
-
|
|
314
|
-
const oldScene = state.scenes[index];
|
|
315
|
-
|
|
316
|
-
const currentGesture = gestures[route.key];
|
|
317
|
-
const previousGesture = previousRoute
|
|
318
|
-
? gestures[previousRoute.key]
|
|
319
|
-
: undefined;
|
|
320
|
-
const nextGesture = nextRoute ? gestures[nextRoute.key] : undefined;
|
|
321
|
-
|
|
322
|
-
const descriptor =
|
|
323
|
-
props.descriptors[route.key] ||
|
|
324
|
-
state.descriptors[route.key] ||
|
|
325
|
-
(oldScene ? oldScene.descriptor : FALLBACK_DESCRIPTOR);
|
|
326
|
-
|
|
327
|
-
const nextOptions =
|
|
328
|
-
nextRoute &&
|
|
329
|
-
(
|
|
330
|
-
props.descriptors[nextRoute?.key] ||
|
|
331
|
-
state.descriptors[nextRoute?.key]
|
|
332
|
-
)?.options;
|
|
333
|
-
|
|
334
|
-
const previousOptions =
|
|
335
|
-
previousRoute &&
|
|
336
|
-
(
|
|
337
|
-
props.descriptors[previousRoute?.key] ||
|
|
338
|
-
state.descriptors[previousRoute?.key]
|
|
339
|
-
)?.options;
|
|
340
|
-
|
|
341
|
-
// When a screen is not the last, it should use next screen's transition config
|
|
342
|
-
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
|
343
|
-
// For example combining a slide and a modal transition would look wrong otherwise
|
|
344
|
-
// With this approach, combining different transition styles in the same navigator mostly looks right
|
|
345
|
-
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
|
346
|
-
// but the majority of the transitions look alright
|
|
347
|
-
const optionsForTransitionConfig =
|
|
348
|
-
index !== self.length - 1 &&
|
|
349
|
-
nextOptions &&
|
|
350
|
-
nextOptions?.presentation !== 'transparentModal'
|
|
351
|
-
? nextOptions
|
|
352
|
-
: descriptor.options;
|
|
353
|
-
|
|
354
|
-
// Assume modal if there are already modal screens in the stack
|
|
355
|
-
// or current screen is a modal when no presentation is specified
|
|
356
|
-
const isModal = modalRouteKeys.includes(route.key);
|
|
357
|
-
|
|
358
|
-
const animation = getDefaultAnimation(
|
|
359
|
-
optionsForTransitionConfig.animation
|
|
360
|
-
);
|
|
345
|
+
const scenes = allRoutes.map((route, index, self) => {
|
|
346
|
+
// For preloaded or retained screens, we don't care about the previous and the next screen
|
|
347
|
+
const isInactive = isInactiveRoute(route, props.routes);
|
|
348
|
+
const previousRoute = isInactive ? undefined : self[index - 1];
|
|
349
|
+
const nextRoute = isInactive ? undefined : self[index + 1];
|
|
361
350
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
351
|
+
const oldScene = state.scenes[index];
|
|
352
|
+
|
|
353
|
+
const currentGesture = gestures[route.key];
|
|
354
|
+
|
|
355
|
+
if (currentGesture == null) {
|
|
356
|
+
throw new Error(`Couldn't find a gesture for route '${route.key}'.`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const previousGesture = previousRoute
|
|
360
|
+
? gestures[previousRoute.key]
|
|
361
|
+
: undefined;
|
|
362
|
+
const nextGesture = nextRoute ? gestures[nextRoute.key] : undefined;
|
|
363
|
+
|
|
364
|
+
const descriptor =
|
|
365
|
+
props.descriptors[route.key] ??
|
|
366
|
+
state.descriptors[route.key] ??
|
|
367
|
+
oldScene?.descriptor;
|
|
368
|
+
|
|
369
|
+
if (descriptor == null) {
|
|
370
|
+
throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const nextOptions = nextRoute
|
|
374
|
+
? (props.descriptors[nextRoute.key] ?? state.descriptors[nextRoute.key])
|
|
375
|
+
?.options
|
|
376
|
+
: undefined;
|
|
377
|
+
|
|
378
|
+
const previousOptions = previousRoute
|
|
379
|
+
? (
|
|
380
|
+
props.descriptors[previousRoute.key] ??
|
|
381
|
+
state.descriptors[previousRoute.key]
|
|
382
|
+
)?.options
|
|
383
|
+
: undefined;
|
|
384
|
+
|
|
385
|
+
// When a screen is not the last, it should use next screen's transition config
|
|
386
|
+
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
|
387
|
+
// For example combining a slide and a modal transition would look wrong otherwise
|
|
388
|
+
// With this approach, combining different transition styles in the same navigator mostly looks right
|
|
389
|
+
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
|
390
|
+
// but the majority of the transitions look alright
|
|
391
|
+
const optionsForTransitionConfig =
|
|
392
|
+
index !== self.length - 1 &&
|
|
393
|
+
nextOptions &&
|
|
394
|
+
nextOptions?.presentation !== 'transparentModal'
|
|
395
|
+
? nextOptions
|
|
396
|
+
: descriptor.options;
|
|
397
|
+
|
|
398
|
+
// Assume modal if there are already modal screens in the stack
|
|
399
|
+
// or current screen is a modal when no presentation is specified
|
|
400
|
+
const isModal = modalRouteKeys.includes(route.key);
|
|
401
|
+
|
|
402
|
+
const animation = getDefaultAnimation(
|
|
403
|
+
optionsForTransitionConfig.animation
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const isAnimationEnabled = getAnimationEnabled(animation);
|
|
407
|
+
|
|
408
|
+
const transitionPreset =
|
|
409
|
+
animation !== 'default'
|
|
410
|
+
? NAMED_TRANSITIONS_PRESETS[animation]
|
|
411
|
+
: optionsForTransitionConfig.presentation === 'transparentModal'
|
|
412
|
+
? ModalFadeTransition
|
|
413
|
+
: optionsForTransitionConfig.presentation === 'modal' || isModal
|
|
414
|
+
? ModalTransition
|
|
415
|
+
: DefaultTransition;
|
|
416
|
+
|
|
417
|
+
const {
|
|
418
|
+
gestureEnabled = Platform.OS === 'ios' && isAnimationEnabled,
|
|
419
|
+
gestureDirection = transitionPreset.gestureDirection,
|
|
420
|
+
transitionSpec = transitionPreset.transitionSpec,
|
|
421
|
+
cardStyleInterpolator = isAnimationEnabled
|
|
422
|
+
? transitionPreset.cardStyleInterpolator
|
|
423
|
+
: forNoAnimationCard,
|
|
424
|
+
headerStyleInterpolator = transitionPreset.headerStyleInterpolator,
|
|
425
|
+
cardOverlayEnabled = (Platform.OS !== 'ios' &&
|
|
426
|
+
optionsForTransitionConfig.presentation !== 'transparentModal') ||
|
|
427
|
+
getIsModalPresentation(cardStyleInterpolator),
|
|
428
|
+
} = optionsForTransitionConfig;
|
|
429
|
+
|
|
430
|
+
const headerMode: StackHeaderMode =
|
|
431
|
+
descriptor.options.headerMode ??
|
|
432
|
+
(!(
|
|
433
|
+
optionsForTransitionConfig.presentation === 'modal' ||
|
|
434
|
+
optionsForTransitionConfig.presentation === 'transparentModal' ||
|
|
435
|
+
nextOptions?.presentation === 'modal' ||
|
|
436
|
+
nextOptions?.presentation === 'transparentModal' ||
|
|
437
|
+
getIsModalPresentation(cardStyleInterpolator)
|
|
438
|
+
) &&
|
|
439
|
+
Platform.OS === 'ios' &&
|
|
440
|
+
descriptor.options.header === undefined
|
|
441
|
+
? 'float'
|
|
442
|
+
: 'screen');
|
|
443
|
+
|
|
444
|
+
const isRTL = props.direction === 'rtl';
|
|
445
|
+
|
|
446
|
+
const scene = {
|
|
447
|
+
route,
|
|
448
|
+
descriptor: {
|
|
449
|
+
...descriptor,
|
|
450
|
+
options: {
|
|
451
|
+
...descriptor.options,
|
|
452
|
+
animation,
|
|
453
|
+
cardOverlayEnabled,
|
|
454
|
+
cardStyleInterpolator,
|
|
455
|
+
gestureDirection,
|
|
456
|
+
gestureEnabled,
|
|
457
|
+
headerStyleInterpolator,
|
|
458
|
+
transitionSpec,
|
|
459
|
+
headerMode,
|
|
417
460
|
},
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
nextGesture,
|
|
429
|
-
state.layout,
|
|
430
|
-
nextOptions,
|
|
431
|
-
isRTL
|
|
432
|
-
)
|
|
433
|
-
: undefined,
|
|
434
|
-
previous: previousGesture
|
|
461
|
+
},
|
|
462
|
+
progress: {
|
|
463
|
+
current: getProgressFromGesture(
|
|
464
|
+
currentGesture,
|
|
465
|
+
state.layout,
|
|
466
|
+
descriptor.options,
|
|
467
|
+
isRTL
|
|
468
|
+
),
|
|
469
|
+
next:
|
|
470
|
+
nextGesture && nextOptions?.presentation !== 'transparentModal'
|
|
435
471
|
? getProgressFromGesture(
|
|
436
|
-
|
|
472
|
+
nextGesture,
|
|
437
473
|
state.layout,
|
|
438
|
-
|
|
474
|
+
nextOptions,
|
|
439
475
|
isRTL
|
|
440
476
|
)
|
|
441
477
|
: undefined,
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return oldScene;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return scene;
|
|
465
|
-
}
|
|
466
|
-
);
|
|
478
|
+
previous: previousGesture
|
|
479
|
+
? getProgressFromGesture(
|
|
480
|
+
previousGesture,
|
|
481
|
+
state.layout,
|
|
482
|
+
previousOptions,
|
|
483
|
+
isRTL
|
|
484
|
+
)
|
|
485
|
+
: undefined,
|
|
486
|
+
},
|
|
487
|
+
__memo: [
|
|
488
|
+
state.layout,
|
|
489
|
+
descriptor,
|
|
490
|
+
nextOptions,
|
|
491
|
+
previousOptions,
|
|
492
|
+
currentGesture,
|
|
493
|
+
nextGesture,
|
|
494
|
+
previousGesture,
|
|
495
|
+
],
|
|
496
|
+
};
|
|
467
497
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const {
|
|
477
|
-
// By default, we don't want to detach the previous screen of the active one for modals
|
|
478
|
-
detachPreviousScreen = options.presentation === 'transparentModal'
|
|
479
|
-
? false
|
|
480
|
-
: getIsModalPresentation(options.cardStyleInterpolator)
|
|
481
|
-
? i !==
|
|
482
|
-
findLastIndex(scenes, (scene) => {
|
|
483
|
-
const { cardStyleInterpolator } = scene.descriptor.options;
|
|
484
|
-
|
|
485
|
-
return (
|
|
486
|
-
cardStyleInterpolator === forModalPresentationIOS ||
|
|
487
|
-
cardStyleInterpolator?.name === 'forModalPresentationIOS'
|
|
488
|
-
);
|
|
489
|
-
})
|
|
490
|
-
: true,
|
|
491
|
-
} = options;
|
|
492
|
-
|
|
493
|
-
if (detachPreviousScreen === false) {
|
|
494
|
-
activeScreensLimit++;
|
|
495
|
-
} else {
|
|
496
|
-
// Check at least last 2 screens before stopping
|
|
497
|
-
// This will make sure that screen isn't detached when another screen is animating on top of the transparent one
|
|
498
|
-
// e.g. opaque -> transparent -> opaque
|
|
499
|
-
if (i <= props.routes.length - 2) {
|
|
500
|
-
break;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
498
|
+
if (
|
|
499
|
+
oldScene &&
|
|
500
|
+
scene.__memo.every((it, i) => {
|
|
501
|
+
// @ts-expect-error: we haven't added __memo to the annotation to prevent usage elsewhere
|
|
502
|
+
return oldScene.__memo[i] === it;
|
|
503
|
+
})
|
|
504
|
+
) {
|
|
505
|
+
return oldScene;
|
|
503
506
|
}
|
|
504
507
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
// 0 - inactive, the screen is detached
|
|
508
|
-
// 1 - transitioning or below the top screen, the screen is mounted but interaction is disabled
|
|
509
|
-
// 2 - on top of the stack, the screen is mounted and interaction is enabled
|
|
510
|
-
let activityState:
|
|
511
|
-
| Animated.AnimatedInterpolation<0 | 1 | 2>
|
|
512
|
-
| 0
|
|
513
|
-
| 1
|
|
514
|
-
| 2;
|
|
515
|
-
|
|
516
|
-
const lastActiveState = state.activeStates[index];
|
|
517
|
-
const activeAfterTransition = index >= self.length - activeScreensLimit;
|
|
518
|
-
|
|
519
|
-
if (lastActiveState === STATE_INACTIVE && !activeAfterTransition) {
|
|
520
|
-
// screen was inactive before and it will still be inactive after the transition
|
|
521
|
-
activityState = STATE_INACTIVE;
|
|
522
|
-
} else {
|
|
523
|
-
const sceneForActivity = scenes[self.length - 1];
|
|
524
|
-
const outputValue =
|
|
525
|
-
index === self.length - 1
|
|
526
|
-
? STATE_ON_TOP // the screen is on top after the transition
|
|
527
|
-
: activeAfterTransition
|
|
528
|
-
? STATE_TRANSITIONING_OR_BELOW_TOP // the screen should stay active after the transition, it is not on top but is in activeLimit
|
|
529
|
-
: STATE_INACTIVE; // the screen should be active only during the transition, it is at the edge of activeLimit
|
|
530
|
-
|
|
531
|
-
activityState = sceneForActivity
|
|
532
|
-
? sceneForActivity.progress.current.interpolate({
|
|
533
|
-
inputRange: [0, 1 - EPSILON, 1],
|
|
534
|
-
outputRange: [1, 1, outputValue],
|
|
535
|
-
extrapolate: 'clamp',
|
|
536
|
-
})
|
|
537
|
-
: STATE_TRANSITIONING_OR_BELOW_TOP;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
return activityState;
|
|
541
|
-
});
|
|
542
|
-
}
|
|
508
|
+
return scene;
|
|
509
|
+
});
|
|
543
510
|
|
|
544
511
|
return {
|
|
545
512
|
routes: props.routes,
|
|
546
513
|
scenes,
|
|
547
514
|
gestures,
|
|
548
515
|
descriptors: props.descriptors,
|
|
549
|
-
activeStates,
|
|
550
516
|
headerHeights: getHeaderHeights(
|
|
551
517
|
scenes,
|
|
552
518
|
props.insets,
|
|
@@ -567,7 +533,6 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
567
533
|
gestures: {},
|
|
568
534
|
layout: SafeAreaProviderCompat.initialMetrics.frame,
|
|
569
535
|
descriptors: this.props.descriptors,
|
|
570
|
-
activeStates: [],
|
|
571
536
|
// Used when card's header is null and mode is float to make transition
|
|
572
537
|
// between screens with headers and those without headers smooth.
|
|
573
538
|
// This is not a great heuristic here. We don't know synchronously
|
|
@@ -611,14 +576,17 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
611
576
|
this.setState(({ headerHeights }) => {
|
|
612
577
|
const previousHeight = headerHeights[route.key];
|
|
613
578
|
|
|
614
|
-
if (previousHeight === height) {
|
|
579
|
+
if (previousHeight?.value === height) {
|
|
615
580
|
return null;
|
|
616
581
|
}
|
|
617
582
|
|
|
618
583
|
return {
|
|
619
584
|
headerHeights: {
|
|
620
585
|
...headerHeights,
|
|
621
|
-
[route.key]:
|
|
586
|
+
[route.key]: {
|
|
587
|
+
measured: true,
|
|
588
|
+
value: height,
|
|
589
|
+
},
|
|
622
590
|
},
|
|
623
591
|
};
|
|
624
592
|
});
|
|
@@ -627,7 +595,13 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
627
595
|
private getFocusedRoute = () => {
|
|
628
596
|
const { state } = this.props;
|
|
629
597
|
|
|
630
|
-
|
|
598
|
+
const route = state.routes[state.index];
|
|
599
|
+
|
|
600
|
+
if (route == null) {
|
|
601
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return route;
|
|
631
605
|
};
|
|
632
606
|
|
|
633
607
|
private getPreviousScene = ({ route }: { route: Route<string> }) => {
|
|
@@ -654,27 +628,37 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
654
628
|
routes,
|
|
655
629
|
openingRouteKeys,
|
|
656
630
|
closingRouteKeys,
|
|
631
|
+
replacingRouteKeys,
|
|
657
632
|
onOpenRoute,
|
|
658
633
|
onCloseRoute,
|
|
634
|
+
onGestureStart,
|
|
635
|
+
onGestureEnd,
|
|
636
|
+
onGestureCancel,
|
|
659
637
|
renderHeader,
|
|
660
638
|
isParentHeaderShown,
|
|
661
639
|
isParentModal,
|
|
662
640
|
onTransitionStart,
|
|
663
641
|
onTransitionEnd,
|
|
664
|
-
onGestureStart,
|
|
665
|
-
onGestureEnd,
|
|
666
|
-
onGestureCancel,
|
|
667
|
-
detachInactiveScreens = Platform.OS === 'web' ||
|
|
668
|
-
Platform.OS === 'android' ||
|
|
669
|
-
Platform.OS === 'ios',
|
|
670
642
|
} = this.props;
|
|
671
643
|
|
|
672
|
-
const { scenes, layout, gestures,
|
|
673
|
-
|
|
644
|
+
const { scenes, layout, gestures, headerHeights } = this.state;
|
|
645
|
+
|
|
646
|
+
const allRoutes = getAllRoutes(routes, state);
|
|
674
647
|
|
|
675
648
|
const focusedRoute = state.routes[state.index];
|
|
676
649
|
|
|
677
|
-
|
|
650
|
+
if (focusedRoute == null) {
|
|
651
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Render only two top-most active headers as a workaround for
|
|
655
|
+
// https://github.com/react-navigation/react-navigation/issues/12456.
|
|
656
|
+
// If the header is persisted, it might be placed incorrectly when navigating back
|
|
657
|
+
const activeHeaderScenes = scenes
|
|
658
|
+
.filter((scene) => !isInactiveRoute(scene.route, routes))
|
|
659
|
+
.slice(-2);
|
|
660
|
+
|
|
661
|
+
const isFloatHeaderAbsolute = activeHeaderScenes.some((scene) => {
|
|
678
662
|
const options = scene.descriptor.options ?? {};
|
|
679
663
|
const { headerMode, headerTransparent, headerShown = true } = options;
|
|
680
664
|
|
|
@@ -693,13 +677,16 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
693
677
|
scenes.findLast((scene) => scene.route.key === focusedRoute.key)
|
|
694
678
|
?.descriptor.options.headerMode === 'float';
|
|
695
679
|
|
|
680
|
+
const focusedHeaderHeight = headerHeights[focusedRoute.key];
|
|
681
|
+
|
|
696
682
|
return (
|
|
697
683
|
<View style={styles.container}>
|
|
698
684
|
{renderHeader({
|
|
699
685
|
mode: 'float',
|
|
700
|
-
scenes,
|
|
686
|
+
scenes: activeHeaderScenes,
|
|
701
687
|
getPreviousScene: this.getPreviousScene,
|
|
702
688
|
getFocusedRoute: this.getFocusedRoute,
|
|
689
|
+
contentHeight: headerHeights[focusedRoute.key],
|
|
703
690
|
onContentHeightChange: this.handleHeaderLayout,
|
|
704
691
|
style: [
|
|
705
692
|
isFloatHeaderAbsolute && [
|
|
@@ -707,39 +694,36 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
707
694
|
// The header title can't be found with maestro without this
|
|
708
695
|
// In some cases, header buttons may not be touchable as well
|
|
709
696
|
isFloatHeaderFocused && {
|
|
710
|
-
height:
|
|
697
|
+
height: focusedHeaderHeight?.value,
|
|
711
698
|
},
|
|
712
699
|
],
|
|
713
700
|
],
|
|
714
701
|
})}
|
|
715
|
-
<
|
|
716
|
-
|
|
717
|
-
style={styles.container}
|
|
718
|
-
onLayout={this.handleLayout}
|
|
719
|
-
>
|
|
720
|
-
{[...routes, ...state.preloadedRoutes].map((route, index) => {
|
|
702
|
+
<View style={styles.container} onLayout={this.handleLayout}>
|
|
703
|
+
{allRoutes.map((route, index, self) => {
|
|
721
704
|
const focused = focusedRoute.key === route.key;
|
|
722
705
|
const gesture = gestures[route.key];
|
|
723
706
|
const scene = scenes[index];
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
state.preloadedRoutes.includes(route) && !routes.includes(route);
|
|
730
|
-
if (
|
|
731
|
-
state.preloadedRoutes.includes(route) &&
|
|
732
|
-
routes.includes(route) &&
|
|
733
|
-
index >= routes.length
|
|
734
|
-
) {
|
|
735
|
-
return null;
|
|
707
|
+
|
|
708
|
+
if (gesture == null) {
|
|
709
|
+
throw new Error(
|
|
710
|
+
`Couldn't find a gesture for route '${route.key}'.`
|
|
711
|
+
);
|
|
736
712
|
}
|
|
737
713
|
|
|
714
|
+
if (scene == null) {
|
|
715
|
+
throw new Error(`Couldn't find a scene at index ${index}.`);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const nextScene = scenes[index + 1];
|
|
719
|
+
const nextRoute = self[index + 1];
|
|
720
|
+
|
|
738
721
|
const {
|
|
722
|
+
inactiveBehavior = 'pause',
|
|
723
|
+
animation,
|
|
739
724
|
headerShown = true,
|
|
725
|
+
headerMode,
|
|
740
726
|
headerTransparent,
|
|
741
|
-
freezeOnBlur,
|
|
742
|
-
autoHideHomeIndicator,
|
|
743
727
|
} = scene.descriptor.options;
|
|
744
728
|
|
|
745
729
|
const safeAreaInsetTop = insets.top;
|
|
@@ -748,7 +732,9 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
748
732
|
const safeAreaInsetLeft = insets.left;
|
|
749
733
|
|
|
750
734
|
const headerHeight =
|
|
751
|
-
headerShown !== false
|
|
735
|
+
headerShown !== false
|
|
736
|
+
? headerHeights[route.key]
|
|
737
|
+
: HIDDEN_HEADER_HEIGHT;
|
|
752
738
|
|
|
753
739
|
// Start from current card and count backwards the number of cards with same interpolation
|
|
754
740
|
const interpolationIndex = getInterpolationIndex(scenes, index);
|
|
@@ -758,33 +744,107 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
758
744
|
isParentModal
|
|
759
745
|
);
|
|
760
746
|
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
'
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
747
|
+
const isAnimationEnabled =
|
|
748
|
+
getAnimationEnabled(animation) ||
|
|
749
|
+
// Also check next screen's animation,
|
|
750
|
+
// As it will result in both screens being visible
|
|
751
|
+
(nextScene
|
|
752
|
+
? getAnimationEnabled(nextScene.descriptor.options.animation)
|
|
753
|
+
: false);
|
|
768
754
|
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
755
|
+
const isNextScreenTransparent =
|
|
756
|
+
nextScene?.descriptor.options.presentation === 'transparentModal';
|
|
757
|
+
|
|
758
|
+
const isRemoving =
|
|
759
|
+
replacingRouteKeys.includes(route.key) ||
|
|
760
|
+
closingRouteKeys.includes(route.key);
|
|
761
|
+
|
|
762
|
+
const isFocusing =
|
|
763
|
+
openingRouteKeys.includes(route.key) ||
|
|
764
|
+
(nextRoute != null &&
|
|
765
|
+
[...closingRouteKeys, ...replacingRouteKeys].includes(
|
|
766
|
+
nextRoute.key
|
|
767
|
+
));
|
|
768
|
+
|
|
769
|
+
const isInactive = isInactiveRoute(route, this.props.routes);
|
|
770
|
+
const isRetained = state.retainedRouteKeys?.includes(route.key);
|
|
771
|
+
const isPreloaded = isInactive && !isRetained;
|
|
772
|
+
|
|
773
|
+
const isTopmost = index === routes.length - 1;
|
|
774
|
+
const isBeforeLast = index === routes.length - 2;
|
|
775
|
+
|
|
776
|
+
// Keep animating and the last two rendered routes visible for smoother transitions
|
|
777
|
+
const isVisible =
|
|
778
|
+
focused ||
|
|
779
|
+
isNextScreenTransparent ||
|
|
780
|
+
// React native destroys the view when hidden
|
|
781
|
+
// So we keep retained screens visible
|
|
782
|
+
// This makes sure any videos being played don't stop etc.
|
|
783
|
+
isRetained ||
|
|
784
|
+
// We only need to keep other screens visible when animation is enabled
|
|
785
|
+
(isAnimationEnabled &&
|
|
786
|
+
(isFocusing ||
|
|
787
|
+
isRemoving ||
|
|
788
|
+
// Preloaded screens should stay mounted, but remain hidden until focused
|
|
789
|
+
(!isPreloaded && index >= routes.length - 2)));
|
|
790
|
+
|
|
791
|
+
const activityMode = // Render focused and animating screens normally
|
|
792
|
+
focused || isFocusing
|
|
793
|
+
? 'normal'
|
|
794
|
+
: inactiveBehavior === 'none' ||
|
|
795
|
+
// Unpause preloaded or retained screens so updates are visible
|
|
796
|
+
// Handle retained explicitly as isInactive won't be updated until animation end
|
|
797
|
+
isInactive ||
|
|
798
|
+
isRetained ||
|
|
799
|
+
// Keep the screen before transparent screen active
|
|
800
|
+
// This lets the screen under the transparent screen update and animate
|
|
801
|
+
isNextScreenTransparent ||
|
|
802
|
+
// Keep the screen before last screen active
|
|
803
|
+
// Otherwise it breaks animation when going back
|
|
804
|
+
isBeforeLast
|
|
805
|
+
? 'inert'
|
|
806
|
+
: inactiveBehavior === 'unmount' &&
|
|
807
|
+
// Don't unmount screens that needs to stay visible
|
|
808
|
+
!isVisible &&
|
|
809
|
+
// Don't unmount screens with nested navigators
|
|
810
|
+
// So we don't lose their state
|
|
811
|
+
!('state' in route && route.state)
|
|
812
|
+
? 'unmounted'
|
|
813
|
+
: 'paused';
|
|
814
|
+
|
|
815
|
+
if (activityMode === 'unmounted') {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
772
818
|
|
|
773
819
|
return (
|
|
774
|
-
<
|
|
820
|
+
<ActivityView
|
|
775
821
|
key={route.key}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
822
|
+
mode={activityMode}
|
|
823
|
+
visible={isVisible}
|
|
824
|
+
style={{
|
|
825
|
+
...StyleSheet.absoluteFill,
|
|
826
|
+
// Avoid unfocused larger pages increasing scroll area
|
|
827
|
+
// e.g. when a smaller screen is pushed over a larger one on web
|
|
828
|
+
overflow: isTopmost ? undefined : 'hidden',
|
|
829
|
+
marginTop:
|
|
830
|
+
headerTransparent || headerMode === 'screen'
|
|
831
|
+
? 0
|
|
832
|
+
: // Add offset for non-transparent floating header
|
|
833
|
+
isFloatHeaderAbsolute
|
|
834
|
+
? // If header is absolute, offset the card by the header height
|
|
835
|
+
headerHeight?.value
|
|
836
|
+
: // Otherwise, the in-flow float header already takes the
|
|
837
|
+
// focused header's space, so offset by the difference
|
|
838
|
+
headerHeight && focusedHeaderHeight
|
|
839
|
+
? headerHeight.value - focusedHeaderHeight.value
|
|
840
|
+
: 0,
|
|
841
|
+
}}
|
|
782
842
|
>
|
|
783
843
|
<CardContainer
|
|
784
844
|
index={index}
|
|
785
845
|
interpolationIndex={interpolationIndex}
|
|
786
846
|
modal={isModal}
|
|
787
|
-
active={
|
|
847
|
+
active={isTopmost}
|
|
788
848
|
focused={focused}
|
|
789
849
|
opening={openingRouteKeys.includes(route.key)}
|
|
790
850
|
closing={closingRouteKeys.includes(route.key)}
|
|
@@ -803,22 +863,19 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
803
863
|
onHeaderHeightChange={this.handleHeaderLayout}
|
|
804
864
|
getPreviousScene={this.getPreviousScene}
|
|
805
865
|
getFocusedRoute={this.getFocusedRoute}
|
|
806
|
-
hasAbsoluteFloatHeader={
|
|
807
|
-
isFloatHeaderAbsolute && !headerTransparent
|
|
808
|
-
}
|
|
809
866
|
renderHeader={renderHeader}
|
|
810
867
|
onOpenRoute={onOpenRoute}
|
|
811
868
|
onCloseRoute={onCloseRoute}
|
|
812
869
|
onTransitionStart={onTransitionStart}
|
|
813
870
|
onTransitionEnd={onTransitionEnd}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
</
|
|
871
|
+
preloaded={isInactive}
|
|
872
|
+
>
|
|
873
|
+
{scene.descriptor.render()}
|
|
874
|
+
</CardContainer>
|
|
875
|
+
</ActivityView>
|
|
819
876
|
);
|
|
820
877
|
})}
|
|
821
|
-
</
|
|
878
|
+
</View>
|
|
822
879
|
</View>
|
|
823
880
|
);
|
|
824
881
|
}
|