diff --git a/example/src/Screens/StackHeaderCustomization.tsx b/example/src/Screens/StackHeaderCustomization.tsx index a78a2b24..75dafc65 100644 --- a/example/src/Screens/StackHeaderCustomization.tsx +++ b/example/src/Screens/StackHeaderCustomization.tsx @@ -1,5 +1,12 @@ import * as React from 'react'; -import { View, StyleSheet, ScrollView, Alert, Platform } from 'react-native'; +import { + View, + StyleSheet, + ScrollView, + Alert, + Platform, + Text, +} from 'react-native'; import { Button, Appbar } from 'react-native-paper'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import { RouteProp, ParamListBase } from '@react-navigation/native'; @@ -8,6 +15,8 @@ import { StackNavigationProp, HeaderBackground, useHeaderHeight, + Header, + StackHeaderProps, } from '@react-navigation/stack'; import BlurView from '../Shared/BlurView'; import Article from '../Shared/Article'; @@ -91,6 +100,20 @@ type Props = Partial> & { navigation: StackNavigationProp; }; +function CustomHeader(props: StackHeaderProps) { + const { navigation } = props; + return ( + <> +
+ + + Why hello there, pardner! + + + + ); +} + export default function SimpleStackScreen({ navigation, ...rest }: Props) { navigation.setOptions({ headerShown: false, @@ -103,6 +126,7 @@ export default function SimpleStackScreen({ navigation, ...rest }: Props) { component={ArticleScreen} options={({ route }) => ({ title: `Article by ${route.params?.author}`, + header: CustomHeader, headerTintColor: '#fff', headerStyle: { backgroundColor: '#ff005d' }, headerBackTitleVisible: false, diff --git a/packages/stack/src/views/Stack/CardContainer.tsx b/packages/stack/src/views/Stack/CardContainer.tsx index 26651644..8c7955c9 100644 --- a/packages/stack/src/views/Stack/CardContainer.tsx +++ b/packages/stack/src/views/Stack/CardContainer.tsx @@ -55,6 +55,7 @@ type Props = TransitionPreset & { headerMode: StackHeaderMode; headerShown?: boolean; headerTransparent?: boolean; + isFloatHeaderAbsolute: boolean; headerHeight: number; onHeaderHeightChange: (props: { route: Route; @@ -85,6 +86,7 @@ function CardContainer({ headerShown, headerStyleInterpolator, headerTransparent, + isFloatHeaderAbsolute, headerHeight, onHeaderHeightChange, index, @@ -188,7 +190,7 @@ function CardContainer({ pointerEvents={active ? 'box-none' : pointerEvents} pageOverflowEnabled={headerMode === 'screen' && mode === 'card'} containerStyle={ - headerMode === 'float' && !headerTransparent && headerShown !== false + isFloatHeaderAbsolute && !headerTransparent && headerShown !== false ? { marginTop: headerHeight } : null } diff --git a/packages/stack/src/views/Stack/CardStack.tsx b/packages/stack/src/views/Stack/CardStack.tsx index 6ad3b217..8e44a276 100755 --- a/packages/stack/src/views/Stack/CardStack.tsx +++ b/packages/stack/src/views/Stack/CardStack.tsx @@ -335,6 +335,21 @@ export default class CardStack extends React.Component { return state.routes[state.index]; }; + private getSomeFloatHeaderNeedsAbsolutePositioning = () => { + 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, @@ -362,6 +377,7 @@ export default class CardStack extends React.Component { const focusedRoute = state.routes[state.index]; const focusedDescriptor = descriptors[focusedRoute.key]; const focusedOptions = focusedDescriptor ? focusedDescriptor.options : {}; + const isFloatHeaderAbsolute = this.getSomeFloatHeaderNeedsAbsolutePositioning(); let defaultTransitionPreset = mode === 'modal' ? ModalTransition : DefaultTransition; @@ -384,8 +400,34 @@ 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') { + const renderedHeader = 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, + }); + floatingHeader = ( + {renderedHeader} + ); + } + return ( + {isFloatHeaderAbsolute ? null : floatingHeader} { headerMode={headerMode} headerShown={headerShown} headerTransparent={headerTransparent} + isFloatHeaderAbsolute={isFloatHeaderAbsolute} renderHeader={renderHeader} renderScene={renderScene} onOpenRoute={onOpenRoute} @@ -538,26 +581,7 @@ export default class CardStack extends React.Component { ); })} - {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, - }) - : null} + {isFloatHeaderAbsolute ? floatingHeader : null} ); }