feat: sync latest stack

This commit is contained in:
Satyajit Sahoo
2020-06-08 11:31:03 +02:00
parent 2ca8c34b2d
commit ba6d7dcbed
14 changed files with 386 additions and 381 deletions

View File

@@ -10,6 +10,14 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
>
<View
pointerEvents="box-none"
style={
Array [
Object {
"zIndex": 1,
},
false,
]
}
>
<View
accessibilityElementsHidden={false}
@@ -180,6 +188,7 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
style={
Object {
"flex": 1,
"opacity": 1,
}
}
>
@@ -267,106 +276,6 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
}
}
>
<View
pointerEvents="box-none"
>
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
onLayout={[Function]}
pointerEvents="box-none"
style={null}
>
<View
pointerEvents="box-none"
style={
Object {
"bottom": 0,
"left": 0,
"opacity": 1,
"position": "absolute",
"right": 0,
"top": 0,
"zIndex": 0,
}
}
>
<View
style={
Object {
"backgroundColor": "#fff",
"borderBottomColor": "#a7a7aa",
"flex": 1,
"shadowColor": "#a7a7aa",
"shadowOffset": Object {
"height": 0.5,
"width": 0,
},
"shadowOpacity": 0.85,
"shadowRadius": 0,
}
}
/>
</View>
<View
pointerEvents="box-none"
style={
Object {
"height": 44,
"maxHeight": undefined,
"minHeight": undefined,
"opacity": undefined,
"transform": undefined,
}
}
>
<View
pointerEvents="none"
style={
Object {
"height": 0,
}
}
/>
<View
pointerEvents="box-none"
style={
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"justifyContent": "center",
}
}
>
<View
pointerEvents="box-none"
style={
Object {
"marginHorizontal": 16,
"opacity": 1,
}
}
>
<Text
accessibilityRole="header"
numberOfLines={1}
onLayout={[Function]}
style={
Object {
"color": "rgba(0, 0, 0, 0.9)",
"fontSize": 17,
"fontWeight": "600",
}
}
>
Home
</Text>
</View>
</View>
</View>
</View>
</View>
<View
onLayout={[Function]}
style={
@@ -439,6 +348,8 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
style={
Object {
"flex": 1,
"marginTop": 0,
"opacity": 1,
}
}
>
@@ -527,6 +438,22 @@ exports[`Nested navigators renders succesfully as direct child 1`] = `
</View>
</View>
</View>
<View
pointerEvents="box-none"
style={
Array [
Object {
"zIndex": 1,
},
Object {
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
]
}
/>
</View>
</View>
</View>

View File

@@ -10,6 +10,14 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
>
<View
pointerEvents="box-none"
style={
Array [
Object {
"zIndex": 1,
},
false,
]
}
>
<View
accessibilityElementsHidden={false}
@@ -197,6 +205,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
style={
Object {
"flex": 1,
"opacity": 1,
}
}
>
@@ -298,6 +307,14 @@ exports[`StackNavigator renders successfully 1`] = `
>
<View
pointerEvents="box-none"
style={
Array [
Object {
"zIndex": 1,
},
false,
]
}
>
<View
accessibilityElementsHidden={false}
@@ -469,6 +486,7 @@ exports[`StackNavigator renders successfully 1`] = `
style={
Object {
"flex": 1,
"opacity": 1,
}
}
>

View File

@@ -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;
/**

View File

@@ -0,0 +1,5 @@
import * as React from 'react';
const HeaderShownContext = React.createContext(false);
export default HeaderShownContext;

View File

@@ -10,7 +10,7 @@ export function PanGestureHandler(props: PanGestureHandlerProperties) {
return (
<GestureHandlerRefContext.Provider value={gestureRef}>
<PanGestureHandlerNative {...props} />
<PanGestureHandlerNative {...props} ref={gestureRef} />
</GestureHandlerRefContext.Provider>
);
}

View File

@@ -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 (
<View pointerEvents="box-none" style={style}>
@@ -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)
) : (
<Header {...props} />
)
) : null}
{header !== undefined ? header(props) : <Header {...props} />}
</View>
</>
</NavigationContext.Provider>

View File

@@ -493,6 +493,12 @@ export default class Card extends React.Component<Props> {
? 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 (
<CardAnimationContext.Provider value={animationContext}>
<View pointerEvents="box-none" {...rest}>
@@ -502,7 +508,12 @@ export default class Card extends React.Component<Props> {
</View>
) : null}
<Animated.View
style={[styles.container, containerStyle, customContainerStyle]}
style={[
styles.container,
dummyStyle,
containerStyle,
customContainerStyle,
]}
pointerEvents="box-none"
>
<PanGestureHandler

View File

@@ -3,6 +3,7 @@ import { Animated, View, StyleSheet, StyleProp, ViewStyle } from 'react-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,
@@ -54,6 +55,7 @@ type Props = TransitionPreset & {
gestureVelocityImpact?: number;
mode: StackCardMode;
headerMode: StackHeaderMode;
headerShown: boolean;
hasAbsoluteHeader: boolean;
headerHeight: number;
onHeaderHeightChange: (props: {
@@ -82,6 +84,7 @@ function CardContainer({
getFocusedRoute,
mode,
headerMode,
headerShown,
headerStyleInterpolator,
hasAbsoluteHeader,
headerHeight,
@@ -159,6 +162,9 @@ function CardContainer({
};
}, [pointerEvents, scene.progress.next]);
const isParentHeaderShown = React.useContext(HeaderShownContext);
const isCurrentHeaderShown = headerMode !== 'none' && headerShown !== false;
return (
<Card
index={index}
@@ -192,9 +198,13 @@ function CardContainer({
>
<View style={styles.container}>
<View style={styles.scene}>
<HeaderHeightContext.Provider value={headerHeight}>
{renderScene({ route: scene.route })}
</HeaderHeightContext.Provider>
<HeaderShownContext.Provider
value={isParentHeaderShown || isCurrentHeaderShown}
>
<HeaderHeightContext.Provider value={headerHeight}>
{renderScene({ route: scene.route })}
</HeaderHeightContext.Provider>
</HeaderShownContext.Provider>
</View>
{headerMode === 'screen'
? renderHeader({

View File

@@ -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<Props, State> {
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<Props, State> {
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<Props, State> {
// 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 = (
<React.Fragment key="header">
{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,
})}
</React.Fragment>
);
}
return (
<React.Fragment>
{isFloatHeaderAbsolute ? null : floatingHeader}
<MaybeScreenContainer
enabled={isScreensEnabled}
style={styles.container}
onLayout={this.handleLayout}
>
{routes.map((route, index, self) => {
const focused = focusedRoute.key === route.key;
const gesture = gestures[route.key];
const scene = scenes[index];
<HeaderShownContext.Consumer>
{(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 (
<MaybeScreen
key={route.key}
style={StyleSheet.absoluteFill}
enabled={isScreensEnabled}
active={isScreenActive}
pointerEvents="box-none"
>
<CardContainer
index={index}
active={index === self.length - 1}
focused={focused}
closing={closingRouteKeys.includes(route.key)}
layout={layout}
gesture={gesture}
scene={scene}
previousScene={previousScene}
safeAreaInsetTop={safeAreaInsetTop}
safeAreaInsetRight={safeAreaInsetRight}
safeAreaInsetBottom={safeAreaInsetBottom}
safeAreaInsetLeft={safeAreaInsetLeft}
cardOverlay={cardOverlay}
cardOverlayEnabled={cardOverlayEnabled}
cardShadowEnabled={cardShadowEnabled}
cardStyle={cardStyle}
onPageChangeStart={onPageChangeStart}
onPageChangeConfirm={onPageChangeConfirm}
onPageChangeCancel={onPageChangeCancel}
gestureResponseDistance={gestureResponseDistance}
headerHeight={headerHeights[route.key]}
onHeaderHeightChange={this.handleHeaderLayout}
getPreviousRoute={getPreviousRoute}
getFocusedRoute={this.getFocusedRoute}
mode={mode}
headerMode={headerMode}
hasAbsoluteHeader={
isFloatHeaderAbsolute &&
headerShown !== false &&
!headerTransparent
if (headerTransparent || headerShown === false) {
return true;
}
renderHeader={renderHeader}
renderScene={renderScene}
onOpenRoute={onOpenRoute}
onCloseRoute={onCloseRoute}
onTransitionStart={onTransitionStart}
onTransitionEnd={onTransitionEnd}
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
gestureVelocityImpact={gestureVelocityImpact}
{...transitionConfig}
/>
</MaybeScreen>
);
})}
</MaybeScreenContainer>
{isFloatHeaderAbsolute ? floatingHeader : null}
</React.Fragment>
return false;
})
: false;
const floatingHeader =
headerMode === 'float' ? (
<React.Fragment key="header">
{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,
],
})}
</React.Fragment>
) : null;
return (
<React.Fragment>
{isFloatHeaderAbsolute ? null : floatingHeader}
<MaybeScreenContainer
enabled={isScreensEnabled}
style={styles.container}
onLayout={this.handleLayout}
>
{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 (
<MaybeScreen
key={route.key}
style={StyleSheet.absoluteFill}
enabled={isScreensEnabled}
active={isScreenActive}
pointerEvents="box-none"
>
<CardContainer
index={index}
active={index === self.length - 1}
focused={focused}
closing={closingRouteKeys.includes(route.key)}
layout={layout}
gesture={gesture}
scene={scene}
previousScene={previousScene}
safeAreaInsetTop={safeAreaInsetTop}
safeAreaInsetRight={safeAreaInsetRight}
safeAreaInsetBottom={safeAreaInsetBottom}
safeAreaInsetLeft={safeAreaInsetLeft}
cardOverlay={cardOverlay}
cardOverlayEnabled={cardOverlayEnabled}
cardShadowEnabled={cardShadowEnabled}
cardStyle={cardStyle}
onPageChangeStart={onPageChangeStart}
onPageChangeConfirm={onPageChangeConfirm}
onPageChangeCancel={onPageChangeCancel}
gestureResponseDistance={gestureResponseDistance}
headerHeight={headerHeight}
onHeaderHeightChange={this.handleHeaderLayout}
getPreviousRoute={getPreviousRoute}
getFocusedRoute={this.getFocusedRoute}
mode={mode}
headerMode={headerMode}
headerShown={headerShown}
hasAbsoluteHeader={
isFloatHeaderAbsolute && !headerTransparent
}
renderHeader={renderHeader}
renderScene={renderScene}
onOpenRoute={onOpenRoute}
onCloseRoute={onCloseRoute}
onTransitionStart={onTransitionStart}
onTransitionEnd={onTransitionEnd}
gestureEnabled={
index !== 0 && getGesturesEnabled({ route })
}
gestureVelocityImpact={gestureVelocityImpact}
{...transitionConfig}
/>
</MaybeScreen>
);
})}
</MaybeScreenContainer>
{isFloatHeaderAbsolute ? floatingHeader : null}
</React.Fragment>
);
}}
</HeaderShownContext.Consumer>
);
}
}
@@ -600,10 +613,13 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
floating: {
absolute: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
},
floating: {
zIndex: 1,
},
});

View File

@@ -356,14 +356,16 @@ export default class StackView extends React.Component<Props, State> {
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
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<Props, State> {
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<Props, State> {
closingRouteKeys,
} = this.state;
const headerMode =
mode === 'card' && Platform.OS === 'ios' ? 'float' : 'screen';
return (
<>
<GestureHandlerWrapper style={styles.container}>