Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# react-navigation-header-buttons

This package will help you render buttons in the navigation bar and handle the styling so you don't have to. It mimics the appearance of native navbar buttons and offers a simple and flexible interface for you to interact with.
This package will help you render buttons in the navigation bar and handle the styling, so you don't have to. It mimics the appearance of native navbar buttons and offers a simple but also flexible interface for you to interact with.

✅ DRY library api

Expand Down Expand Up @@ -94,7 +94,7 @@ Version >= 11 requires React Native 0.71 / Expo 48 or newer. Use version 10 if y

1. `yarn add react-navigation-header-buttons`

2. Wrap your root component in `HeaderButtonsProvider` and pass the `stackType` prop (`'native' | 'js'`), as seen in [example's App.tsx](https://github.com/vonovak/react-navigation-header-buttons/tree/master/example/App.tsx).
2. Wrap your root component in `HeaderButtonsProvider` and pass the `stackType` prop (`'native' | 'js'`), as seen in [example's App.tsx](https://github.com/vonovak/react-navigation-header-buttons/blob/master/example/src/App.tsx).

**IMPORTANT** `HeaderButtonsProvider` must be placed as a descendant of `NavigationContainer`, otherwise this library will not receive the correct theme from React Navigation.

Expand All @@ -110,11 +110,12 @@ In particular, it allows setting `IconComponent`, `size` and `color` once so tha

`HeaderButtons` accepts:

| prop and type | description | note |
| ---------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| HeaderButtonComponent?: `ComponentType<HeaderButtonProps>` | component that renders the buttons, `HeaderButton` by default | Typically, you'll want to provide a component that wraps `HeaderButton` provided by this package, as seen in the [quick example](#quick-example). |
| children: ReactNode | whatever you want to render inside | Typically, `Item` or your component that renders `Item`, but it can be any React element. |
| left?: boolean | whether the `HeaderButtons` are on the left from header title | false by default, it only influences styling in a subtle way |
| prop and type | description | note |
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| HeaderButtonComponent?: `ComponentType<HeaderButtonProps>` | component that renders the buttons, `HeaderButton` by default | Typically, you'll want to provide a component that wraps `HeaderButton` provided by this package, as seen in the [quick example](#quick-example). |
| children: ReactNode | whatever you want to render inside | Typically, `Item` or your component that renders `Item`, but it can be any React element. |
| left?: boolean | whether the `HeaderButtons` are on the left from header title | false by default, it only influences styling in a subtle way |
| preset?: 'tabHeader' \| 'stackHeader' | headers are typically rendered in Stack Navigator, however, you can also render them in a Tab Navigator header. Pass 'tabHeader' if button margins are missing. | 'stackHeader' by default |

### `Item`

Expand Down Expand Up @@ -159,14 +160,15 @@ You can also use the [react-native-menu](https://github.com/react-native-menu/me
`OverflowMenu` accepts:

| prop and type | description | note |
| -------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| -------------------------------------------- | ----------------------------------------------------------- |-------------------------------------------------------------------------------------------------------------------------|
| OverflowIcon: ReactElement \| ComponentType | React element or component for the overflow icon | if you provide a component, it will receive `color` prop as seen in example above |
| style?: ViewStyle | optional styles for overflow button | there are some default styles set, as seen in `OverflowButton.js` |
| style?: ViewStyle | optional styles for overflow button | there are some default styles set, as seen in `OverflowButton.tsx` |
| onPress?: (OnOverflowMenuPressParams) => any | function that is called when overflow menu is pressed. | This will override the default handler. Note the default handler offers (limited) customization. See more in "Recipes". |
| testID?: string | testID to locate the overflow button in e2e tests | the default is available under `import { OVERFLOW_BUTTON_TEST_ID } from 'react-navigation-header-buttons/e2e'` |
| accessibilityLabel?: string | | 'More options' by default |
| left?: boolean | whether the `OverflowMenu` is on the left from header title | false by default, it just influences styling. No need to pass this if you already passed it to `HeaderButtons`. |
| children: ReactNode | the overflow items | typically `HiddenItem`s, please read the note below |
| preset?: 'tabHeader' \| 'stackHeader' | | see [props of headerbuttons](#headerbuttons) |
| other props | props passed to the nested `PlatformPressable` | pass eg. `pressColor` to control ripple color on Android |

##### Important note
Expand All @@ -181,7 +183,7 @@ Only `overflowMenuPressHandlerDropdownMenu` supports rendering custom elements,

This limitation may look weird, but it should not really bother you in any way: if you need to have state in your items, lift the state up.
The limitation exists because we need to be able to transform declarative React elements into imperative calls (`ActionSheetIOS.showActionSheetWithOptions` / `UIManager.showPopupMenu`).
If this is a problem for you for some reason, please raise an issue and we'll see what can be done about it.
If this is a problem for you for some reason, please raise an issue, and we'll see what can be done about it.

If `OverflowMenu` contains no valid child elements, nothing will be rendered at all. (No `OverflowIcon`, no wrapper.)

Expand Down Expand Up @@ -255,7 +257,7 @@ You can fully customize what it renders inside of the `PlatformPressable` using

The default handler for overflow menu on iOS is `overflowMenuPressHandlerActionSheet`.

One of the usual things you may want to do is override the cancel button label on iOS - see [example](example/screens/UsageWithOverflow.tsx).
One of the usual things you may want to do is override the cancel button label on iOS - see [example](example/src/screens/UsageWithOverflow.tsx).

You can also use the [react-native-menu](https://github.com/react-native-menu/menu) to show the overflow menu, as seen in the example app.

Expand All @@ -265,7 +267,7 @@ Use the `buttonStyle` prop to set [`textTransform`](https://reactnative.dev/docs

#### How to integrate in your project

This sections covers how you should use the library in your project. Please note that there are numerous [example screens](https://github.com/vonovak/react-navigation-header-buttons/tree/master/example/screens).
This sections covers how you should use the library in your project. Please note that there are numerous [example screens](https://github.com/vonovak/react-navigation-header-buttons/tree/master/example/src/screens).

1 . Define one file where the styling of header buttons is taken care of.

Expand Down
22 changes: 12 additions & 10 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,30 @@
"start": "expo start --dev-client",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web"
"web": "expo start --web",
"fix-deps": "npx expo install --check"
},
"dependencies": {
"@expo/react-native-action-sheet": "^4.0.1",
"@react-native-menu/menu": "^0.8.0",
"@react-navigation/bottom-tabs": "^6.5.9",
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"@react-navigation/stack": "^6.3.16",
"expo": "~48.0.18",
"expo-splash-screen": "~0.18.2",
"expo-status-bar": "~1.4.4",
"expo": "~49.0.13",
"expo-splash-screen": "~0.20.5",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.71.8",
"react-native-gesture-handler": "~2.9.0",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"react-native-web": "~0.18.10"
"react-native": "0.72.5",
"react-native-gesture-handler": "~2.12.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0",
"react-native-web": "~0.19.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@expo/webpack-config": "^18.0.1",
"@expo/webpack-config": "^19.0.0",
"babel-loader": "^8.1.0",
"babel-plugin-module-resolver": "^4.1.0"
},
Expand Down
28 changes: 24 additions & 4 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ import { NavigationContainer } from '@react-navigation/native';
import { HeaderButtonsProvider } from 'react-navigation-header-buttons';
// just for custom overflow menu onPress action
import { ActionSheetProvider } from '@expo/react-native-action-sheet';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

import { StatusBar } from 'expo-status-bar';
import { ThemeContext, ThemeProvider } from './ThemeProvider';
import { screens } from './NavTypes';
// import { createStackNavigator } from '@react-navigation/stack';
// const stackType = 'js';
import { TabScreenWithButtons } from './screens/TabScreenWithButtons';
import { createNativeStackNavigator as createStackNavigator } from '@react-navigation/native-stack';
const stackType = 'native';
// import { createStackNavigator } from '@react-navigation/stack';
// const stackType = 'js';

const Stack = createStackNavigator();

const Body = () => {
return (
<>
<StatusBar style="light" backgroundColor="darkgreen" />
{/*@ts-ignore*/}
<Stack.Navigator>
{Object.keys(screens).map((screenName) => {
Expand All @@ -39,13 +41,31 @@ const Body = () => {
);
};

const Tab = createBottomTabNavigator();

function TabbedApp() {
return (
//@ts-ignore
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen name="Home" component={Body} />
<Tab.Screen
name="Settings"
component={TabScreenWithButtons}
options={{ headerShown: true }}
/>
</Tab.Navigator>
);
}

const ThemedApp = () => {
const { theme } = useContext(ThemeContext);
return (
<NavigationContainer theme={theme}>
<StatusBar style="light" backgroundColor="darkgreen" />

<ActionSheetProvider>
<HeaderButtonsProvider stackType={stackType}>
<Body />
<TabbedApp />
</HeaderButtonsProvider>
</ActionSheetProvider>
</NavigationContainer>
Expand Down
52 changes: 52 additions & 0 deletions example/src/screens/TabScreenWithButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import {
Divider,
HeaderButtons,
HiddenItem,
Item,
OverflowMenu,
} from 'react-navigation-header-buttons';
import { MaterialHeaderButton } from '../components/MaterialHeaderButton';
import { ScreenBody } from '../components/ScreenBody';
import { Text } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';

export type TabScreenWithButtonsProps = any;
export const TabScreenWithButtons = ({
navigation,
}: TabScreenWithButtonsProps) => {
React.useLayoutEffect(() => {
navigation.setOptions({
title: 'Buttons in tab bar',
headerRight: () => (
<HeaderButtons
HeaderButtonComponent={MaterialHeaderButton}
preset={'tabHeader'}
>
<Item
title="search"
iconName="search"
onPress={() => alert('search')}
/>
<OverflowMenu
OverflowIcon={({ color }) => (
<MaterialIcons name="more-horiz" size={23} color={color} />
)}
// preset={'tabHeader'}
>
<HiddenItem title="hidden1" onPress={() => alert('hidden1')} />
<Divider />
</OverflowMenu>
</HeaderButtons>
),
});
}, [navigation]);

return (
<ScreenBody>
<Text>
dummy screen in tab bar, with header managed by the tab navigator
</Text>
</ScreenBody>
);
};
Loading