Skip to content

Commit d13bb6f

Browse files
authored
feat: extract HeaderButtonsProviders (#230)
BREAKING CHANGE: requires RN 73 and new metro config
1 parent b8ed2cb commit d13bb6f

35 files changed

+4282
-4343
lines changed

INSTALL.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Installation & Setup
2+
3+
Version >= 12 requires React Native 0.73 / Expo 50 or newer (because of `unstable_enablePackageExports`). Use version 11 if you're on older version of RN / Expo.
4+
5+
Version >= 11 requires React Native 0.71 / Expo 48 or newer. Use version 10 if you're on older version of RN / Expo.
6+
7+
0. In your `tsconfig.json`, make sure you have [`"moduleResolution": "NodeNext"`](https://www.typescriptlang.org/tsconfig#moduleResolution) set. This is required for TS to see the typings exported via [package.json `exports`](https://reactnative.dev/blog/2023/06/21/package-exports-support).
8+
9+
1. add [`unstable_enablePackageExports`](https://metrobundler.dev/docs/configuration/#unstable_enablepackageexports-experimental) to your metro config (in `metro.config.js`). This field will default to `true` in a future version of RN so don't need to worry about it. This allows us to do some bundle size savings.
10+
11+
```js
12+
// if you use Expo:
13+
const config = getDefaultConfig(__dirname);
14+
// unstable_enablePackageExports: true,
15+
config.resolver.unstable_enablePackageExports = true;
16+
module.exports = config;
17+
18+
// if you use bare React Native:
19+
const config = {
20+
resolver: {
21+
unstable_enablePackageExports: true,
22+
},
23+
};
24+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
25+
```
26+
27+
2. `yarn add react-navigation-header-buttons`
28+
29+
3. Wrap your root component in a `HeaderButtons` Provider 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).
30+
31+
There are 3 providers to choose from. You'll get an actionable warning if you don't use the right one. They are:
32+
33+
- `HeaderButtonsProvider` - the recommended one, which assumes you will use `overflowMenuPressHandlerDropdownMenu` on Android but not iOS (because that's the default behavior that the library ships with). Internally, this translates to `HeaderButtonsProviderDropdownMenu` on Android and `HeaderButtonsProviderPlain` on iOS.
34+
- `HeaderButtonsProviderPlain` - use it if you're not planning to use `overflowMenuPressHandlerDropdownMenu` at all. It will shave a few kB off your bundle and Hermes won't have to parse some code that would not run in the end.
35+
- `HeaderButtonsProviderDropdownMenu` - use it if you're planning to use `overflowMenuPressHandlerDropdownMenu` on all platforms.
36+
37+
Importing: `import { your_chosen_provider } from 'react-navigation-header-buttons/your_chosen_provider'`.
38+
39+
> [!IMPORTANT]
40+
> The Provider must be placed as a descendant of `NavigationContainer`, otherwise this library will not receive the correct theme from React Navigation.

README.md

+46-38
Large diffs are not rendered by default.

babel.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module.exports = {
2-
presets: ['module:metro-react-native-babel-preset'],
2+
presets: ['module:@react-native/babel-preset'],
33
};

example/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,7 @@ yarn-error.log
6969
.metro-health-check*
7070

7171
# @end expo-cli
72+
73+
# generated by tests
74+
requires-android.txt
75+
requires-ios.txt

example/babel.config.js

+10-21
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
1-
const path = require('path');
2-
const pak = require('../package.json');
3-
4-
module.exports = function (api) {
5-
api.cache(true);
6-
7-
return {
8-
presets: ['babel-preset-expo'],
9-
plugins: [
10-
[
11-
'module-resolver',
12-
{
13-
extensions: ['.tsx', '.ts', '.js', '.json'],
14-
alias: {
15-
// For development, we want to alias the library to the source
16-
[pak.name]: path.join(__dirname, '..', pak.source),
17-
},
18-
},
19-
],
20-
],
21-
};
1+
module.exports = {
2+
presets: ['module:@react-native/babel-preset'],
223
};
4+
5+
// module.exports = function (api) {
6+
// api.cache(true);
7+
//
8+
// return {
9+
// presets: ['babel-preset-expo'],
10+
// };
11+
// };

example/metro.config.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const modules = Object.keys({
1212

1313
const defaultConfig = getDefaultConfig(__dirname);
1414

15-
module.exports = {
15+
const config = {
1616
...defaultConfig,
1717

1818
projectRoot: __dirname,
@@ -22,6 +22,7 @@ module.exports = {
2222
// So we block them at the root, and alias them to the versions in example's node_modules
2323
resolver: {
2424
...defaultConfig.resolver,
25+
unstable_enablePackageExports: true,
2526

2627
blockList: exclusionList(
2728
modules.map(
@@ -30,9 +31,15 @@ module.exports = {
3031
)
3132
),
3233

33-
extraNodeModules: modules.reduce((acc, name) => {
34-
acc[name] = path.join(__dirname, 'node_modules', name);
35-
return acc;
36-
}, {}),
34+
extraNodeModules: modules.reduce(
35+
(acc, name) => {
36+
acc[name] = path.join(__dirname, 'node_modules', name);
37+
return acc;
38+
},
39+
{
40+
[pak.name]: root,
41+
}
42+
),
3743
},
3844
};
45+
module.exports = config;

example/package.json

+11-7
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,39 @@
22
"name": "example",
33
"version": "1.0.0",
44
"scripts": {
5-
"start": "expo start --dev-client",
5+
"start": "EXPO_METRO_UNSTABLE_ERRORS=1 expo start --dev-client -c",
66
"android": "expo run:android",
77
"ios": "expo run:ios",
88
"web": "expo start --web",
9-
"fix-deps": "npx expo install --check"
9+
"fix-deps": "npx expo install --check",
10+
"requires-ios": "yarn metro get-dependencies --entry-file App.js --platform ios --output requires-ios.txt",
11+
"requires-android": "yarn metro get-dependencies --entry-file App.js --platform android --output requires-android.txt"
1012
},
1113
"dependencies": {
1214
"@expo/react-native-action-sheet": "^4.0.1",
13-
"@react-native-menu/menu": "^0.8.0",
15+
"@react-native-menu/menu": "^0.9.1",
1416
"@react-navigation/bottom-tabs": "^6.5.9",
1517
"@react-navigation/native": "^6.1.6",
1618
"@react-navigation/native-stack": "^6.9.12",
1719
"@react-navigation/stack": "^6.3.16",
18-
"expo": "~50.0.6",
20+
"expo": "~50.0.17",
1921
"expo-splash-screen": "~0.26.4",
2022
"expo-status-bar": "~1.11.1",
2123
"react": "18.2.0",
2224
"react-dom": "18.2.0",
23-
"react-native": "0.73.4",
25+
"react-native": "0.73.6",
2426
"react-native-gesture-handler": "~2.14.0",
2527
"react-native-safe-area-context": "4.8.2",
2628
"react-native-screens": "~3.29.0",
2729
"react-native-web": "~0.19.6"
2830
},
31+
"resolutions": {
32+
"@types/react": "18.2.79"
33+
},
2934
"devDependencies": {
3035
"@babel/core": "^7.20.0",
3136
"@expo/webpack-config": "~19.0.1",
32-
"babel-loader": "^8.1.0",
33-
"babel-plugin-module-resolver": "^4.1.0"
37+
"babel-loader": "^8.1.0"
3438
},
3539
"private": true
3640
}

example/src/App.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React, { useContext } from 'react';
22
import { NavigationContainer } from '@react-navigation/native';
3-
import { HeaderButtonsProvider } from 'react-navigation-header-buttons';
3+
import { HeaderButtonsProvider } from 'react-navigation-header-buttons/HeaderButtonsProvider';
44
// just for custom overflow menu onPress action
55
import { ActionSheetProvider } from '@expo/react-native-action-sheet';
66
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
7-
87
import { StatusBar } from 'expo-status-bar';
98
import { ThemeContext, ThemeProvider } from './ThemeProvider';
109
import { screens } from './NavTypes';
@@ -46,7 +45,9 @@ const Tab = createBottomTabNavigator();
4645
function TabbedApp() {
4746
return (
4847
//@ts-ignore
49-
<Tab.Navigator screenOptions={{ headerShown: false }}>
48+
<Tab.Navigator
49+
screenOptions={{ headerShown: false, tabBarIcon: () => null }}
50+
>
5051
<Tab.Screen name="Home" component={Body} />
5152
<Tab.Screen
5253
name="Settings"
@@ -62,7 +63,6 @@ const ThemedApp = () => {
6263
return (
6364
<NavigationContainer theme={theme}>
6465
<StatusBar style="light" backgroundColor="darkgreen" />
65-
6666
<ActionSheetProvider>
6767
<HeaderButtonsProvider stackType={stackType}>
6868
<TabbedApp />

example/src/screens/UsageDifferentFontFamilies.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function UsageDifferentFontFamilies({
2323
<Item
2424
title="settings-ion"
2525
IconComponent={Ionicons}
26-
iconName="ios-settings"
26+
iconName="settings"
2727
onPress={() => alert('ionicons settings')}
2828
/>
2929
<Item

example/src/screens/UsageDisabled.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function UsageDisabled({ navigation }: ScreenProps<'UsageDisabled'>) {
2828
<HeaderButtons HeaderButtonComponent={DisableableHeaderButton}>
2929
<Item
3030
title="search"
31-
iconName="ios-search"
31+
iconName="search"
3232
onPress={() => alert('search')}
3333
disabled
3434
/>

example/src/screens/UsageNativeMenu.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function UsageNativeMenu({
7878

7979
return (
8080
<ScreenBody>
81-
<View style={{ height: 500 }} />
81+
<View style={{ height: 250 }} />
8282

8383
<MenuView
8484
title="Menu Title"

example/src/screens/UsageWithIcons.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import {
55
Item,
66
HiddenItem,
77
OverflowMenu,
8-
Divider,
98
ItemProps,
109
HiddenItemProps,
10+
Divider,
1111
} from 'react-navigation-header-buttons';
1212
import type { ScreenProps } from '../NavTypes';
1313
import { MaterialHeaderButton } from '../components/MaterialHeaderButton';

example/src/screens/UsageWithOverflowComplex.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { View, Text } from 'react-native';
44
import {
55
HiddenItem,
66
OverflowMenu,
7-
Divider,
87
overflowMenuPressHandlerActionSheet,
98
overflowMenuPressHandlerPopupMenu,
109
overflowMenuPressHandlerDropdownMenu,
1110
HiddenItemProps,
1211
OnOverflowMenuPressParams,
1312
useOverflowMenu,
13+
Divider,
1414
} from 'react-navigation-header-buttons';
1515
import type { ScreenProps } from '../NavTypes';
1616
import { Button } from '../components/PaddedButton';

0 commit comments

Comments
 (0)