Skip to content

Commit fe45bd3

Browse files
authored
feat: add preset to support tab navigator headers (#212)
* feat: add preset to support tab navigator headers * docs: fix file name
1 parent 566c687 commit fe45bd3

12 files changed

+1104
-1339
lines changed

README.md

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# react-navigation-header-buttons
22

3-
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.
3+
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.
44

55
✅ DRY library api
66

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

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

97-
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).
97+
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).
9898

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

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

111111
`HeaderButtons` accepts:
112112

113-
| prop and type | description | note |
114-
| ---------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
115-
| 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). |
116-
| children: ReactNode | whatever you want to render inside | Typically, `Item` or your component that renders `Item`, but it can be any React element. |
117-
| left?: boolean | whether the `HeaderButtons` are on the left from header title | false by default, it only influences styling in a subtle way |
113+
| prop and type | description | note |
114+
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
115+
| 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). |
116+
| children: ReactNode | whatever you want to render inside | Typically, `Item` or your component that renders `Item`, but it can be any React element. |
117+
| left?: boolean | whether the `HeaderButtons` are on the left from header title | false by default, it only influences styling in a subtle way |
118+
| 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 |
118119

119120
### `Item`
120121

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

161162
| prop and type | description | note |
162-
| -------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
163+
| -------------------------------------------- | ----------------------------------------------------------- |-------------------------------------------------------------------------------------------------------------------------|
163164
| 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 |
164-
| style?: ViewStyle | optional styles for overflow button | there are some default styles set, as seen in `OverflowButton.js` |
165+
| style?: ViewStyle | optional styles for overflow button | there are some default styles set, as seen in `OverflowButton.tsx` |
165166
| 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". |
166167
| 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'` |
167168
| accessibilityLabel?: string | | 'More options' by default |
168169
| 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`. |
169170
| children: ReactNode | the overflow items | typically `HiddenItem`s, please read the note below |
171+
| preset?: 'tabHeader' \| 'stackHeader' | | see [props of headerbuttons](#headerbuttons) |
170172
| other props | props passed to the nested `PlatformPressable` | pass eg. `pressColor` to control ripple color on Android |
171173

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

182184
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.
183185
The limitation exists because we need to be able to transform declarative React elements into imperative calls (`ActionSheetIOS.showActionSheetWithOptions` / `UIManager.showPopupMenu`).
184-
If this is a problem for you for some reason, please raise an issue and we'll see what can be done about it.
186+
If this is a problem for you for some reason, please raise an issue, and we'll see what can be done about it.
185187

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

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

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

258-
One of the usual things you may want to do is override the cancel button label on iOS - see [example](example/screens/UsageWithOverflow.tsx).
260+
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).
259261

260262
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.
261263

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

266268
#### How to integrate in your project
267269

268-
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).
270+
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).
269271

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

example/package.json

+12-10
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,30 @@
55
"start": "expo start --dev-client",
66
"android": "expo run:android",
77
"ios": "expo run:ios",
8-
"web": "expo start --web"
8+
"web": "expo start --web",
9+
"fix-deps": "npx expo install --check"
910
},
1011
"dependencies": {
1112
"@expo/react-native-action-sheet": "^4.0.1",
1213
"@react-native-menu/menu": "^0.8.0",
14+
"@react-navigation/bottom-tabs": "^6.5.9",
1315
"@react-navigation/native": "^6.1.6",
1416
"@react-navigation/native-stack": "^6.9.12",
1517
"@react-navigation/stack": "^6.3.16",
16-
"expo": "~48.0.18",
17-
"expo-splash-screen": "~0.18.2",
18-
"expo-status-bar": "~1.4.4",
18+
"expo": "~49.0.13",
19+
"expo-splash-screen": "~0.20.5",
20+
"expo-status-bar": "~1.6.0",
1921
"react": "18.2.0",
2022
"react-dom": "18.2.0",
21-
"react-native": "0.71.8",
22-
"react-native-gesture-handler": "~2.9.0",
23-
"react-native-safe-area-context": "4.5.0",
24-
"react-native-screens": "~3.20.0",
25-
"react-native-web": "~0.18.10"
23+
"react-native": "0.72.5",
24+
"react-native-gesture-handler": "~2.12.0",
25+
"react-native-safe-area-context": "4.6.3",
26+
"react-native-screens": "~3.22.0",
27+
"react-native-web": "~0.19.6"
2628
},
2729
"devDependencies": {
2830
"@babel/core": "^7.20.0",
29-
"@expo/webpack-config": "^18.0.1",
31+
"@expo/webpack-config": "^19.0.0",
3032
"babel-loader": "^8.1.0",
3133
"babel-plugin-module-resolver": "^4.1.0"
3234
},

example/src/App.tsx

+24-4
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ import { NavigationContainer } from '@react-navigation/native';
33
import { HeaderButtonsProvider } from 'react-navigation-header-buttons';
44
// just for custom overflow menu onPress action
55
import { ActionSheetProvider } from '@expo/react-native-action-sheet';
6+
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
7+
68
import { StatusBar } from 'expo-status-bar';
79
import { ThemeContext, ThemeProvider } from './ThemeProvider';
810
import { screens } from './NavTypes';
9-
// import { createStackNavigator } from '@react-navigation/stack';
10-
// const stackType = 'js';
11+
import { TabScreenWithButtons } from './screens/TabScreenWithButtons';
1112
import { createNativeStackNavigator as createStackNavigator } from '@react-navigation/native-stack';
1213
const stackType = 'native';
14+
// import { createStackNavigator } from '@react-navigation/stack';
15+
// const stackType = 'js';
1316

1417
const Stack = createStackNavigator();
1518

1619
const Body = () => {
1720
return (
1821
<>
19-
<StatusBar style="light" backgroundColor="darkgreen" />
2022
{/*@ts-ignore*/}
2123
<Stack.Navigator>
2224
{Object.keys(screens).map((screenName) => {
@@ -39,13 +41,31 @@ const Body = () => {
3941
);
4042
};
4143

44+
const Tab = createBottomTabNavigator();
45+
46+
function TabbedApp() {
47+
return (
48+
//@ts-ignore
49+
<Tab.Navigator screenOptions={{ headerShown: false }}>
50+
<Tab.Screen name="Home" component={Body} />
51+
<Tab.Screen
52+
name="Settings"
53+
component={TabScreenWithButtons}
54+
options={{ headerShown: true }}
55+
/>
56+
</Tab.Navigator>
57+
);
58+
}
59+
4260
const ThemedApp = () => {
4361
const { theme } = useContext(ThemeContext);
4462
return (
4563
<NavigationContainer theme={theme}>
64+
<StatusBar style="light" backgroundColor="darkgreen" />
65+
4666
<ActionSheetProvider>
4767
<HeaderButtonsProvider stackType={stackType}>
48-
<Body />
68+
<TabbedApp />
4969
</HeaderButtonsProvider>
5070
</ActionSheetProvider>
5171
</NavigationContainer>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React from 'react';
2+
import {
3+
Divider,
4+
HeaderButtons,
5+
HiddenItem,
6+
Item,
7+
OverflowMenu,
8+
} from 'react-navigation-header-buttons';
9+
import { MaterialHeaderButton } from '../components/MaterialHeaderButton';
10+
import { ScreenBody } from '../components/ScreenBody';
11+
import { Text } from 'react-native';
12+
import { MaterialIcons } from '@expo/vector-icons';
13+
14+
export type TabScreenWithButtonsProps = any;
15+
export const TabScreenWithButtons = ({
16+
navigation,
17+
}: TabScreenWithButtonsProps) => {
18+
React.useLayoutEffect(() => {
19+
navigation.setOptions({
20+
title: 'Buttons in tab bar',
21+
headerRight: () => (
22+
<HeaderButtons
23+
HeaderButtonComponent={MaterialHeaderButton}
24+
preset={'tabHeader'}
25+
>
26+
<Item
27+
title="search"
28+
iconName="search"
29+
onPress={() => alert('search')}
30+
/>
31+
<OverflowMenu
32+
OverflowIcon={({ color }) => (
33+
<MaterialIcons name="more-horiz" size={23} color={color} />
34+
)}
35+
// preset={'tabHeader'}
36+
>
37+
<HiddenItem title="hidden1" onPress={() => alert('hidden1')} />
38+
<Divider />
39+
</OverflowMenu>
40+
</HeaderButtons>
41+
),
42+
});
43+
}, [navigation]);
44+
45+
return (
46+
<ScreenBody>
47+
<Text>
48+
dummy screen in tab bar, with header managed by the tab navigator
49+
</Text>
50+
</ScreenBody>
51+
);
52+
};

0 commit comments

Comments
 (0)