import React from 'react'; import { Platform, StyleSheet, Animated, StyleProp, TextStyle, ViewStyle, } from 'react-native'; import { ScreenContext, ScreenStack, ScreenStackHeaderBackButtonImage, ScreenStackHeaderCenterView, ScreenStackHeaderConfig, ScreenStackHeaderConfigProps, ScreenStackHeaderLeftView, ScreenStackHeaderRightView, ScreenStackHeaderSearchBarView, SearchBar, StackPresentationTypes, } from 'react-native-screens'; import { createNavigator, SceneView, StackActions, StackRouter, NavigationRouteConfigMap, CreateNavigatorConfig, NavigationStackRouterConfig, NavigationParams, NavigationRoute, NavigationDescriptor, NavigationState, NavigationNavigator, NavigationAction, NavigationProp, NavigationScreenProp, } from 'react-navigation'; import { NativeStackNavigationOptions as NativeStackNavigationOptionsV5 } from './native-stack/types'; import { HeaderBackButton } from 'react-navigation-stack'; import { StackNavigationHelpers, StackNavigationProp, Layout, } from 'react-navigation-stack/src/vendor/types'; const REMOVE_ACTION = 'NativeStackNavigator/REMOVE'; const isAndroid = Platform.OS === 'android'; let didWarn = isAndroid; function renderComponentOrThunk(componentOrThunk: unknown, props: unknown) { if (typeof componentOrThunk === 'function') { return componentOrThunk(props); } return componentOrThunk; } type NativeStackRemoveNavigationAction = { type: typeof REMOVE_ACTION; immediate: boolean; dismissCount: number; key?: string; }; export type NativeStackNavigationProp = StackNavigationProp; export type NativeStackNavigationOptions = StackNavigatorOptions & NativeStackNavigationOptionsV5 & BackButtonProps & { onWillAppear?: () => void; onAppear?: () => void; onWillDisappear?: () => void; onDisappear?: () => void; // these props differ from the ones used in v5 `native-stack`, and we would like to keep the API consistent between versions /** Use `headerHideShadow` to be consistent with v5 `native-stack` */ hideShadow?: boolean; /** Use `headerLargeTitle` to be consistent with v5 `native-stack` */ largeTitle?: boolean; /** Use `headerLargeTitleHideShadow` to be consistent with v5 `native-stack` */ largeTitleHideShadow?: boolean; /** Use `headerTranslucent` to be consistent with v5 `native-stack` */ translucent?: boolean; }; // these are adopted from `stack` navigator type StackNavigatorOptions = { /** This is an option from `stackNavigator` and it hides the header when set to `null`. Use `headerShown` instead to be consistent with v5 `native-stack`. */ header?: React.ComponentType> | null; /** This is an option from `stackNavigator` and it controls the stack presentation along with `mode` prop. Use `stackPresentation` instead to be consistent with v5 `native-stack` */ cardTransparent?: boolean; /** This is an option from `stackNavigator` and it sets stack animation to none when `false` passed. Use `stackAnimation: 'none'` instead to be consistent with v5 `native-stack` */ animationEnabled?: boolean; cardStyle?: StyleProp; }; // these are the props used for rendering back button taken from `react-navigation-stack` type BackButtonProps = { headerBackImage?: (props: { tintColor: string }) => React.ReactNode; headerPressColorAndroid?: string; headerTintColor?: string; backButtonTitle?: string; truncatedBackButtonTitle?: string; backTitleVisible?: boolean; headerBackTitleStyle?: Animated.WithAnimatedValue>; layoutPreset?: Layout; }; type NativeStackDescriptor = NavigationDescriptor< NavigationParams, NativeStackNavigationOptions >; type NativeStackDescriptorMap = { [key: string]: NativeStackDescriptor; }; // these are the props used for rendering back button taken from `react-navigation-stack` type NativeStackNavigationConfig = { /** This is an option from `stackNavigator` and controls the stack presentation along with `cardTransparent` prop. Use `stackPresentation` instead to be consistent with v5 `native-stack` */ mode?: 'modal' | 'containedModal'; /** This is an option from `stackNavigator` and makes the header hide when set to `none`. Use `headerShown` instead to be consistent with v5 `native-stack` */ headerMode?: 'none'; /** This is an option from `stackNavigator` and controls the stack presentation along with `mode` prop. Use `stackPresentation` instead to be consistent with v5 `native-stack` */ transparentCard?: boolean; }; function removeScene( route: NavigationRoute, dismissCount: number, navigation: StackNavigationHelpers ) { navigation.dispatch({ // @ts-ignore special navigation action for native stack type: REMOVE_ACTION, immediate: true, key: route.key, dismissCount, }); } function onAppear( route: NavigationRoute, descriptor: NativeStackDescriptor, navigation: StackNavigationHelpers ) { descriptor.options?.onAppear?.(); navigation.dispatch( StackActions.completeTransition({ toChildKey: route.key, key: navigation.state.key, }) ); } function onFinishTransitioning(navigation: StackNavigationHelpers) { const { routes } = navigation.state; const lastRoute = routes?.length && routes[routes.length - 1]; if (lastRoute) { navigation.dispatch( StackActions.completeTransition({ toChildKey: lastRoute.key, key: navigation.state.key, }) ); } } function renderHeaderConfig( index: number, route: NavigationRoute, descriptor: NativeStackDescriptor, navigationConfig: NativeStackNavigationConfig ) { const { options } = descriptor; const { headerMode } = navigationConfig; const { backButtonInCustomView, direction, disableBackButtonMenu, headerBackTitle, headerBackTitleStyle, headerBackTitleVisible, headerHideBackButton, headerHideShadow, headerLargeStyle, headerLargeTitle, headerLargeTitleHideShadow, headerLargeTitleStyle, headerShown, headerStyle, headerTintColor, headerTitleStyle, headerTopInsetEnabled = true, headerTranslucent, hideShadow, largeTitle, largeTitleHideShadow, title, translucent, } = options; const scene = { index, key: route.key, route, descriptor, }; const headerOptions: ScreenStackHeaderConfigProps = { backButtonInCustomView, backTitle: headerBackTitleVisible === false ? '' : headerBackTitle, backTitleFontFamily: headerBackTitleStyle?.fontFamily, backTitleFontSize: headerBackTitleStyle?.fontSize, color: headerTintColor, direction, disableBackButtonMenu, topInsetEnabled: headerTopInsetEnabled, hideBackButton: headerHideBackButton, hideShadow: headerHideShadow || hideShadow, largeTitle: headerLargeTitle || largeTitle, largeTitleBackgroundColor: headerLargeStyle?.backgroundColor || // @ts-ignore old implementation, will not be present in TS API, but can be used here headerLargeTitleStyle?.backgroundColor, largeTitleColor: headerLargeTitleStyle?.color, largeTitleFontFamily: headerLargeTitleStyle?.fontFamily, largeTitleFontSize: headerLargeTitleStyle?.fontSize, largeTitleFontWeight: headerLargeTitleStyle?.fontWeight, largeTitleHideShadow: largeTitleHideShadow || headerLargeTitleHideShadow, title, titleColor: headerTitleStyle?.color || headerTintColor, titleFontFamily: headerTitleStyle?.fontFamily, titleFontSize: headerTitleStyle?.fontSize, titleFontWeight: headerTitleStyle?.fontWeight, translucent: headerTranslucent || translucent || false, }; const hasHeader = headerShown !== false && headerMode !== 'none' && options.header !== null; if (!hasHeader) { return