@react-navigation/stack 8.0.0-alpha.24 → 8.0.0-alpha.26
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.map +1 -1
- package/lib/module/navigators/createStackNavigator.js +3 -7
- package/lib/module/navigators/createStackNavigator.js.map +1 -1
- package/lib/module/views/Stack/CardContainer.js +5 -1
- package/lib/module/views/Stack/CardContainer.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +39 -24
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/module/views/Stack/StackView.js +31 -26
- package/lib/module/views/Stack/StackView.js.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createStackNavigator.d.ts +8 -14
- package/lib/typescript/src/navigators/createStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +0 -6
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContainer.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -1
- package/package.json +5 -7
- package/src/index.tsx +1 -0
- package/src/navigators/createStackNavigator.tsx +9 -40
- package/src/types.tsx +0 -6
- package/src/views/Stack/CardContainer.tsx +10 -1
- package/src/views/Stack/CardStack.tsx +200 -185
- package/src/views/Stack/StackView.tsx +41 -35
|
@@ -258,6 +258,27 @@ export function getAnimationEnabled(animation: StackAnimationName | undefined) {
|
|
|
258
258
|
return getDefaultAnimation(animation) !== 'none';
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
const getAllRoutes = (
|
|
262
|
+
routes: Route<string>[],
|
|
263
|
+
state: StackNavigationState<ParamListBase>
|
|
264
|
+
) => {
|
|
265
|
+
const routeKeys = new Set(routes.map((route) => route.key));
|
|
266
|
+
const inactiveRoutes = state.routes.slice(state.index + 1);
|
|
267
|
+
|
|
268
|
+
// If a route is moved from active routes to inactive routes,
|
|
269
|
+
// It can still be in the local copy of `routes` until the animation ends
|
|
270
|
+
// So we need to deduplicate the routes to avoid rendering the same route twice
|
|
271
|
+
return [
|
|
272
|
+
...routes,
|
|
273
|
+
...inactiveRoutes.filter((route) => !routeKeys.has(route.key)),
|
|
274
|
+
];
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const isInactiveRoute = (route: Route<string>, routes: Route<string>[]) =>
|
|
278
|
+
// `routes` contains active routes and routes animating during transitions.
|
|
279
|
+
// Any route added by `getAllRoutes` that's missing from this list is inactive.
|
|
280
|
+
!routes.some((currentRoute) => currentRoute.key === route.key);
|
|
281
|
+
|
|
261
282
|
export class CardStack extends React.Component<Props, State> {
|
|
262
283
|
static getDerivedStateFromProps(
|
|
263
284
|
props: Props,
|
|
@@ -270,10 +291,9 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
270
291
|
return null;
|
|
271
292
|
}
|
|
272
293
|
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
].reduce<GestureValues>((acc, curr) => {
|
|
294
|
+
const allRoutes = getAllRoutes(props.routes, props.state);
|
|
295
|
+
|
|
296
|
+
const gestures = allRoutes.reduce<GestureValues>((acc, curr) => {
|
|
277
297
|
const descriptor = props.descriptors[curr.key];
|
|
278
298
|
const { animation } = descriptor?.options || {};
|
|
279
299
|
|
|
@@ -282,7 +302,7 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
282
302
|
new Animated.Value(
|
|
283
303
|
(props.openingRouteKeys.includes(curr.key) &&
|
|
284
304
|
getAnimationEnabled(animation)) ||
|
|
285
|
-
props.
|
|
305
|
+
isInactiveRoute(curr, props.routes)
|
|
286
306
|
? getDistanceFromOptions(
|
|
287
307
|
state.layout,
|
|
288
308
|
descriptor?.options,
|
|
@@ -294,171 +314,164 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
294
314
|
return acc;
|
|
295
315
|
}, {});
|
|
296
316
|
|
|
297
|
-
const modalRouteKeys = getModalRouteKeys(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
// or current screen is a modal when no presentation is specified
|
|
351
|
-
const isModal = modalRouteKeys.includes(route.key);
|
|
352
|
-
|
|
353
|
-
const animation = getDefaultAnimation(
|
|
354
|
-
optionsForTransitionConfig.animation
|
|
355
|
-
);
|
|
317
|
+
const modalRouteKeys = getModalRouteKeys(allRoutes, props.descriptors);
|
|
318
|
+
|
|
319
|
+
const scenes = allRoutes.map((route, index, self) => {
|
|
320
|
+
// For preloaded or retained screens, we don't care about the previous and the next screen
|
|
321
|
+
const isInactive = isInactiveRoute(route, props.routes);
|
|
322
|
+
const previousRoute = isInactive ? undefined : self[index - 1];
|
|
323
|
+
const nextRoute = isInactive ? undefined : self[index + 1];
|
|
324
|
+
|
|
325
|
+
const oldScene = state.scenes[index];
|
|
326
|
+
|
|
327
|
+
const currentGesture = gestures[route.key];
|
|
328
|
+
const previousGesture = previousRoute
|
|
329
|
+
? gestures[previousRoute.key]
|
|
330
|
+
: undefined;
|
|
331
|
+
const nextGesture = nextRoute ? gestures[nextRoute.key] : undefined;
|
|
332
|
+
|
|
333
|
+
const descriptor =
|
|
334
|
+
props.descriptors[route.key] ||
|
|
335
|
+
state.descriptors[route.key] ||
|
|
336
|
+
(oldScene ? oldScene.descriptor : FALLBACK_DESCRIPTOR);
|
|
337
|
+
|
|
338
|
+
const nextOptions =
|
|
339
|
+
nextRoute &&
|
|
340
|
+
(props.descriptors[nextRoute?.key] || state.descriptors[nextRoute?.key])
|
|
341
|
+
?.options;
|
|
342
|
+
|
|
343
|
+
const previousOptions =
|
|
344
|
+
previousRoute &&
|
|
345
|
+
(
|
|
346
|
+
props.descriptors[previousRoute?.key] ||
|
|
347
|
+
state.descriptors[previousRoute?.key]
|
|
348
|
+
)?.options;
|
|
349
|
+
|
|
350
|
+
// When a screen is not the last, it should use next screen's transition config
|
|
351
|
+
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
|
|
352
|
+
// For example combining a slide and a modal transition would look wrong otherwise
|
|
353
|
+
// With this approach, combining different transition styles in the same navigator mostly looks right
|
|
354
|
+
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
|
|
355
|
+
// but the majority of the transitions look alright
|
|
356
|
+
const optionsForTransitionConfig =
|
|
357
|
+
index !== self.length - 1 &&
|
|
358
|
+
nextOptions &&
|
|
359
|
+
nextOptions?.presentation !== 'transparentModal'
|
|
360
|
+
? nextOptions
|
|
361
|
+
: descriptor.options;
|
|
362
|
+
|
|
363
|
+
// Assume modal if there are already modal screens in the stack
|
|
364
|
+
// or current screen is a modal when no presentation is specified
|
|
365
|
+
const isModal = modalRouteKeys.includes(route.key);
|
|
366
|
+
|
|
367
|
+
const animation = getDefaultAnimation(
|
|
368
|
+
optionsForTransitionConfig.animation
|
|
369
|
+
);
|
|
356
370
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
},
|
|
371
|
+
const isAnimationEnabled = getAnimationEnabled(animation);
|
|
372
|
+
|
|
373
|
+
const transitionPreset =
|
|
374
|
+
animation !== 'default'
|
|
375
|
+
? NAMED_TRANSITIONS_PRESETS[animation]
|
|
376
|
+
: optionsForTransitionConfig.presentation === 'transparentModal'
|
|
377
|
+
? ModalFadeTransition
|
|
378
|
+
: optionsForTransitionConfig.presentation === 'modal' || isModal
|
|
379
|
+
? ModalTransition
|
|
380
|
+
: DefaultTransition;
|
|
381
|
+
|
|
382
|
+
const {
|
|
383
|
+
gestureEnabled = Platform.OS === 'ios' && isAnimationEnabled,
|
|
384
|
+
gestureDirection = transitionPreset.gestureDirection,
|
|
385
|
+
transitionSpec = transitionPreset.transitionSpec,
|
|
386
|
+
cardStyleInterpolator = isAnimationEnabled
|
|
387
|
+
? transitionPreset.cardStyleInterpolator
|
|
388
|
+
: forNoAnimationCard,
|
|
389
|
+
headerStyleInterpolator = transitionPreset.headerStyleInterpolator,
|
|
390
|
+
cardOverlayEnabled = (Platform.OS !== 'ios' &&
|
|
391
|
+
optionsForTransitionConfig.presentation !== 'transparentModal') ||
|
|
392
|
+
getIsModalPresentation(cardStyleInterpolator),
|
|
393
|
+
} = optionsForTransitionConfig;
|
|
394
|
+
|
|
395
|
+
const headerMode: StackHeaderMode =
|
|
396
|
+
descriptor.options.headerMode ??
|
|
397
|
+
(!(
|
|
398
|
+
optionsForTransitionConfig.presentation === 'modal' ||
|
|
399
|
+
optionsForTransitionConfig.presentation === 'transparentModal' ||
|
|
400
|
+
nextOptions?.presentation === 'modal' ||
|
|
401
|
+
nextOptions?.presentation === 'transparentModal' ||
|
|
402
|
+
getIsModalPresentation(cardStyleInterpolator)
|
|
403
|
+
) &&
|
|
404
|
+
Platform.OS === 'ios' &&
|
|
405
|
+
descriptor.options.header === undefined
|
|
406
|
+
? 'float'
|
|
407
|
+
: 'screen');
|
|
408
|
+
|
|
409
|
+
const isRTL = props.direction === 'rtl';
|
|
410
|
+
|
|
411
|
+
const scene = {
|
|
412
|
+
route,
|
|
413
|
+
descriptor: {
|
|
414
|
+
...descriptor,
|
|
415
|
+
options: {
|
|
416
|
+
...descriptor.options,
|
|
417
|
+
animation,
|
|
418
|
+
cardOverlayEnabled,
|
|
419
|
+
cardStyleInterpolator,
|
|
420
|
+
gestureDirection,
|
|
421
|
+
gestureEnabled,
|
|
422
|
+
headerStyleInterpolator,
|
|
423
|
+
transitionSpec,
|
|
424
|
+
headerMode,
|
|
412
425
|
},
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
nextGesture,
|
|
424
|
-
state.layout,
|
|
425
|
-
nextOptions,
|
|
426
|
-
isRTL
|
|
427
|
-
)
|
|
428
|
-
: undefined,
|
|
429
|
-
previous: previousGesture
|
|
426
|
+
},
|
|
427
|
+
progress: {
|
|
428
|
+
current: getProgressFromGesture(
|
|
429
|
+
currentGesture,
|
|
430
|
+
state.layout,
|
|
431
|
+
descriptor.options,
|
|
432
|
+
isRTL
|
|
433
|
+
),
|
|
434
|
+
next:
|
|
435
|
+
nextGesture && nextOptions?.presentation !== 'transparentModal'
|
|
430
436
|
? getProgressFromGesture(
|
|
431
|
-
|
|
437
|
+
nextGesture,
|
|
432
438
|
state.layout,
|
|
433
|
-
|
|
439
|
+
nextOptions,
|
|
434
440
|
isRTL
|
|
435
441
|
)
|
|
436
442
|
: undefined,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
443
|
+
previous: previousGesture
|
|
444
|
+
? getProgressFromGesture(
|
|
445
|
+
previousGesture,
|
|
446
|
+
state.layout,
|
|
447
|
+
previousOptions,
|
|
448
|
+
isRTL
|
|
449
|
+
)
|
|
450
|
+
: undefined,
|
|
451
|
+
},
|
|
452
|
+
__memo: [
|
|
453
|
+
state.layout,
|
|
454
|
+
descriptor,
|
|
455
|
+
nextOptions,
|
|
456
|
+
previousOptions,
|
|
457
|
+
currentGesture,
|
|
458
|
+
nextGesture,
|
|
459
|
+
previousGesture,
|
|
460
|
+
],
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
if (
|
|
464
|
+
oldScene &&
|
|
465
|
+
scene.__memo.every((it, i) => {
|
|
466
|
+
// @ts-expect-error: we haven't added __memo to the annotation to prevent usage elsewhere
|
|
467
|
+
return oldScene.__memo[i] === it;
|
|
468
|
+
})
|
|
469
|
+
) {
|
|
470
|
+
return oldScene;
|
|
460
471
|
}
|
|
461
|
-
|
|
472
|
+
|
|
473
|
+
return scene;
|
|
474
|
+
});
|
|
462
475
|
|
|
463
476
|
return {
|
|
464
477
|
routes: props.routes,
|
|
@@ -586,6 +599,8 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
586
599
|
|
|
587
600
|
const { scenes, layout, gestures, headerHeights } = this.state;
|
|
588
601
|
|
|
602
|
+
const allRoutes = getAllRoutes(routes, state);
|
|
603
|
+
|
|
589
604
|
const focusedRoute = state.routes[state.index];
|
|
590
605
|
|
|
591
606
|
const isFloatHeaderAbsolute = scenes.slice(-2).some((scene) => {
|
|
@@ -627,26 +642,15 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
627
642
|
],
|
|
628
643
|
})}
|
|
629
644
|
<View style={styles.container} onLayout={this.handleLayout}>
|
|
630
|
-
{
|
|
645
|
+
{allRoutes.map((route, index, self) => {
|
|
631
646
|
const focused = focusedRoute.key === route.key;
|
|
632
647
|
const gesture = gestures[route.key];
|
|
633
648
|
const scene = scenes[index];
|
|
634
|
-
|
|
635
|
-
// Particularly, if the screen is removed with `retain`, then it needs a moment to execute the animation.
|
|
636
|
-
// However, due to the router action, it immediately populates the `preloadedRoutes` array.
|
|
637
|
-
// Practically, the logic below takes care that it is rendered only once.
|
|
638
|
-
const isPreloaded =
|
|
639
|
-
state.preloadedRoutes.includes(route) && !routes.includes(route);
|
|
640
|
-
if (
|
|
641
|
-
state.preloadedRoutes.includes(route) &&
|
|
642
|
-
routes.includes(route) &&
|
|
643
|
-
index >= routes.length
|
|
644
|
-
) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
649
|
+
const isInactive = isInactiveRoute(route, this.props.routes);
|
|
647
650
|
|
|
648
651
|
const {
|
|
649
652
|
inactiveBehavior = 'pause',
|
|
653
|
+
animation,
|
|
650
654
|
headerShown = true,
|
|
651
655
|
headerTransparent,
|
|
652
656
|
} = scene.descriptor.options;
|
|
@@ -667,6 +671,16 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
667
671
|
isParentModal
|
|
668
672
|
);
|
|
669
673
|
|
|
674
|
+
const isAnimationEnabled =
|
|
675
|
+
getAnimationEnabled(animation) ||
|
|
676
|
+
// Also check next screen's animation,
|
|
677
|
+
// As it will result in both screens being visible
|
|
678
|
+
(scenes[index + 1]
|
|
679
|
+
? getAnimationEnabled(
|
|
680
|
+
scenes[index + 1].descriptor.options.animation
|
|
681
|
+
)
|
|
682
|
+
: false);
|
|
683
|
+
|
|
670
684
|
const isNextScreenTransparent =
|
|
671
685
|
scenes[index + 1]?.descriptor.options.presentation ===
|
|
672
686
|
'transparentModal';
|
|
@@ -683,22 +697,23 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
683
697
|
|
|
684
698
|
const isBeforeLast = index === routes.length - 2;
|
|
685
699
|
|
|
686
|
-
// Keep animating and the last two rendered routes visible for smoother transitions
|
|
687
|
-
// Preloaded routes should stay mounted, but remain hidden until focused.
|
|
700
|
+
// Keep animating and the last two rendered routes visible for smoother transitions
|
|
688
701
|
const isVisible =
|
|
689
702
|
focused ||
|
|
690
|
-
isFocusing ||
|
|
691
|
-
isRemoving ||
|
|
692
703
|
isNextScreenTransparent ||
|
|
693
|
-
|
|
704
|
+
// We only need to keep other screens visible when animation is enabled
|
|
705
|
+
(isAnimationEnabled &&
|
|
706
|
+
(isFocusing ||
|
|
707
|
+
isRemoving ||
|
|
708
|
+
// Preloaded and retained screens should stay mounted, but remain hidden until focused
|
|
709
|
+
(!isInactive && index >= routes.length - 2)));
|
|
694
710
|
|
|
695
711
|
const activityMode = // Render focused and animating screens normally
|
|
696
712
|
focused || isFocusing
|
|
697
713
|
? 'normal'
|
|
698
714
|
: inactiveBehavior === 'none' ||
|
|
699
|
-
// Unpause preloaded screens so updates are visible
|
|
700
|
-
|
|
701
|
-
isPreloaded ||
|
|
715
|
+
// Unpause preloaded or retained screens so updates are visible
|
|
716
|
+
isInactive ||
|
|
702
717
|
// Keep the screen before transparent screen active
|
|
703
718
|
// This lets the screen under the transparent screen update and animate
|
|
704
719
|
isNextScreenTransparent ||
|
|
@@ -753,7 +768,7 @@ export class CardStack extends React.Component<Props, State> {
|
|
|
753
768
|
onTransitionStart={onTransitionStart}
|
|
754
769
|
onTransitionEnd={onTransitionEnd}
|
|
755
770
|
isNextScreenTransparent={isNextScreenTransparent}
|
|
756
|
-
preloaded={
|
|
771
|
+
preloaded={isInactive}
|
|
757
772
|
>
|
|
758
773
|
<ActivityView
|
|
759
774
|
mode={activityMode}
|
|
@@ -62,10 +62,13 @@ export class StackView extends React.Component<Props, State> {
|
|
|
62
62
|
props: Readonly<Props>,
|
|
63
63
|
state: Readonly<State>
|
|
64
64
|
) {
|
|
65
|
-
const allRoutes =
|
|
66
|
-
const previousRoutes = state.previousState
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
const allRoutes = props.state.routes;
|
|
66
|
+
const previousRoutes = state.previousState?.routes ?? [];
|
|
67
|
+
const previousFocusedRoute = state.previousState
|
|
68
|
+
? state.previousState.routes[state.previousState.index]
|
|
69
|
+
: undefined;
|
|
70
|
+
const nextFocusedRouteFromState = props.state.routes[props.state.index];
|
|
71
|
+
const activeRoutes = props.state.routes.slice(0, props.state.index + 1);
|
|
69
72
|
|
|
70
73
|
// If there was no change in routes, we don't need to compute anything
|
|
71
74
|
if (
|
|
@@ -73,6 +76,7 @@ export class StackView extends React.Component<Props, State> {
|
|
|
73
76
|
allRoutes.map((r) => r.key),
|
|
74
77
|
previousRoutes.map((r) => r.key)
|
|
75
78
|
) &&
|
|
79
|
+
previousFocusedRoute?.key === nextFocusedRouteFromState?.key &&
|
|
76
80
|
state.routes.length
|
|
77
81
|
) {
|
|
78
82
|
// If there were any routes being closed or replaced,
|
|
@@ -83,16 +87,16 @@ export class StackView extends React.Component<Props, State> {
|
|
|
83
87
|
const closingRoutes = state.routes.filter(
|
|
84
88
|
(r) =>
|
|
85
89
|
state.closingRouteKeys.includes(r.key) &&
|
|
86
|
-
!
|
|
90
|
+
!activeRoutes.some((pr) => pr.key === r.key)
|
|
87
91
|
);
|
|
88
92
|
|
|
89
93
|
const replacingRoutes = state.routes.filter(
|
|
90
94
|
(r) =>
|
|
91
95
|
state.replacingRouteKeys.includes(r.key) &&
|
|
92
|
-
!
|
|
96
|
+
!activeRoutes.some((pr) => pr.key === r.key)
|
|
93
97
|
);
|
|
94
98
|
|
|
95
|
-
let routes: Route<string>[] =
|
|
99
|
+
let routes: Route<string>[] = activeRoutes.slice();
|
|
96
100
|
|
|
97
101
|
// Replacing routes go before the focused route (they're being covered)
|
|
98
102
|
if (replacingRoutes.length) {
|
|
@@ -117,9 +121,10 @@ export class StackView extends React.Component<Props, State> {
|
|
|
117
121
|
routes = routes.map((route) => map[route.key] || route);
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
const routeKeys = new Set(routes.map((route) => route.key));
|
|
120
125
|
const descriptors = [
|
|
121
126
|
...routes,
|
|
122
|
-
...props.state.
|
|
127
|
+
...props.state.routes.filter((route) => !routeKeys.has(route.key)),
|
|
123
128
|
].reduce<StackDescriptorMap>((acc, route) => {
|
|
124
129
|
acc[route.key] =
|
|
125
130
|
props.descriptors[route.key] || state.descriptors[route.key];
|
|
@@ -137,32 +142,20 @@ export class StackView extends React.Component<Props, State> {
|
|
|
137
142
|
// Here we determine which routes were added or removed to animate them
|
|
138
143
|
// We keep a copy of the route being removed in local state to be able to animate it
|
|
139
144
|
|
|
140
|
-
let routes =
|
|
141
|
-
props.state.index < props.state.routes.length - 1
|
|
142
|
-
? // Remove any extra routes from the state
|
|
143
|
-
// The last visible route should be the focused route, i.e. at current index
|
|
144
|
-
props.state.routes.slice(0, props.state.index + 1)
|
|
145
|
-
: props.state.routes;
|
|
146
|
-
|
|
147
145
|
let { openingRouteKeys, closingRouteKeys, replacingRouteKeys } = state;
|
|
148
146
|
|
|
149
147
|
// If a route that was closing or being replaced is now back in the routes,
|
|
150
148
|
// it was added back before the animation finished, so stop tracking it
|
|
151
149
|
closingRouteKeys = closingRouteKeys.filter(
|
|
152
|
-
(key) => !
|
|
150
|
+
(key) => !activeRoutes.some((r) => r.key === key)
|
|
153
151
|
);
|
|
154
152
|
|
|
155
153
|
replacingRouteKeys = replacingRouteKeys.filter(
|
|
156
|
-
(key) => !
|
|
154
|
+
(key) => !activeRoutes.some((r) => r.key === key)
|
|
157
155
|
);
|
|
158
156
|
|
|
159
|
-
// Get previous focused route from
|
|
160
|
-
|
|
161
|
-
const previousFocusedRoute = state.previousState
|
|
162
|
-
? state.previousState.routes[state.previousState.index]
|
|
163
|
-
: undefined;
|
|
164
|
-
|
|
165
|
-
const nextFocusedRoute = routes[routes.length - 1];
|
|
157
|
+
// Get previous focused route from previous active routes
|
|
158
|
+
const nextFocusedRoute = activeRoutes[activeRoutes.length - 1];
|
|
166
159
|
|
|
167
160
|
const isAnimationEnabled = (key: string) => {
|
|
168
161
|
const descriptor = props.descriptors[key] || state.descriptors[key];
|
|
@@ -176,6 +169,8 @@ export class StackView extends React.Component<Props, State> {
|
|
|
176
169
|
return descriptor.options.animationTypeForReplace ?? 'push';
|
|
177
170
|
};
|
|
178
171
|
|
|
172
|
+
let routes = activeRoutes;
|
|
173
|
+
|
|
179
174
|
if (
|
|
180
175
|
previousFocusedRoute &&
|
|
181
176
|
previousFocusedRoute.key !== nextFocusedRoute.key
|
|
@@ -301,9 +296,10 @@ export class StackView extends React.Component<Props, State> {
|
|
|
301
296
|
);
|
|
302
297
|
}
|
|
303
298
|
|
|
299
|
+
const routeKeys = new Set(routes.map((route) => route.key));
|
|
304
300
|
const descriptors = [
|
|
305
301
|
...routes,
|
|
306
|
-
...props.state.
|
|
302
|
+
...props.state.routes.filter((route) => !routeKeys.has(route.key)),
|
|
307
303
|
].reduce<StackDescriptorMap>((acc, route) => {
|
|
308
304
|
acc[route.key] =
|
|
309
305
|
props.descriptors[route.key] || state.descriptors[route.key];
|
|
@@ -352,25 +348,33 @@ export class StackView extends React.Component<Props, State> {
|
|
|
352
348
|
const { state, navigation } = this.props;
|
|
353
349
|
const { closingRouteKeys, replacingRouteKeys } = this.state;
|
|
354
350
|
|
|
351
|
+
const activeRoutes = state.routes.slice(0, state.index + 1);
|
|
352
|
+
|
|
355
353
|
if (
|
|
356
354
|
closingRouteKeys.some((key) => key === route.key) &&
|
|
357
355
|
replacingRouteKeys.every((key) => key !== route.key) &&
|
|
358
356
|
state.routeNames.includes(route.name) &&
|
|
359
|
-
!
|
|
357
|
+
!activeRoutes.some((r) => r.key === route.key)
|
|
360
358
|
) {
|
|
361
|
-
// If route
|
|
359
|
+
// If route is no longer active, but was closing, assume that a close animation was cancelled
|
|
362
360
|
// So we need to add this route back to the state
|
|
363
361
|
navigation.dispatch((state) => {
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
route
|
|
367
|
-
|
|
362
|
+
const activeRoutes = state.routes
|
|
363
|
+
.slice(0, state.index + 1)
|
|
364
|
+
.filter((r) => r.key !== route.key);
|
|
365
|
+
const inactiveRoutes = state.routes
|
|
366
|
+
.slice(state.index + 1)
|
|
367
|
+
.filter((r) => r.key !== route.key);
|
|
368
|
+
|
|
369
|
+
const routes = [...activeRoutes, route];
|
|
368
370
|
|
|
369
|
-
|
|
371
|
+
const resetState = {
|
|
370
372
|
...state,
|
|
371
|
-
routes,
|
|
372
373
|
index: routes.length - 1,
|
|
373
|
-
|
|
374
|
+
routes: routes.concat(inactiveRoutes),
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
return CommonActions.reset(resetState);
|
|
374
378
|
});
|
|
375
379
|
} else {
|
|
376
380
|
this.setState((state) => {
|
|
@@ -408,7 +412,9 @@ export class StackView extends React.Component<Props, State> {
|
|
|
408
412
|
private handleCloseRoute = ({ route }: { route: Route<string> }) => {
|
|
409
413
|
const { state, navigation } = this.props;
|
|
410
414
|
|
|
411
|
-
|
|
415
|
+
const activeRoutes = state.routes.slice(0, state.index + 1);
|
|
416
|
+
|
|
417
|
+
if (activeRoutes.some((r) => r.key === route.key)) {
|
|
412
418
|
// If a route exists in state, trigger a pop
|
|
413
419
|
// This will happen in when the route was closed from the card component
|
|
414
420
|
// e.g. When the close animation triggered from a gesture ends
|