From ba6d7dcbedcfca774a53de32f92acc45140bb1fe Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 8 Jun 2020 11:31:03 +0200 Subject: [PATCH] feat: sync latest stack --- example/src/SimpleStack.tsx | 6 +- packages/stack/package.json | 2 +- packages/stack/scripts/stack.patch | 93 ++-- .../NestedNavigator.test.tsx.snap | 127 ++---- .../StackNavigator.test.tsx.snap | 18 + packages/stack/src/vendor/types.tsx | 3 +- .../src/vendor/utils/HeaderShownContext.tsx | 5 + .../vendor/views/GestureHandler.native.tsx | 2 +- .../vendor/views/Header/HeaderContainer.tsx | 36 +- .../stack/src/vendor/views/Stack/Card.tsx | 13 +- .../src/vendor/views/Stack/CardContainer.tsx | 16 +- .../src/vendor/views/Stack/CardStack.tsx | 426 +++++++++--------- .../src/vendor/views/Stack/StackView.tsx | 12 +- yarn.lock | 8 +- 14 files changed, 386 insertions(+), 381 deletions(-) create mode 100644 packages/stack/src/vendor/utils/HeaderShownContext.tsx diff --git a/example/src/SimpleStack.tsx b/example/src/SimpleStack.tsx index e1148ae8..e48029ff 100644 --- a/example/src/SimpleStack.tsx +++ b/example/src/SimpleStack.tsx @@ -64,7 +64,7 @@ class ListScreen extends React.Component { flex: 1, alignItems: 'center', justifyContent: 'center', - backgroundColor: '#fff', + // backgroundColor: '#fff', }} > List Screen @@ -96,7 +96,7 @@ class DetailsScreen extends React.Component { flex: 1, alignItems: 'center', justifyContent: 'center', - backgroundColor: '#fff', + // backgroundColor: '#fff', }} > Details Screen @@ -119,7 +119,7 @@ class HeaderlessScreen extends React.Component { flex: 1, alignItems: 'center', justifyContent: 'center', - backgroundColor: '#fff', + // backgroundColor: '#fff', }} > Headerless Screen diff --git a/packages/stack/package.json b/packages/stack/package.json index 52f7c5ec..76eafeae 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -45,7 +45,7 @@ "devDependencies": { "@react-native-community/bob": "^0.10.0", "@react-native-community/masked-view": "^0.1.7", - "@react-navigation/stack": "^5.4.2", + "@react-navigation/stack": "^5.5.1", "@types/color": "^3.0.1", "@types/react": "^16.9.23", "@types/react-native": "^0.61.22", diff --git a/packages/stack/scripts/stack.patch b/packages/stack/scripts/stack.patch index 797a0472..b1911814 100644 --- a/packages/stack/scripts/stack.patch +++ b/packages/stack/scripts/stack.patch @@ -1,6 +1,6 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/index.tsx ---- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/index.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/index.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/index.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -3,11 +3,6 @@ import * as TransitionSpecs from './TransitionConfigs/TransitionSpecs'; import * as TransitionPresets from './TransitionConfigs/TransitionPresets'; @@ -28,7 +28,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/index.tsx src/vendor/i StackHeaderLeftButtonProps, StackHeaderTitleProps, diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx src/vendor/navigators/createStackNavigator.tsx ---- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-06-06 02:18:25.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/navigators/createStackNavigator.tsx 2020-06-08 11:22:56.000000000 +0200 +++ src/vendor/navigators/createStackNavigator.tsx 1970-01-01 01:00:00.000000000 +0100 @@ -1,96 +0,0 @@ -import * as React from 'react'; @@ -128,8 +128,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/navigators/createStack - typeof StackNavigator ->(StackNavigator); diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/types.tsx ---- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/types.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/types.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/types.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -8,15 +8,28 @@ } from 'react-native'; import { EdgeInsets } from 'react-native-safe-area-context'; @@ -218,7 +218,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t }; export type Layout = { width: number; height: number }; -@@ -227,24 +239,27 @@ +@@ -228,24 +240,27 @@ /** * Navigation prop for the header. */ @@ -252,7 +252,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t export type StackNavigationOptions = StackHeaderOptions & Partial & { /** -@@ -329,6 +344,8 @@ +@@ -330,6 +345,8 @@ bottom?: number; left?: number; }; @@ -262,8 +262,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/types.tsx src/vendor/t export type StackNavigationConfig = { diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx src/vendor/views/Header/Header.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Header/Header.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Header/Header.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Header/Header.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -1,12 +1,14 @@ import * as React from 'react'; -import { StackActions } from '@react-navigation/native'; @@ -344,8 +344,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/Header.ts + +export default Header; diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx src/vendor/views/Header/HeaderBackButton.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Header/HeaderBackButton.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackButton.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Header/HeaderBackButton.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -8,9 +8,9 @@ StyleSheet, LayoutChangeEvent, @@ -358,8 +358,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBac type Props = StackHeaderLeftButtonProps; diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx src/vendor/views/Header/HeaderBackground.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Header/HeaderBackground.tsx 2020-06-06 02:23:41.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBackground.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Header/HeaderBackground.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -7,7 +7,7 @@ StyleProp, ViewStyle, @@ -370,8 +370,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderBac type Props = ViewProps & { style?: Animated.WithAnimatedValue>; diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx src/vendor/views/Header/HeaderContainer.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Header/HeaderContainer.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Header/HeaderContainer.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -1,11 +1,6 @@ import * as React from 'react'; import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native'; @@ -385,15 +385,15 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon import { EdgeInsets } from 'react-native-safe-area-context'; import Header from './Header'; -@@ -16,6 +11,7 @@ - forSlideRight, +@@ -17,6 +12,7 @@ } from '../../TransitionConfigs/HeaderStyleInterpolators'; + import HeaderShownContext from '../../utils/HeaderShownContext'; import { + Route, Layout, Scene, StackHeaderStyleInterpolator, -@@ -99,9 +95,7 @@ +@@ -117,9 +113,7 @@ insets, scene, previous, @@ -404,7 +404,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon styleInterpolator: mode === 'float' ? isHeaderStatic -@@ -120,7 +114,7 @@ +@@ -138,7 +132,7 @@ key={scene.route.key} value={scene.descriptor.navigation} > @@ -413,9 +413,9 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon + {header !== undefined ? header(props) :
} - + @@ -423,8 +423,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderCon ); })} diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx src/vendor/views/Header/HeaderSegment.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Header/HeaderSegment.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSegment.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Header/HeaderSegment.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -8,7 +8,7 @@ ViewStyle, } from 'react-native'; @@ -444,8 +444,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderSeg }; diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx src/vendor/views/Header/HeaderTitle.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Header/HeaderTitle.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTitle.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Header/HeaderTitle.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -1,6 +1,6 @@ import * as React from 'react'; import { Animated, StyleSheet, Platform } from 'react-native'; @@ -455,8 +455,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Header/HeaderTit type Props = Omit, 'key'> & { tintColor?: string; diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx src/vendor/views/Stack/Card.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Stack/Card.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Stack/Card.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -146,7 +146,7 @@ private interactionHandle: number | undefined; @@ -467,15 +467,16 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/Card.tsx s private animate = ({ closing, diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx src/vendor/views/Stack/CardContainer.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Stack/CardContainer.tsx 2020-06-06 02:22:30.000000000 +0200 -@@ -1,10 +1,11 @@ +--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardContainer.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Stack/CardContainer.tsx 2020-06-08 11:23:27.000000000 +0200 +@@ -1,11 +1,12 @@ import * as React from 'react'; import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-native'; -import { Route, useTheme } from '@react-navigation/native'; import { Props as HeaderContainerProps } from '../Header/HeaderContainer'; import Card from './Card'; import HeaderHeightContext from '../../utils/HeaderHeightContext'; + import HeaderShownContext from '../../utils/HeaderShownContext'; +import useTheme from '../../../utils/useTheme'; import { + Route, @@ -483,8 +484,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardContai Layout, StackHeaderMode, diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx src/vendor/views/Stack/CardStack.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Stack/CardStack.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Stack/CardStack.tsx 2020-06-08 11:23:27.000000000 +0200 @@ -7,7 +7,7 @@ Platform, } from 'react-native'; @@ -494,8 +495,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack. import { MaybeScreenContainer, MaybeScreen } from '../Screens'; import { getDefaultHeaderHeight } from '../Header/HeaderSegment'; -@@ -21,6 +21,7 @@ - import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators'; +@@ -22,6 +22,7 @@ + import HeaderShownContext from '../../utils/HeaderShownContext'; import getDistanceForDirection from '../../utils/getDistanceForDirection'; import { + Route, @@ -503,8 +504,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/CardStack. StackHeaderMode, StackCardMode, diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx src/vendor/views/Stack/StackView.tsx ---- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-06-06 02:18:25.000000000 +0200 -+++ src/vendor/views/Stack/StackView.tsx 2020-06-06 02:22:30.000000000 +0200 +--- ../../node_modules/@react-navigation/stack/src/views/Stack/StackView.tsx 2020-06-08 11:22:56.000000000 +0200 ++++ src/vendor/views/Stack/StackView.tsx 2020-06-08 11:23:40.000000000 +0200 @@ -2,11 +2,11 @@ import { View, Platform, StyleSheet } from 'react-native'; import { SafeAreaConsumer, EdgeInsets } from 'react-native-safe-area-context'; @@ -548,7 +549,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView. } return false; -@@ -321,24 +325,47 @@ +@@ -321,26 +325,49 @@ return null; } @@ -584,22 +585,24 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView. + private handleOpenRoute = ({ route }: { route: Route }) => { const { state, navigation } = this.props; + const { closingRouteKeys, replacingRouteKeys } = this.state; + this.handleTransitionComplete(); + if ( - this.state.replacingRouteKeys.every((key) => key !== route.key) && + closingRouteKeys.some((key) => key === route.key) && + replacingRouteKeys.every((key) => key !== route.key) && - state.routeNames.includes(route.name) && !state.routes.some((r) => r.key === route.key) ) { - // If route isn't present in current state, assume that a close animation was cancelled + // If route isn't present in current state, but was closing, assume that a close animation was cancelled // So we need to add this route back to the state - navigation.navigate(route); + navigation.dispatch(NavigationActions.navigate(route)); } else { this.setState((state) => ({ routes: state.replacingRouteKeys.length -@@ -364,12 +391,11 @@ +@@ -366,12 +393,11 @@ // If a route exists in state, trigger a pop // This will happen in when the route was closed from the card component // e.g. When the close animation triggered from a gesture ends @@ -616,7 +619,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView. // We need to clean up any state tracking the route and pop it immediately this.setState((state) => ({ routes: state.routes.filter((r) => r.key !== route.key), -@@ -386,26 +412,29 @@ +@@ -388,26 +414,29 @@ private handleTransitionStart = ( { route }: { route: Route }, closing: boolean @@ -658,8 +661,8 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView. navigation, keyboardHandlingEnabled, mode = 'card', -@@ -423,7 +452,7 @@ - mode === 'card' && Platform.OS === 'ios' ? 'float' : 'screen'; +@@ -425,7 +454,7 @@ + } = this.state; return ( - @@ -667,7 +670,7 @@ diff -Naur ../../node_modules/@react-navigation/stack/src/views/Stack/StackView. -@@ -456,7 +485,7 @@ +@@ -458,7 +487,7 @@ diff --git a/packages/stack/src/navigators/__tests__/__snapshots__/NestedNavigator.test.tsx.snap b/packages/stack/src/navigators/__tests__/__snapshots__/NestedNavigator.test.tsx.snap index 91d288f1..a9cad75d 100644 --- a/packages/stack/src/navigators/__tests__/__snapshots__/NestedNavigator.test.tsx.snap +++ b/packages/stack/src/navigators/__tests__/__snapshots__/NestedNavigator.test.tsx.snap @@ -10,6 +10,14 @@ exports[`Nested navigators renders succesfully as direct child 1`] = ` > @@ -267,106 +276,6 @@ exports[`Nested navigators renders succesfully as direct child 1`] = ` } } > - - - - - - - - - - - Home - - - - - - @@ -527,6 +438,22 @@ exports[`Nested navigators renders succesfully as direct child 1`] = ` + diff --git a/packages/stack/src/navigators/__tests__/__snapshots__/StackNavigator.test.tsx.snap b/packages/stack/src/navigators/__tests__/__snapshots__/StackNavigator.test.tsx.snap index 3848fde8..7b888f59 100644 --- a/packages/stack/src/navigators/__tests__/__snapshots__/StackNavigator.test.tsx.snap +++ b/packages/stack/src/navigators/__tests__/__snapshots__/StackNavigator.test.tsx.snap @@ -10,6 +10,14 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] = > @@ -298,6 +307,14 @@ exports[`StackNavigator renders successfully 1`] = ` > diff --git a/packages/stack/src/vendor/types.tsx b/packages/stack/src/vendor/types.tsx index 0d187d27..5c2e4311 100644 --- a/packages/stack/src/vendor/types.tsx +++ b/packages/stack/src/vendor/types.tsx @@ -145,7 +145,8 @@ export type StackHeaderOptions = { */ headerBackAllowFontScaling?: boolean; /** - * Title string used by the back button on iOS, or `null` to disable label. Defaults to the previous scene's `headerTitle`. + * Title string used by the back button on iOS. Defaults to the previous scene's `headerTitle`. + * Use `headerBackTitleVisible: false` to hide it. */ headerBackTitle?: string; /** diff --git a/packages/stack/src/vendor/utils/HeaderShownContext.tsx b/packages/stack/src/vendor/utils/HeaderShownContext.tsx new file mode 100644 index 00000000..be5b7a57 --- /dev/null +++ b/packages/stack/src/vendor/utils/HeaderShownContext.tsx @@ -0,0 +1,5 @@ +import * as React from 'react'; + +const HeaderShownContext = React.createContext(false); + +export default HeaderShownContext; diff --git a/packages/stack/src/vendor/views/GestureHandler.native.tsx b/packages/stack/src/vendor/views/GestureHandler.native.tsx index e2c91f99..c00d675e 100644 --- a/packages/stack/src/vendor/views/GestureHandler.native.tsx +++ b/packages/stack/src/vendor/views/GestureHandler.native.tsx @@ -10,7 +10,7 @@ export function PanGestureHandler(props: PanGestureHandlerProperties) { return ( - + ); } diff --git a/packages/stack/src/vendor/views/Header/HeaderContainer.tsx b/packages/stack/src/vendor/views/Header/HeaderContainer.tsx index 0fb02097..6303fea8 100644 --- a/packages/stack/src/vendor/views/Header/HeaderContainer.tsx +++ b/packages/stack/src/vendor/views/Header/HeaderContainer.tsx @@ -10,6 +10,7 @@ import { forNoAnimation, forSlideRight, } from '../../TransitionConfigs/HeaderStyleInterpolators'; +import HeaderShownContext from '../../utils/HeaderShownContext'; import { Route, Layout, @@ -50,6 +51,7 @@ export default function HeaderContainer({ style, }: Props) { const focusedRoute = getFocusedRoute(); + const isParentHeaderShown = React.useContext(HeaderShownContext); return ( @@ -58,7 +60,16 @@ export default function HeaderContainer({ return null; } - const { options } = scene.descriptor; + const { + header, + headerShown = isParentHeaderShown === false, + headerTransparent, + } = scene.descriptor.options || {}; + + if (!headerShown) { + return null; + } + const isFocused = focusedRoute.key === scene.route.key; const previousRoute = getPreviousRoute({ route: scene.route }); @@ -81,13 +92,20 @@ export default function HeaderContainer({ // This makes the header look like it's moving with the screen const previousScene = self[i - 1]; const nextScene = self[i + 1]; + + const { + headerShown: previousHeaderShown = isParentHeaderShown === false, + } = previousScene?.descriptor.options || {}; + + const { headerShown: nextHeaderShown = isParentHeaderShown === false } = + nextScene?.descriptor.options || {}; + const isHeaderStatic = - (previousScene && - previousScene.descriptor.options.headerShown === false && + (previousHeaderShown === false && // We still need to animate when coming back from next scene // A hacky way to check this is if the next scene exists !nextScene) || - (nextScene && nextScene.descriptor.options.headerShown === false); + nextHeaderShown === false; const props = { mode, @@ -133,18 +151,12 @@ export default function HeaderContainer({ style={ // Avoid positioning the focused header absolutely // Otherwise accessibility tools don't seem to be able to find it - (mode === 'float' && !isFocused) || options.headerTransparent + (mode === 'float' && !isFocused) || headerTransparent ? styles.header : null } > - {options.headerShown !== false ? ( - options.header !== undefined ? ( - options.header(props) - ) : ( -
- ) - ) : null} + {header !== undefined ? header(props) :
} diff --git a/packages/stack/src/vendor/views/Stack/Card.tsx b/packages/stack/src/vendor/views/Stack/Card.tsx index 2ae6f151..790a1c84 100755 --- a/packages/stack/src/vendor/views/Stack/Card.tsx +++ b/packages/stack/src/vendor/views/Stack/Card.tsx @@ -493,6 +493,12 @@ export default class Card extends React.Component { ? Color(backgroundColor).alpha() === 0 : false; + // This is a dummy style that doesn't actually change anything visually. + // Animated needs the animated value to be used somewhere, otherwise things don't update properly. + // If we disable animations and hide header, it could end up making the value unused. + // So we have this dummy style that will always be used regardless of what else changed. + const dummyStyle = { opacity: Animated.diffClamp(current, 1, 1) }; + return ( @@ -502,7 +508,12 @@ export default class Card extends React.Component { ) : null} - - {renderScene({ route: scene.route })} - + + + {renderScene({ route: scene.route })} + + {headerMode === 'screen' ? renderHeader({ diff --git a/packages/stack/src/vendor/views/Stack/CardStack.tsx b/packages/stack/src/vendor/views/Stack/CardStack.tsx index 79004a44..9c91f142 100755 --- a/packages/stack/src/vendor/views/Stack/CardStack.tsx +++ b/packages/stack/src/vendor/views/Stack/CardStack.tsx @@ -19,6 +19,7 @@ import { } from '../../TransitionConfigs/TransitionPresets'; import { forNoAnimation as forNoAnimationHeader } from '../../TransitionConfigs/HeaderStyleInterpolators'; import { forNoAnimation as forNoAnimationCard } from '../../TransitionConfigs/CardStyleInterpolators'; +import HeaderShownContext from '../../utils/HeaderShownContext'; import getDistanceForDirection from '../../utils/getDistanceForDirection'; import { Route, @@ -336,24 +337,6 @@ export default class CardStack extends React.Component { return state.routes[state.index]; }; - private doesFloatHeaderNeedAbsolutePositioning = () => { - if (this.props.headerMode !== 'float') { - return false; - } - - return this.state.scenes.slice(-2).some((scene) => { - const { descriptor } = scene; - const options = descriptor ? descriptor.options : {}; - const { headerTransparent, headerShown } = options; - - if (headerTransparent || headerShown === false) { - return true; - } - - return false; - }); - }; - render() { const { mode, @@ -382,8 +365,6 @@ export default class CardStack extends React.Component { const focusedDescriptor = descriptors[focusedRoute.key]; const focusedOptions = focusedDescriptor ? focusedDescriptor.options : {}; - const isFloatHeaderAbsolute = this.doesFloatHeaderNeedAbsolutePositioning(); - let defaultTransitionPreset = mode === 'modal' ? ModalTransition : DefaultTransition; @@ -405,193 +386,225 @@ export default class CardStack extends React.Component { // For modals, usually we want the screen underneath to be visible, so also disable it there const isScreensEnabled = Platform.OS !== 'ios' && mode !== 'modal'; - let floatingHeader; - - if (headerMode === 'float') { - floatingHeader = ( - - {renderHeader({ - mode: 'float', - layout, - insets: { top, right, bottom, left }, - scenes, - getPreviousRoute, - getFocusedRoute: this.getFocusedRoute, - onContentHeightChange: this.handleHeaderLayout, - gestureDirection: - focusedOptions.gestureDirection !== undefined - ? focusedOptions.gestureDirection - : defaultTransitionPreset.gestureDirection, - styleInterpolator: - focusedOptions.headerStyleInterpolator !== undefined - ? focusedOptions.headerStyleInterpolator - : defaultTransitionPreset.headerStyleInterpolator, - style: isFloatHeaderAbsolute ? styles.floating : undefined, - })} - - ); - } - return ( - - {isFloatHeaderAbsolute ? null : floatingHeader} - - {routes.map((route, index, self) => { - const focused = focusedRoute.key === route.key; - const gesture = gestures[route.key]; - const scene = scenes[index]; + + {(isParentHeaderShown) => { + const isFloatHeaderAbsolute = + headerMode === 'float' + ? this.state.scenes.slice(-2).some((scene) => { + const { descriptor } = scene; + const options = descriptor ? descriptor.options : {}; + const { + headerTransparent, + headerShown = isParentHeaderShown === false, + } = options; - const isScreenActive = scene.progress.next - ? scene.progress.next.interpolate({ - inputRange: [0, 1 - EPSILON, 1], - outputRange: [1, 1, 0], - extrapolate: 'clamp', - }) - : 1; - - const { - safeAreaInsets, - headerShown, - headerTransparent, - cardShadowEnabled, - cardOverlayEnabled, - cardOverlay, - cardStyle, - animationEnabled, - gestureResponseDistance, - gestureVelocityImpact, - gestureDirection = defaultTransitionPreset.gestureDirection, - transitionSpec = defaultTransitionPreset.transitionSpec, - cardStyleInterpolator = animationEnabled === false - ? forNoAnimationCard - : defaultTransitionPreset.cardStyleInterpolator, - headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator, - } = scene.descriptor - ? scene.descriptor.options - : ({} as StackNavigationOptions); - - let transitionConfig = { - gestureDirection, - transitionSpec, - cardStyleInterpolator, - headerStyleInterpolator, - }; - - // When a screen is not the last, it should use next screen's transition config - // Many transitions also animate the previous screen, so using 2 different transitions doesn't look right - // For example combining a slide and a modal transition would look wrong otherwise - // With this approach, combining different transition styles in the same navigator mostly looks right - // This will still be broken when 2 transitions have different idle state (e.g. modal presentation), - // but majority of the transitions look alright - if (index !== self.length - 1) { - const nextScene = scenes[index + 1]; - - if (nextScene) { - const { - animationEnabled, - gestureDirection = defaultTransitionPreset.gestureDirection, - transitionSpec = defaultTransitionPreset.transitionSpec, - cardStyleInterpolator = animationEnabled === false - ? forNoAnimationCard - : defaultTransitionPreset.cardStyleInterpolator, - headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator, - } = nextScene.descriptor - ? nextScene.descriptor.options - : ({} as StackNavigationOptions); - - transitionConfig = { - gestureDirection, - transitionSpec, - cardStyleInterpolator, - headerStyleInterpolator, - }; - } - } - - const { - top: safeAreaInsetTop = insets.top, - right: safeAreaInsetRight = insets.right, - bottom: safeAreaInsetBottom = insets.bottom, - left: safeAreaInsetLeft = insets.left, - } = safeAreaInsets || {}; - - const previousRoute = getPreviousRoute({ route: scene.route }); - - let previousScene = scenes[index - 1]; - - if (previousRoute) { - // The previous scene will be shortly before the current scene in the array - // So loop back from current index to avoid looping over the full array - for (let j = index - 1; j >= 0; j--) { - const s = scenes[j]; - - if (s && s.route.key === previousRoute.key) { - previousScene = s; - break; - } - } - } - - return ( - - - - ); - })} - - {isFloatHeaderAbsolute ? floatingHeader : null} - + + return false; + }) + : false; + + const floatingHeader = + headerMode === 'float' ? ( + + {renderHeader({ + mode: 'float', + layout, + insets: { top, right, bottom, left }, + scenes, + getPreviousRoute, + getFocusedRoute: this.getFocusedRoute, + onContentHeightChange: this.handleHeaderLayout, + gestureDirection: + focusedOptions.gestureDirection !== undefined + ? focusedOptions.gestureDirection + : defaultTransitionPreset.gestureDirection, + styleInterpolator: + focusedOptions.headerStyleInterpolator !== undefined + ? focusedOptions.headerStyleInterpolator + : defaultTransitionPreset.headerStyleInterpolator, + style: [ + styles.floating, + isFloatHeaderAbsolute && styles.absolute, + ], + })} + + ) : null; + + return ( + + {isFloatHeaderAbsolute ? null : floatingHeader} + + {routes.map((route, index, self) => { + const focused = focusedRoute.key === route.key; + const gesture = gestures[route.key]; + const scene = scenes[index]; + + const isScreenActive = scene.progress.next + ? scene.progress.next.interpolate({ + inputRange: [0, 1 - EPSILON, 1], + outputRange: [1, 1, 0], + extrapolate: 'clamp', + }) + : 1; + + const { + safeAreaInsets, + headerShown = isParentHeaderShown === false, + headerTransparent, + cardShadowEnabled, + cardOverlayEnabled, + cardOverlay, + cardStyle, + animationEnabled, + gestureResponseDistance, + gestureVelocityImpact, + gestureDirection = defaultTransitionPreset.gestureDirection, + transitionSpec = defaultTransitionPreset.transitionSpec, + cardStyleInterpolator = animationEnabled === false + ? forNoAnimationCard + : defaultTransitionPreset.cardStyleInterpolator, + headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator, + } = scene.descriptor + ? scene.descriptor.options + : ({} as StackNavigationOptions); + + let transitionConfig = { + gestureDirection, + transitionSpec, + cardStyleInterpolator, + headerStyleInterpolator, + }; + + // When a screen is not the last, it should use next screen's transition config + // Many transitions also animate the previous screen, so using 2 different transitions doesn't look right + // For example combining a slide and a modal transition would look wrong otherwise + // With this approach, combining different transition styles in the same navigator mostly looks right + // This will still be broken when 2 transitions have different idle state (e.g. modal presentation), + // but majority of the transitions look alright + if (index !== self.length - 1) { + const nextScene = scenes[index + 1]; + + if (nextScene) { + const { + animationEnabled, + gestureDirection = defaultTransitionPreset.gestureDirection, + transitionSpec = defaultTransitionPreset.transitionSpec, + cardStyleInterpolator = animationEnabled === false + ? forNoAnimationCard + : defaultTransitionPreset.cardStyleInterpolator, + headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator, + } = nextScene.descriptor + ? nextScene.descriptor.options + : ({} as StackNavigationOptions); + + transitionConfig = { + gestureDirection, + transitionSpec, + cardStyleInterpolator, + headerStyleInterpolator, + }; + } + } + + const { + top: safeAreaInsetTop = insets.top, + right: safeAreaInsetRight = insets.right, + bottom: safeAreaInsetBottom = insets.bottom, + left: safeAreaInsetLeft = insets.left, + } = safeAreaInsets || {}; + + const previousRoute = getPreviousRoute({ + route: scene.route, + }); + + let previousScene = scenes[index - 1]; + + if (previousRoute) { + // The previous scene will be shortly before the current scene in the array + // So loop back from current index to avoid looping over the full array + for (let j = index - 1; j >= 0; j--) { + const s = scenes[j]; + + if (s && s.route.key === previousRoute.key) { + previousScene = s; + break; + } + } + } + + const headerHeight = + headerMode !== 'none' && headerShown !== false + ? headerHeights[route.key] + : 0; + + return ( + + + + ); + })} + + {isFloatHeaderAbsolute ? floatingHeader : null} + + ); + }} + ); } } @@ -600,10 +613,13 @@ const styles = StyleSheet.create({ container: { flex: 1, }, - floating: { + absolute: { position: 'absolute', top: 0, left: 0, right: 0, }, + floating: { + zIndex: 1, + }, }); diff --git a/packages/stack/src/vendor/views/Stack/StackView.tsx b/packages/stack/src/vendor/views/Stack/StackView.tsx index dcb21773..9474e771 100644 --- a/packages/stack/src/vendor/views/Stack/StackView.tsx +++ b/packages/stack/src/vendor/views/Stack/StackView.tsx @@ -356,14 +356,16 @@ export default class StackView extends React.Component { private handleOpenRoute = ({ route }: { route: Route }) => { const { state, navigation } = this.props; + const { closingRouteKeys, replacingRouteKeys } = this.state; this.handleTransitionComplete(); if ( - this.state.replacingRouteKeys.every((key) => key !== route.key) && + closingRouteKeys.some((key) => key === route.key) && + replacingRouteKeys.every((key) => key !== route.key) && !state.routes.some((r) => r.key === route.key) ) { - // If route isn't present in current state, assume that a close animation was cancelled + // If route isn't present in current state, but was closing, assume that a close animation was cancelled // So we need to add this route back to the state navigation.dispatch(NavigationActions.navigate(route)); } else { @@ -438,6 +440,9 @@ export default class StackView extends React.Component { navigation, keyboardHandlingEnabled, mode = 'card', + headerMode = mode === 'card' && Platform.OS === 'ios' + ? 'float' + : 'screen', ...rest } = this.props; @@ -448,9 +453,6 @@ export default class StackView extends React.Component { closingRouteKeys, } = this.state; - const headerMode = - mode === 'card' && Platform.OS === 'ios' ? 'float' : 'screen'; - return ( <> diff --git a/yarn.lock b/yarn.lock index c4e37b24..ed1c53a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3202,10 +3202,10 @@ resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.7.tgz#a65ce0702f55cb67fd777995de6fc7b3e5781903" integrity sha512-9KbP7LTLFz9dx1heURJbO6nuVMdSjDez8znlrUzaB1nUwKVsTTwlKRuHxGUYIIkReLWrJQeCv9tidy+84z2eCw== -"@react-navigation/stack@^5.4.2": - version "5.4.2" - resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-5.4.2.tgz#ca4e77e8ab55b446e44f656068702825ec3b29d3" - integrity sha512-EG8DqHsfg257XeNaO6MIeAnPClekmr8po3PYikezyXon02rJUmHU4px25/PWOvh1hTFiH40W4WYBKzrzMBFaOQ== +"@react-navigation/stack@^5.5.1": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-5.5.1.tgz#95efa28edbfd1323e5ad9f01f539d43ffad8decc" + integrity sha512-oU2FEm+Ba6jMd5VA2WnuNfCO2HlZmGhrEX9yjBCyFj7fFCG1SB7WJdKLhZShtx3KxG/qWKphICeTLlYvkHdSpQ== dependencies: color "^3.1.2" react-native-iphone-x-helper "^1.2.1"