'use client'; import React from 'react'; import { HeaderBarButtonItemMenuAction, HeaderBarButtonItemWithMenu, ScreenStackHeaderConfigProps, ScreenStackHeaderSubviewProps, } from '../types'; import { Image, ImageProps, NativeSyntheticEvent, Platform, StyleSheet, View, ViewProps, } from 'react-native'; import featureFlags from '../flags'; // Native components import ScreenStackHeaderConfigNativeComponent from '../fabric/ScreenStackHeaderConfigNativeComponent'; import ScreenStackHeaderSubviewNativeComponent, { type NativeProps as ScreenStackHeaderSubviewNativeProps, } from '../fabric/ScreenStackHeaderSubviewNativeComponent'; import { prepareHeaderBarButtonItems } from './helpers/prepareHeaderBarButtonItems'; import { isHeaderBarButtonsAvailableForCurrentPlatform } from '../utils'; import { useEdgeInsetApplication } from './contexts/EdgeInsetApplicationContext'; export const ScreenStackHeaderSubview: React.ComponentType = ScreenStackHeaderSubviewNativeComponent; // Nominal instance type for header config refs. `React.ComponentRef` // declaration emit resolves down to the non-public `ReactNativeElement` class. // An interface stops that resolution at a name this package can emit. export interface ScreenStackHeaderConfigInstance extends React.ComponentRef {} export const ScreenStackHeaderConfig = React.forwardRef< ScreenStackHeaderConfigInstance, ScreenStackHeaderConfigProps >((props, ref) => { const { appliesTopInset, useLegacyBehavior, consumeLeftInset, consumeRightInset, consumeBottomInset, } = useEdgeInsetApplication( !props.hidden, props.disableTopInsetApplication ?? false, props.disableLeftInsetApplication ?? false, props.disableRightInsetApplication ?? false, props.disableBottomInsetApplication ?? false, ); const { headerLeftBarButtonItems, headerRightBarButtonItems } = props; const preparedHeaderLeftBarButtonItems = headerLeftBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerLeftBarButtonItems, 'left') : undefined; const preparedHeaderRightBarButtonItems = headerRightBarButtonItems && isHeaderBarButtonsAvailableForCurrentPlatform ? prepareHeaderBarButtonItems(headerRightBarButtonItems, 'right') : undefined; const hasHeaderBarButtonItems = isHeaderBarButtonsAvailableForCurrentPlatform && (preparedHeaderLeftBarButtonItems?.length || preparedHeaderRightBarButtonItems?.length); // Handle bar button item presses const onPressHeaderBarButtonItem = hasHeaderBarButtonItems ? (event: NativeSyntheticEvent<{ buttonId: string }>) => { const pressedItem = [ ...(preparedHeaderLeftBarButtonItems ?? []), ...(preparedHeaderRightBarButtonItems ?? []), ].find( item => item && 'buttonId' in item && item.buttonId === event.nativeEvent.buttonId, ); if ( pressedItem && pressedItem.type === 'button' && pressedItem.onPress ) { pressedItem.onPress(); } } : undefined; // Handle bar button menu item presses by deep-searching nested menus const onPressHeaderBarButtonMenuItem = hasHeaderBarButtonItems ? (event: NativeSyntheticEvent<{ menuId: string }>) => { // Recursively search menu tree const findInMenu = ( menu: HeaderBarButtonItemWithMenu['menu'], menuId: string, ): HeaderBarButtonItemMenuAction | undefined => { for (const item of menu.items) { if ('items' in item) { // submenu: recurse const found = findInMenu(item, menuId); if (found) { return found; } } else if ('menuId' in item && item.menuId === menuId) { return item; } } return undefined; }; // Check each bar-button item with a menu const allItems = [ ...(preparedHeaderLeftBarButtonItems ?? []), ...(preparedHeaderRightBarButtonItems ?? []), ]; for (const item of allItems) { if (item && item.type === 'menu' && item.menu) { const action = findInMenu(item.menu, event.nativeEvent.menuId); if (action) { action.onPress(); return; } } } } : undefined; return ( ); }); ScreenStackHeaderConfig.displayName = 'ScreenStackHeaderConfig'; export const ScreenStackHeaderBackButtonImage = ( props: ImageProps, ): React.JSX.Element => ( ); export const ScreenStackHeaderRightView = ( props: ScreenStackHeaderSubviewProps & ViewProps, ): React.JSX.Element => { const { style, ...rest } = props; return ( ); }; export const ScreenStackHeaderLeftView = ( props: ScreenStackHeaderSubviewProps & ViewProps, ): React.JSX.Element => { const { style, ...rest } = props; return ( ); }; export const ScreenStackHeaderCenterView = ( props: ViewProps, ): React.JSX.Element => { const { style, ...rest } = props; return ( ); }; export const ScreenStackHeaderSearchBarView = ( props: ViewProps, ): React.JSX.Element => ( ); const styles = StyleSheet.create({ headerSubview: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, headerSubviewCenter: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', flexShrink: 1, }, headerConfig: { position: 'absolute', width: '100%', flexDirection: 'row', justifyContent: 'space-between', // We only want to center align the subviews on iOS. // See https://github.com/software-mansion/react-native-screens/pull/2456 alignItems: Platform.OS === 'ios' ? 'center' : undefined, }, });