Compare commits

..

52 Commits

Author SHA1 Message Date
satyajit.happy
de2b6d8715 chore: publish
- @react-navigation/compat@5.0.0-alpha.9
 - @react-navigation/core@5.0.0-alpha.17
 - @react-navigation/drawer@5.0.0-alpha.16
 - @react-navigation/example@5.0.0-alpha.15
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.15
 - @react-navigation/native-stack@5.0.0-alpha.4
 - @react-navigation/native@5.0.0-alpha.13
 - @react-navigation/stack@5.0.0-alpha.28
2019-10-22 10:58:35 +02:00
Satyajit Sahoo
f22abb726c fix: don't fire onOpen when screen is unmounting (#137)
I can't think of a scenario a screen would unmount when opening.
So it's probably a safe-bet to always call onClose.

Fixes #136
2019-10-22 09:26:59 +02:00
satyajit.happy
031c4d2378 fix: don't keep unfocused header backgrounds visible 2019-10-22 04:54:38 +02:00
satyajit.happy
2b5955efbe refactor: use Record type for objects 2019-10-22 00:53:11 +02:00
satyajit.happy
70f7e7a7c0 ci: update CI config 2019-10-21 15:34:02 +02:00
David
6cf1a041b2 feat(native-stack): add support for large title attributes (#135)
Co-Authored-By: Satyajit Sahoo <satyajit.happy@gmail.com>
2019-10-21 15:25:50 +02:00
osdnk
0d8cdc8a27 fix: navigation drawer sometimes not closing when pressed outside 2019-10-21 11:26:30 +02:00
satyajit.happy
2680b461a2 chore: publish
- @react-navigation/native-stack@5.0.0-alpha.3
2019-10-18 19:16:33 +02:00
satyajit.happy
fb726eede3 fix: remove top margin when header is hidden in native stack. fixes #131 2019-10-18 19:15:43 +02:00
satyajit.happy
3aaf6eb648 chore: publish
- @react-navigation/core@5.0.0-alpha.16
 - @react-navigation/native-stack@5.0.0-alpha.2
 - @react-navigation/stack@5.0.0-alpha.27
2019-10-18 16:44:00 +02:00
Janic Duplessis
477c08858d fix: fix header font size config in native stack (#128)
Oups copy paste mistake :o
2019-10-18 16:43:04 +02:00
satyajit.happy
300791ab49 feat: add an option to override safe area insets 2019-10-18 16:20:53 +02:00
satyajit.happy
3e92e22941 fix: rehydrate state before using it 2019-10-18 16:06:21 +02:00
Satyajit Sahoo
a543f1bfc3 feat: make it easier to navigate to a specific route in navigator (#114)
We now use the `params` of the route to determine `initialRouteName` and `initialParams` of a navigator.

So you can do something like this:

```js
navigation.push('Auth', { name: 'Login', params: { token 'xxx' } })
```

This will navigate to the `Login` screen inside the `Auth` navigator.

Closes #90
2019-10-18 15:16:19 +02:00
satyajit.happy
cd5f355bd0 chore: publish
- @react-navigation/drawer@5.0.0-alpha.15
 - @react-navigation/example@5.0.0-alpha.14
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.14
 - @react-navigation/stack@5.0.0-alpha.26
2019-10-18 01:57:14 +02:00
satyajit.happy
cab616069f fix: fix passing content options in drawer 2019-10-18 01:56:04 +02:00
satyajit.happy
f90e00cc93 chore: update paper so types work 2019-10-17 22:52:48 +02:00
satyajit.happy
731cf7d5b1 fix: fix incorrect type 2019-10-16 22:59:45 +02:00
Sirui Li
c6d0c19b49 fix: don't fade incoming background when fading header (#127) 2019-10-16 22:58:31 +02:00
satyajit.happy
442b95d9e4 fix: use header height from style if specified 2019-10-16 15:02:39 +02:00
osdnk
01277575f2 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.14
 - @react-navigation/compat@5.0.0-alpha.8
 - @react-navigation/core@5.0.0-alpha.15
 - @react-navigation/drawer@5.0.0-alpha.14
 - @react-navigation/example@5.0.0-alpha.13
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.13
 - @react-navigation/material-top-tabs@5.0.0-alpha.12
 - @react-navigation/native-stack@5.0.0-alpha.1
 - @react-navigation/native@5.0.0-alpha.12
 - @react-navigation/stack@5.0.0-alpha.25
2019-10-15 16:08:48 +02:00
Michał Osadnik
386d1c0888 fix: block GH interactions in Native Stack example (#126) 2019-10-15 14:28:39 +02:00
satyajit.happy
9d9fe31f02 fix: don't ignore descriptors change 2019-10-15 14:17:49 +02:00
satyajit.happy
0a5fb3e268 fix: fix content component not rendering in drawer 2019-10-11 18:20:56 +02:00
satyajit.happy
1d7a37cd4a chore: fix minor typo 2019-10-11 18:05:32 +02:00
satyajit.happy
2b57702a62 fix: add flex: 1 to drawer content 2019-10-11 17:49:15 +02:00
satyajit.happy
c7da1e4145 fix: increase hitSlop of back button on Android 2019-10-11 13:39:29 +02:00
satyajit.happy
6a0ca90873 feat: add a headerTitleAlign option to center or left align title 2019-10-11 13:38:53 +02:00
osdnk
e9a1cfcec3 chore: bump screens 2019-10-11 13:26:52 +02:00
satyajit.happy
b71f4e52a3 fix: update supported options for native stack 2019-10-11 13:12:39 +02:00
satyajit.happy
9ce8ec59fb refactor: refresh drawer according to latest material design guidelines 2019-10-11 00:54:22 +02:00
satyajit.happy
d685e78fa9 refactor: change tintColor argument to just color 2019-10-11 00:40:39 +02:00
satyajit.happy
7a901af5b5 fix: make it possible to run the example on web 2019-10-11 00:28:38 +02:00
Michał Osadnik
ba3f718ab3 feat: initial version of native stack (#102) 2019-10-10 23:39:04 +02:00
satyajit.happy
42beb660ca fix: improve keyboard handling with bottom tab bar 2019-10-10 14:50:42 +02:00
Satyajit Sahoo
e789846692 fix: make modal presentation mode fullscreen on landscape (#124) 2019-10-08 15:50:52 +02:00
osdnk
b32cda2446 fix: interpolation in iOS modal presentation 2019-10-08 13:01:15 +02:00
satyajit.happy
708dde0e47 feat: export TransitionSpecs 2019-10-07 17:43:37 +02:00
satyajit.happy
5303e8ffb5 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.13
 - @react-navigation/compat@5.0.0-alpha.7
 - @react-navigation/core@5.0.0-alpha.14
 - @react-navigation/drawer@5.0.0-alpha.13
 - @react-navigation/example@5.0.0-alpha.12
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.12
 - @react-navigation/material-top-tabs@5.0.0-alpha.11
 - @react-navigation/native@5.0.0-alpha.11
 - @react-navigation/stack@5.0.0-alpha.24
2019-10-06 16:44:17 +02:00
satyajit.happy
ba6b6ae025 feat: drop header: null in favor of more explitit headerShown option 2019-10-06 15:56:30 +02:00
satyajit.happy
16079d1050 fix: actually expose gestureVelocityImpact in the public API 2019-10-06 04:17:49 +02:00
satyajit.happy
b4a76814c6 fix: use next screen's animation when not focused. fixes #87 2019-10-06 04:13:13 +02:00
Michał Osadnik
8294efc8f4 feat: add gestureVelocityImpact as a prop for stack (#123) 2019-10-06 00:05:42 +02:00
Michał Osadnik
a27ade8881 fix: handling vertical gesture in RTL (#122) 2019-10-06 00:00:43 +02:00
satyajit.happy
615b523d26 fix: don't recompute if routes didn't change 2019-10-05 22:50:09 +02:00
satyajit.happy
070c46ba64 chore: fix react and react-native versions 2019-10-04 14:36:49 +02:00
satyajit.happy
d8394cf919 chore: publish
- @react-navigation/example@5.0.0-alpha.11
 - @react-navigation/stack@5.0.0-alpha.23
2019-10-04 00:54:40 +02:00
satyajit.happy
a7c4a4d7cd fix: fix vertical gesture 2019-10-04 00:53:26 +02:00
satyajit.happy
282b465ae1 chore: add example for modal presentation 2019-10-04 00:12:56 +02:00
satyajit.happy
6f5f4b7d35 fix: fix passing insets to interpolator 2019-10-04 00:01:45 +02:00
satyajit.happy
d75915d1f3 chore: publish
- @react-navigation/bottom-tabs@5.0.0-alpha.12
 - @react-navigation/compat@5.0.0-alpha.6
 - @react-navigation/core@5.0.0-alpha.13
 - @react-navigation/drawer@5.0.0-alpha.12
 - @react-navigation/example@5.0.0-alpha.10
 - @react-navigation/material-bottom-tabs@5.0.0-alpha.11
 - @react-navigation/material-top-tabs@5.0.0-alpha.10
 - @react-navigation/native@5.0.0-alpha.10
 - @react-navigation/stack@5.0.0-alpha.22
2019-10-03 21:33:06 +02:00
satyajit.happy
832ed882bc refactor: use react-native-safe-area-context 2019-10-03 21:31:09 +02:00
103 changed files with 3560 additions and 1899 deletions

View File

@@ -14,11 +14,11 @@ jobs:
at: ~/project at: ~/project
- restore_cache: - restore_cache:
keys: keys:
- v1-dependencies-{{ checksum "package.json" }} - v1-dependencies-{{ checksum "yarn.lock" }}
- v1-dependencies- - v1-dependencies-
- run: yarn install - run: yarn install --frozen-lockfile
- save_cache: - save_cache:
key: v1-dependencies-{{ checksum "package.json" }} key: v1-dependencies-{{ checksum "yarn.lock" }}
paths: node_modules paths: node_modules
- persist_to_workspace: - persist_to_workspace:
root: . root: .

View File

@@ -8,6 +8,7 @@
"@react-navigation/routers", "@react-navigation/routers",
"@react-navigation/compat", "@react-navigation/compat",
"@react-navigation/stack", "@react-navigation/stack",
"@react-navigation/native-stack",
"@react-navigation/drawer", "@react-navigation/drawer",
"@react-navigation/bottom-tabs", "@react-navigation/bottom-tabs",
"@react-navigation/material-top-tabs", "@react-navigation/material-top-tabs",

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@
.expo .expo
.gradle .gradle
local.properties
/coverage/ /coverage/
/types/ /types/
/dist/ /dist/

View File

@@ -41,9 +41,8 @@
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"resolutions": { "resolutions": {
"react": "16.8.3", "react": "~16.8.3",
"react-native": "0.59.10", "react-native": "~0.59.10"
"react-native-safe-area-view": "0.14.7"
}, },
"husky": { "husky": {
"hooks": { "hooks": {

View File

@@ -3,6 +3,39 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.14](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.13...@react-navigation/bottom-tabs@5.0.0-alpha.14) (2019-10-15)
### Bug Fixes
* improve keyboard handling with bottom tab bar ([42beb66](https://github.com/react-navigation/navigation-ex/commit/42beb66))
* make it possible to run the example on web ([7a901af](https://github.com/react-navigation/navigation-ex/commit/7a901af))
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.12...@react-navigation/bottom-tabs@5.0.0-alpha.13) (2019-10-06)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.11...@react-navigation/bottom-tabs@5.0.0-alpha.12) (2019-10-03)
**Note:** Version bump only for package @react-navigation/bottom-tabs
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.10...@react-navigation/bottom-tabs@5.0.0-alpha.11) (2019-10-03) # [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/bottom-tabs@5.0.0-alpha.10...@react-navigation/bottom-tabs@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/bottom-tabs **Note:** Version bump only for package @react-navigation/bottom-tabs

View File

@@ -10,11 +10,40 @@ Open a Terminal in your project's folder and run,
yarn add @react-navigation/core @react-navigation/bottom-tabs yarn add @react-navigation/core @react-navigation/bottom-tabs
``` ```
Now we need to install [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
```sh
expo install react-native-safe-area-context
```
If you are not using Expo, run the following:
```sh
yarn add react-native-safe-area-context
```
If you are using Expo, you are done. Otherwise, continue to the next steps.
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
```sh
cd ios
pod install
cd ..
```
## Usage ## Usage
```js ```js
import { MaterialCommunityIcons } from 'react-native-vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const getTabBarIcon = name => ({ color, size }) => (
<MaterialCommunityIcons name={name} color={color} size={size} />
);
const BottomTabs = createBottomTabNavigator(); const BottomTabs = createBottomTabNavigator();
export default function App() { export default function App() {
@@ -25,7 +54,7 @@ export default function App() {
component={Article} component={Article}
options={{ options={{
tabBarLabel: 'Article', tabBarLabel: 'Article',
tabBarIcon: 'chrome-reader-mode', tabBarIcon: getTabBarIcon('file-document-box'),
}} }}
/> />
<BottomTabs.Screen <BottomTabs.Screen
@@ -33,7 +62,7 @@ export default function App() {
component={Chat} component={Chat}
options={{ options={{
tabBarLabel: 'Chat', tabBarLabel: 'Chat',
tabBarIcon: 'chat-bubble', tabBarIcon: getTabBarIcon('message-reply'),
}} }}
/> />
<BottomTabs.Screen <BottomTabs.Screen
@@ -41,7 +70,7 @@ export default function App() {
component={Contacts} component={Contacts}
options={{ options={{
tabBarLabel: 'Contacts', tabBarLabel: 'Contacts',
tabBarIcon: 'contacts', tabBarIcon: getTabBarIcon('contacts'),
}} }}
/> />
</BottomTabs.Navigator> </BottomTabs.Navigator>

View File

@@ -10,7 +10,7 @@
"android", "android",
"tab" "tab"
], ],
"version": "5.0.0-alpha.11", "version": "5.0.0-alpha.14",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -33,22 +33,23 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.9", "@react-navigation/routers": "^5.0.0-alpha.9"
"react-native-safe-area-view": "^0.14.7"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.7.0", "@react-native-community/bob": "^0.7.0",
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.17",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "16.8.3", "react": "~16.8.3",
"react-native": "0.59.10", "react-native": "~0.59.10",
"react-native-safe-area-context": "^0.3.6",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"peerDependencies": { "peerDependencies": {
"@react-navigation/core": "^5.0.0-alpha.0", "@react-navigation/core": "^5.0.0-alpha.0",
"react": "*", "react": "*",
"react-native": "*" "react-native": "*",
"react-native-safe-area-context": "^0.3.6"
}, },
"@react-native-community/bob": { "@react-native-community/bob": {
"source": "src", "source": "src",

View File

@@ -6,7 +6,6 @@ import {
TextStyle, TextStyle,
ViewStyle, ViewStyle,
} from 'react-native'; } from 'react-native';
import SafeAreaView from 'react-native-safe-area-view';
import { import {
NavigationHelpers, NavigationHelpers,
NavigationProp, NavigationProp,
@@ -67,26 +66,26 @@ export type BottomTabNavigationOptions = {
/** /**
* Title string of a tab displayed in the tab bar or React Element * Title string of a tab displayed in the tab bar or React Element
* or a function that given { focused: boolean, tintColor: string } returns a React.Node, to display in tab bar. * or a function that given { focused: boolean, color: string } returns a React.Node, to display in tab bar.
* When undefined, scene title is used. To hide, see tabBarOptions.showLabel in the previous section. * When undefined, scene title is used. To hide, see tabBarOptions.showLabel in the previous section.
*/ */
tabBarLabel?: tabBarLabel?:
| React.ReactNode | React.ReactNode
| ((props: { | ((props: {
focused: boolean; focused: boolean;
tintColor: string; color: string;
horizontal: boolean; size: number;
}) => React.ReactNode); }) => React.ReactNode);
/** /**
* React Element or a function that given { focused: boolean, tintColor: string } returns a React.Node, to display in the tab bar. * React Element or a function that given { focused: boolean, color: string } returns a React.Node, to display in the tab bar.
*/ */
tabBarIcon?: tabBarIcon?:
| React.ReactNode | React.ReactNode
| ((props: { | ((props: {
focused: boolean; focused: boolean;
tintColor: string; color: string;
horizontal: boolean; size: number;
}) => React.ReactNode); }) => React.ReactNode);
/** /**
@@ -140,7 +139,7 @@ export type BottomTabNavigationConfig = {
export type BottomTabBarOptions = { export type BottomTabBarOptions = {
/** /**
* Whether the tab bar gets hidden when the keyboard is shown. * Whether the tab bar gets hidden when the keyboard is shown. Defaults to `false`.
*/ */
keyboardHidesTabBar?: boolean; keyboardHidesTabBar?: boolean;
/** /**
@@ -221,7 +220,7 @@ export type BottomTabBarProps = BottomTabBarOptions & {
}) => }) =>
| ((scene: { | ((scene: {
focused: boolean; focused: boolean;
tintColor: string; color: string;
orientation: 'horizontal' | 'vertical'; orientation: 'horizontal' | 'vertical';
}) => React.ReactNode | undefined) }) => React.ReactNode | undefined)
| React.ReactNode; | React.ReactNode;
@@ -229,10 +228,9 @@ export type BottomTabBarProps = BottomTabBarOptions & {
renderIcon: (props: { renderIcon: (props: {
route: Route<string>; route: Route<string>;
focused: boolean; focused: boolean;
tintColor: string; color: string;
horizontal: boolean; size: number;
}) => React.ReactNode; }) => React.ReactNode;
activeTintColor: string; activeTintColor: string;
inactiveTintColor: string; inactiveTintColor: string;
safeAreaInset?: React.ComponentProps<typeof SafeAreaView>['forceInset'];
}; };

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { import {
View,
Animated, Animated,
StyleSheet, StyleSheet,
Keyboard, Keyboard,
@@ -8,8 +9,8 @@ import {
ScaledSize, ScaledSize,
Dimensions, Dimensions,
} from 'react-native'; } from 'react-native';
import SafeAreaView from 'react-native-safe-area-view';
import { Route, NavigationContext } from '@react-navigation/core'; import { Route, NavigationContext } from '@react-navigation/core';
import { SafeAreaConsumer } from 'react-native-safe-area-context';
import TabBarIcon from './TabBarIcon'; import TabBarIcon from './TabBarIcon';
import TouchableWithoutFeedbackWrapper from './TouchableWithoutFeedbackWrapper'; import TouchableWithoutFeedbackWrapper from './TouchableWithoutFeedbackWrapper';
@@ -22,9 +23,7 @@ type State = {
visible: Animated.Value; visible: Animated.Value;
}; };
type Props = BottomTabBarProps & { type Props = BottomTabBarProps;
safeAreaInset: React.ComponentProps<typeof SafeAreaView>['forceInset'];
};
const majorVersion = parseInt(Platform.Version as string, 10); const majorVersion = parseInt(Platform.Version as string, 10);
const isIos = Platform.OS === 'ios'; const isIos = Platform.OS === 'ios';
@@ -34,7 +33,7 @@ const DEFAULT_MAX_TAB_ITEM_WIDTH = 125;
export default class TabBarBottom extends React.Component<Props, State> { export default class TabBarBottom extends React.Component<Props, State> {
static defaultProps = { static defaultProps = {
keyboardHidesTabBar: true, keyboardHidesTabBar: false,
activeTintColor: '#007AFF', activeTintColor: '#007AFF',
activeBackgroundColor: 'transparent', activeBackgroundColor: 'transparent',
inactiveTintColor: '#8E8E93', inactiveTintColor: '#8E8E93',
@@ -43,9 +42,6 @@ export default class TabBarBottom extends React.Component<Props, State> {
showIcon: true, showIcon: true,
allowFontScaling: true, allowFontScaling: true,
adaptive: isIOS11, adaptive: isIOS11,
safeAreaInset: { bottom: 'always', top: 'never' } as React.ComponentProps<
typeof SafeAreaView
>['forceInset'],
}; };
state = { state = {
@@ -87,7 +83,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
this.setState({ keyboard: true }, () => this.setState({ keyboard: true }, () =>
Animated.timing(this.state.visible, { Animated.timing(this.state.visible, {
toValue: 0, toValue: 0,
duration: 150, duration: 200,
useNativeDriver: true, useNativeDriver: true,
}).start() }).start()
); );
@@ -95,10 +91,12 @@ export default class TabBarBottom extends React.Component<Props, State> {
private handleKeyboardHide = () => private handleKeyboardHide = () =>
Animated.timing(this.state.visible, { Animated.timing(this.state.visible, {
toValue: 1, toValue: 1,
duration: 100, duration: 250,
useNativeDriver: true, useNativeDriver: true,
}).start(() => { }).start(({ finished }) => {
this.setState({ keyboard: false }); if (finished) {
this.setState({ keyboard: false });
}
}); });
private handleLayout = (e: LayoutChangeEvent) => { private handleLayout = (e: LayoutChangeEvent) => {
@@ -139,7 +137,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
const label = this.props.getLabelText({ route }); const label = this.props.getLabelText({ route });
const horizontal = this.shouldUseHorizontalLabels(); const horizontal = this.shouldUseHorizontalLabels();
const tintColor = focused ? activeTintColor : inactiveTintColor; const color = focused ? activeTintColor : inactiveTintColor;
if (typeof label === 'string') { if (typeof label === 'string') {
return ( return (
@@ -147,7 +145,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
numberOfLines={1} numberOfLines={1}
style={[ style={[
styles.label, styles.label,
{ color: tintColor }, { color },
showIcon && horizontal ? styles.labelBeside : styles.labelBeneath, showIcon && horizontal ? styles.labelBeside : styles.labelBeneath,
labelStyle, labelStyle,
]} ]}
@@ -161,7 +159,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
if (typeof label === 'function') { if (typeof label === 'function') {
return label({ return label({
focused, focused,
tintColor, color,
orientation: horizontal ? 'horizontal' : 'vertical', orientation: horizontal ? 'horizontal' : 'vertical',
}); });
} }
@@ -196,7 +194,7 @@ export default class TabBarBottom extends React.Component<Props, State> {
return ( return (
<TabBarIcon <TabBarIcon
route={route} route={route}
horizontal={horizontal} size={horizontal ? 17 : 24}
activeOpacity={activeOpacity} activeOpacity={activeOpacity}
inactiveOpacity={inactiveOpacity} inactiveOpacity={inactiveOpacity}
activeTintColor={activeTintColor} activeTintColor={activeTintColor}
@@ -270,99 +268,102 @@ export default class TabBarBottom extends React.Component<Props, State> {
getAccessibilityStates, getAccessibilityStates,
getButtonComponent, getButtonComponent,
getTestID, getTestID,
safeAreaInset,
style, style,
tabStyle, tabStyle,
} = this.props; } = this.props;
const { routes } = state; const { routes } = state;
const tabBarStyle = [
styles.tabBar,
// @ts-ignore
this.shouldUseHorizontalLabels() && !Platform.isPad
? styles.tabBarCompact
: styles.tabBarRegular,
style,
];
return ( return (
<Animated.View <SafeAreaConsumer>
style={[ {insets => (
styles.container, <Animated.View
keyboardHidesTabBar style={[
? { styles.tabBar,
// When the keyboard is shown, slide down the tab bar keyboardHidesTabBar
transform: [ ? {
{ // When the keyboard is shown, slide down the tab bar
translateY: this.state.visible.interpolate({ transform: [
inputRange: [0, 1], {
outputRange: [this.state.layout.height, 0], translateY: this.state.visible.interpolate({
}), inputRange: [0, 1],
}, outputRange: [this.state.layout.height, 0],
], }),
// Absolutely position the tab bar so that the content is below it },
// This is needed to avoid gap at bottom when the tab bar is hidden ],
position: this.state.keyboard ? 'absolute' : null, // Absolutely position the tab bar so that the content is below it
} // This is needed to avoid gap at bottom when the tab bar is hidden
: null, position: this.state.keyboard ? 'absolute' : null,
]} }
pointerEvents={ : null,
keyboardHidesTabBar && this.state.keyboard ? 'none' : 'auto' {
} height:
onLayout={this.handleLayout} // @ts-ignore
> (this.shouldUseHorizontalLabels() && !Platform.isPad
<SafeAreaView style={tabBarStyle} forceInset={safeAreaInset}> ? COMPACT_HEIGHT
{routes.map((route, index) => { : DEFAULT_HEIGHT) + (insets ? insets.bottom : 0),
const focused = index === state.index; paddingBottom: insets ? insets.bottom : 0,
const scene = { route, focused }; },
const accessibilityLabel = getAccessibilityLabel({ style,
route, ]}
}); pointerEvents={
keyboardHidesTabBar && this.state.keyboard ? 'none' : 'auto'
}
>
<View style={styles.content} onLayout={this.handleLayout}>
{routes.map((route, index) => {
const focused = index === state.index;
const scene = { route, focused };
const accessibilityLabel = getAccessibilityLabel({
route,
});
const accessibilityRole = getAccessibilityRole({ const accessibilityRole = getAccessibilityRole({
route, route,
}); });
const accessibilityStates = getAccessibilityStates(scene); const accessibilityStates = getAccessibilityStates(scene);
const testID = getTestID({ route }); const testID = getTestID({ route });
const backgroundColor = focused const backgroundColor = focused
? activeBackgroundColor ? activeBackgroundColor
: inactiveBackgroundColor; : inactiveBackgroundColor;
const ButtonComponent = const ButtonComponent =
getButtonComponent({ route }) || TouchableWithoutFeedbackWrapper; getButtonComponent({ route }) ||
TouchableWithoutFeedbackWrapper;
return ( return (
<NavigationContext.Provider <NavigationContext.Provider
key={route.key} key={route.key}
value={descriptors[route.key].navigation} value={descriptors[route.key].navigation}
> >
<ButtonComponent <ButtonComponent
onPress={() => onTabPress({ route })} onPress={() => onTabPress({ route })}
onLongPress={() => onTabLongPress({ route })} onLongPress={() => onTabLongPress({ route })}
testID={testID} testID={testID}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
accessibilityRole={accessibilityRole} accessibilityRole={accessibilityRole}
accessibilityStates={accessibilityStates} accessibilityStates={accessibilityStates}
style={[ style={[
styles.tab, styles.tab,
{ backgroundColor }, { backgroundColor },
this.shouldUseHorizontalLabels() this.shouldUseHorizontalLabels()
? styles.tabLandscape ? styles.tabLandscape
: styles.tabPortrait, : styles.tabPortrait,
tabStyle, tabStyle,
]} ]}
> >
{this.renderIcon(scene)} {this.renderIcon(scene)}
{this.renderLabel(scene)} {this.renderLabel(scene)}
</ButtonComponent> </ButtonComponent>
</NavigationContext.Provider> </NavigationContext.Provider>
); );
})} })}
</SafeAreaView> </View>
</Animated.View> </Animated.View>
)}
</SafeAreaConsumer>
); );
} }
} }
@@ -372,22 +373,17 @@ const COMPACT_HEIGHT = 29;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
tabBar: { tabBar: {
backgroundColor: '#fff',
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: 'rgba(0, 0, 0, .3)',
flexDirection: 'row',
},
container: {
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
backgroundColor: '#fff',
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: 'rgba(0, 0, 0, .3)',
elevation: 8, elevation: 8,
}, },
tabBarCompact: { content: {
height: COMPACT_HEIGHT, flex: 1,
}, flexDirection: 'row',
tabBarRegular: {
height: DEFAULT_HEIGHT,
}, },
tab: { tab: {
flex: 1, flex: 1,

View File

@@ -9,6 +9,7 @@ import { Route, CommonActions } from '@react-navigation/core';
import { TabNavigationState } from '@react-navigation/routers'; import { TabNavigationState } from '@react-navigation/routers';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { ScreenContainer } from 'react-native-screens'; import { ScreenContainer } from 'react-native-screens';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import BottomTabBar from './BottomTabBar'; import BottomTabBar from './BottomTabBar';
import { import {
@@ -63,13 +64,13 @@ export default class BottomTabView extends React.Component<Props, State> {
private renderIcon = ({ private renderIcon = ({
route, route,
focused, focused,
tintColor, color,
horizontal, size,
}: { }: {
route: Route<string>; route: Route<string>;
focused: boolean; focused: boolean;
tintColor: string; color: string;
horizontal: boolean; size: number;
}) => { }) => {
const { descriptors } = this.props; const { descriptors } = this.props;
const descriptor = descriptors[route.key]; const descriptor = descriptors[route.key];
@@ -77,7 +78,7 @@ export default class BottomTabView extends React.Component<Props, State> {
if (options.tabBarIcon) { if (options.tabBarIcon) {
return typeof options.tabBarIcon === 'function' return typeof options.tabBarIcon === 'function'
? options.tabBarIcon({ focused, tintColor, horizontal }) ? options.tabBarIcon({ focused, color, size })
: options.tabBarIcon; : options.tabBarIcon;
} }
@@ -198,29 +199,31 @@ export default class BottomTabView extends React.Component<Props, State> {
const { loaded } = this.state; const { loaded } = this.state;
return ( return (
<View style={styles.container}> <SafeAreaProvider>
<ScreenContainer style={styles.pages}> <View style={styles.container}>
{routes.map((route, index) => { <ScreenContainer style={styles.pages}>
if (lazy && !loaded.includes(index)) { {routes.map((route, index) => {
// Don't render a screen if we've never navigated to it if (lazy && !loaded.includes(index)) {
return null; // Don't render a screen if we've never navigated to it
} return null;
}
const isFocused = state.index === index; const isFocused = state.index === index;
return ( return (
<ResourceSavingScene <ResourceSavingScene
key={route.key} key={route.key}
style={StyleSheet.absoluteFill} style={StyleSheet.absoluteFill}
isVisible={isFocused} isVisible={isFocused}
> >
{descriptors[route.key].render()} {descriptors[route.key].render()}
</ResourceSavingScene> </ResourceSavingScene>
); );
})} })}
</ScreenContainer> </ScreenContainer>
{this.renderTabBar()} {this.renderTabBar()}
</View> </View>
</SafeAreaProvider>
); );
} }
} }

View File

@@ -4,7 +4,7 @@ import { Route } from '@react-navigation/core';
type Props = { type Props = {
route: Route<string>; route: Route<string>;
horizontal: boolean; size: number;
activeOpacity: number; activeOpacity: number;
inactiveOpacity: number; inactiveOpacity: number;
activeTintColor: string; activeTintColor: string;
@@ -12,8 +12,8 @@ type Props = {
renderIcon: (props: { renderIcon: (props: {
route: Route<string>; route: Route<string>;
focused: boolean; focused: boolean;
tintColor: string; color: string;
horizontal: boolean; size: number;
}) => React.ReactNode; }) => React.ReactNode;
style: StyleProp<ViewStyle>; style: StyleProp<ViewStyle>;
}; };
@@ -25,7 +25,7 @@ export default function TabBarIcon({
activeTintColor, activeTintColor,
inactiveTintColor, inactiveTintColor,
renderIcon, renderIcon,
horizontal, size,
style, style,
}: Props) { }: Props) {
// We render the icon twice at the same position on top of each other: // We render the icon twice at the same position on top of each other:
@@ -36,16 +36,16 @@ export default function TabBarIcon({
{renderIcon({ {renderIcon({
route, route,
focused: true, focused: true,
horizontal, size,
tintColor: activeTintColor, color: activeTintColor,
})} })}
</View> </View>
<View style={[styles.icon, { opacity: inactiveOpacity }]}> <View style={[styles.icon, { opacity: inactiveOpacity }]}>
{renderIcon({ {renderIcon({
route, route,
focused: false, focused: false,
horizontal, size,
tintColor: inactiveTintColor, color: inactiveTintColor,
})} })}
</View> </View>
</View> </View>

View File

@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.8...@react-navigation/compat@5.0.0-alpha.9) (2019-10-22)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.8](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.7...@react-navigation/compat@5.0.0-alpha.8) (2019-10-15)
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.7](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.6...@react-navigation/compat@5.0.0-alpha.7) (2019-10-06)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.6](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.5...@react-navigation/compat@5.0.0-alpha.6) (2019-10-03)
**Note:** Version bump only for package @react-navigation/compat
# [5.0.0-alpha.5](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.4...@react-navigation/compat@5.0.0-alpha.5) (2019-10-03) # [5.0.0-alpha.5](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/compat@5.0.0-alpha.4...@react-navigation/compat@5.0.0-alpha.5) (2019-10-03)
**Note:** Version bump only for package @react-navigation/compat **Note:** Version bump only for package @react-navigation/compat

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/compat", "name": "@react-navigation/compat",
"description": "Compatibility layer to write navigator definitions in static configuration format", "description": "Compatibility layer to write navigator definitions in static configuration format",
"version": "5.0.0-alpha.5", "version": "5.0.0-alpha.9",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -28,12 +28,12 @@
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"react": "^16.8.3", "react": "~16.8.3",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"peerDependencies": { "peerDependencies": {
"@react-navigation/core": "^5.0.0-alpha.0", "@react-navigation/core": "^5.0.0-alpha.0",
"react": "^16.8.3" "react": "~16.8.3"
}, },
"@react-native-community/bob": { "@react-native-community/bob": {
"source": "src", "source": "src",

View File

@@ -49,7 +49,7 @@ export default function createCompatNavigatorFactory<
navigationConfig: Partial<Omit<NavigationConfig, 'screenOptions'>> & { navigationConfig: Partial<Omit<NavigationConfig, 'screenOptions'>> & {
order?: Array<Extract<keyof ParamList, string>>; order?: Array<Extract<keyof ParamList, string>>;
defaultNavigationOptions?: ScreenOptions; defaultNavigationOptions?: ScreenOptions;
navigationOptions?: { [key: string]: any }; navigationOptions?: Record<string, any>;
} = {} } = {}
) => { ) => {
const Pair = createNavigator(); const Pair = createNavigator();

View File

@@ -3,6 +3,57 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.17](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.16...@react-navigation/core@5.0.0-alpha.17) (2019-10-22)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.16](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.15...@react-navigation/core@5.0.0-alpha.16) (2019-10-18)
### Bug Fixes
* rehydrate state before using it ([3e92e22](https://github.com/react-navigation/navigation-ex/commit/3e92e22))
### Features
* make it easier to navigate to a specific route in navigator ([#114](https://github.com/react-navigation/navigation-ex/issues/114)) ([a543f1b](https://github.com/react-navigation/navigation-ex/commit/a543f1b)), closes [#90](https://github.com/react-navigation/navigation-ex/issues/90)
# [5.0.0-alpha.15](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.14...@react-navigation/core@5.0.0-alpha.15) (2019-10-15)
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.14](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.13...@react-navigation/core@5.0.0-alpha.14) (2019-10-06)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.12...@react-navigation/core@5.0.0-alpha.13) (2019-10-03)
**Note:** Version bump only for package @react-navigation/core
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.11...@react-navigation/core@5.0.0-alpha.12) (2019-10-03) # [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/core@5.0.0-alpha.11...@react-navigation/core@5.0.0-alpha.12) (2019-10-03)

View File

@@ -6,7 +6,7 @@
"react-native", "react-native",
"react-navigation" "react-navigation"
], ],
"version": "5.0.0-alpha.12", "version": "5.0.0-alpha.17",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -40,13 +40,13 @@
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/shortid": "^0.0.29", "@types/shortid": "^0.0.29",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "^16.8.3", "react": "~16.8.3",
"react-native-testing-library": "^1.9.1", "react-native-testing-library": "^1.9.1",
"react-test-renderer": "16.8.3", "react-test-renderer": "~16.8.3",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.3" "react": "~16.8.3"
}, },
"@react-native-community/bob": { "@react-native-community/bob": {
"source": "src", "source": "src",

View File

@@ -108,7 +108,24 @@ export default function MockRouter(options: DefaultRouterOptions) {
return null; return null;
} }
return { ...state, index }; return {
...state,
index,
routes:
action.payload.params !== undefined
? state.routes.map((route, i) =>
i === index
? {
...route,
params: {
...route.params,
...action.payload.params,
},
}
: route
)
: state.routes,
};
} }
default: default:

View File

@@ -5,7 +5,7 @@ import NavigationContainer from '../NavigationContainer';
import useNavigationBuilder from '../useNavigationBuilder'; import useNavigationBuilder from '../useNavigationBuilder';
import useNavigation from '../useNavigation'; import useNavigation from '../useNavigation';
import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter'; import MockRouter, { MockRouterKey } from './__fixtures__/MockRouter';
import { NavigationState } from '../types'; import { NavigationState, NavigationContainerRef } from '../types';
beforeEach(() => (MockRouterKey.current = 0)); beforeEach(() => (MockRouterKey.current = 0));
@@ -523,6 +523,80 @@ it('handles change in route names', () => {
}); });
}); });
it('navigates to nested child in a navigator', () => {
const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props);
return descriptors[state.routes[state.index].key].render();
};
const TestComponent = ({ route }: any): any =>
`[${route.name}, ${JSON.stringify(route.params)}]`;
const onStateChange = jest.fn();
const navigation = React.createRef<NavigationContainerRef>();
const element = render(
<NavigationContainer ref={navigation} onStateChange={onStateChange}>
<TestNavigator>
<Screen name="foo">
{() => (
<TestNavigator>
<Screen name="foo-a" component={TestComponent} />
<Screen name="foo-b" component={TestComponent} />
</TestNavigator>
)}
</Screen>
<Screen name="bar">
{() => (
<TestNavigator initialRouteName="bar-a">
<Screen
name="bar-a"
component={TestComponent}
initialParams={{ lol: 'why' }}
/>
<Screen
name="bar-b"
component={TestComponent}
initialParams={{ some: 'stuff' }}
/>
</TestNavigator>
)}
</Screen>
</TestNavigator>
</NavigationContainer>
);
expect(element).toMatchInlineSnapshot(`"[foo-a, undefined]"`);
act(
() =>
navigation.current &&
navigation.current.navigate('bar', {
screen: 'bar-b',
params: { test: 42 },
})
);
expect(element).toMatchInlineSnapshot(
`"[bar-b, {\\"some\\":\\"stuff\\",\\"test\\":42}]"`
);
act(
() =>
navigation.current &&
navigation.current.navigate('bar', {
screen: 'bar-a',
params: { whoa: 'test' },
})
);
expect(element).toMatchInlineSnapshot(
`"[bar-a, {\\"lol\\":\\"why\\",\\"whoa\\":\\"test\\"}]"`
);
});
it('gives access to internal state', () => { it('gives access to internal state', () => {
const TestNavigator = (props: any): any => { const TestNavigator = (props: any): any => {
const { state, descriptors } = useNavigationBuilder(MockRouter, props); const { state, descriptors } = useNavigationBuilder(MockRouter, props);

View File

@@ -273,7 +273,11 @@ it('fires blur event when a route is removed with a delay', async () => {
const [previous, dispatch] = React.useReducer( const [previous, dispatch] = React.useReducer(
(state, action) => { (state, action) => {
return { ...state, ...action }; if (state.routes !== action.routes) {
return { ...state, ...action };
}
return state;
}, },
{ routes: state.routes, descriptors } { routes: state.routes, descriptors }
); );

View File

@@ -3,7 +3,7 @@ import { NavigationState, PartialState, Route } from './types';
type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>; type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
type StringifyConfig = { [key: string]: (value: any) => string }; type StringifyConfig = Record<string, (value: any) => string>;
type Options = { type Options = {
[routeName: string]: string | { path: string; stringify?: StringifyConfig }; [routeName: string]: string | { path: string; stringify?: StringifyConfig };

View File

@@ -2,7 +2,7 @@ import escape from 'escape-string-regexp';
import queryString from 'query-string'; import queryString from 'query-string';
import { NavigationState, PartialState } from './types'; import { NavigationState, PartialState } from './types';
type ParseConfig = { [key: string]: (value: string) => any }; type ParseConfig = Record<string, (value: string) => any>;
type Options = { type Options = {
[routeName: string]: string | { path: string; parse?: ParseConfig }; [routeName: string]: string | { path: string; parse?: ParseConfig };
@@ -78,7 +78,7 @@ export default function getStateFromPath(
.filter(p => p.startsWith(':')); .filter(p => p.startsWith(':'));
if (paramPatterns.length) { if (paramPatterns.length) {
params = paramPatterns.reduce<{ [key: string]: any }>((acc, p, i) => { params = paramPatterns.reduce<Record<string, any>>((acc, p, i) => {
const key = p.replace(/^:/, ''); const key = p.replace(/^:/, '');
const value = match[i + 1]; // The param segments start from index 1 in the regex match result const value = match[i + 1]; // The param segments start from index 1 in the regex match result

View File

@@ -189,7 +189,7 @@ export type Router<
actionCreators?: ActionCreators<Action>; actionCreators?: ActionCreators<Action>;
}; };
export type ParamListBase = { [key: string]: object | undefined }; export type ParamListBase = Record<string, object | undefined>;
export type EventMapBase = { export type EventMapBase = {
focus: undefined; focus: undefined;
@@ -215,7 +215,7 @@ export type EventListenerCallback<EventName extends string, Data> = (
e: EventArg<EventName, Data> e: EventArg<EventName, Data>
) => void; ) => void;
export type EventConsumer<EventMap extends { [key: string]: any }> = { export type EventConsumer<EventMap extends Record<string, any>> = {
/** /**
* Subscribe to events from the parent navigator. * Subscribe to events from the parent navigator.
* *
@@ -232,7 +232,7 @@ export type EventConsumer<EventMap extends { [key: string]: any }> = {
): void; ): void;
}; };
export type EventEmitter<EventMap extends { [key: string]: any }> = { export type EventEmitter<EventMap extends Record<string, any>> = {
/** /**
* Emit an event to child screens. * Emit an event to child screens.
* *
@@ -349,7 +349,7 @@ type NavigationHelpersCommon<
export type NavigationHelpers< export type NavigationHelpers<
ParamList extends ParamListBase, ParamList extends ParamListBase,
EventMap extends { [key: string]: any } = {} EventMap extends Record<string, any> = {}
> = NavigationHelpersCommon<ParamList> & > = NavigationHelpersCommon<ParamList> &
EventEmitter<EventMap> & { EventEmitter<EventMap> & {
/** /**
@@ -376,7 +376,7 @@ export type NavigationProp<
RouteName extends keyof ParamList = string, RouteName extends keyof ParamList = string,
State extends NavigationState = NavigationState, State extends NavigationState = NavigationState,
ScreenOptions extends object = {}, ScreenOptions extends object = {},
EventMap extends { [key: string]: any } = {} EventMap extends Record<string, any> = {}
> = NavigationHelpersCommon<ParamList, State> & { > = NavigationHelpersCommon<ParamList, State> & {
/** /**
* Update the param object for the route. * Update the param object for the route.
@@ -468,7 +468,7 @@ export type Descriptor<
RouteName extends keyof ParamList = string, RouteName extends keyof ParamList = string,
State extends NavigationState = NavigationState, State extends NavigationState = NavigationState,
ScreenOptions extends object = {}, ScreenOptions extends object = {},
EventMap extends { [key: string]: any } = {} EventMap extends Record<string, any> = {}
> = { > = {
/** /**
* Render the component associated with this route. * Render the component associated with this route.

View File

@@ -20,7 +20,7 @@ import {
type Options<State extends NavigationState, ScreenOptions extends object> = { type Options<State extends NavigationState, ScreenOptions extends object> = {
state: State; state: State;
screens: { [key: string]: RouteConfig<ParamListBase, string, ScreenOptions> }; screens: Record<string, RouteConfig<ParamListBase, string, ScreenOptions>>;
navigation: NavigationHelpers<ParamListBase>; navigation: NavigationHelpers<ParamListBase>;
screenOptions?: screenOptions?:
| ScreenOptions | ScreenOptions
@@ -68,7 +68,7 @@ export default function useDescriptors<
router, router,
emitter, emitter,
}: Options<State, ScreenOptions>) { }: Options<State, ScreenOptions>) {
const [options, setOptions] = React.useState<{ [key: string]: object }>({}); const [options, setOptions] = React.useState<Record<string, object>>({});
const { trackAction } = React.useContext(NavigationBuilderContext); const { trackAction } = React.useContext(NavigationBuilderContext);
const context = React.useMemo( const context = React.useMemo(

View File

@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import { EventEmitter, EventConsumer, EventArg } from './types'; import { EventEmitter, EventConsumer, EventArg } from './types';
export type NavigationEventEmitter = EventEmitter<{ [key: string]: any }> & { export type NavigationEventEmitter = EventEmitter<Record<string, any>> & {
create: (target: string) => EventConsumer<{ [key: string]: any }>; create: (target: string) => EventConsumer<Record<string, any>>;
}; };
type Listeners = Array<(data: any) => void>; type Listeners = Array<(data: any) => void>;
@@ -11,9 +11,7 @@ type Listeners = Array<(data: any) => void>;
* Hook to manage the event system used by the navigator to notify screens of various events. * Hook to manage the event system used by the navigator to notify screens of various events.
*/ */
export default function useEventEmitter(): NavigationEventEmitter { export default function useEventEmitter(): NavigationEventEmitter {
const listeners = React.useRef<{ const listeners = React.useRef<Record<string, Record<string, Listeners>>>({});
[key: string]: { [key: string]: Listeners };
}>({});
const create = React.useCallback((target: string) => { const create = React.useCallback((target: string) => {
const removeListener = (type: string, callback: (data: any) => void) => { const removeListener = (type: string, callback: (data: any) => void) => {

View File

@@ -1,6 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import { NavigationStateContext } from './NavigationContainer'; import { NavigationStateContext } from './NavigationContainer';
import NavigationRouteContext from './NavigationRouteContext';
import Screen from './Screen'; import Screen from './Screen';
import { navigate } from './CommonActions';
import useEventEmitter from './useEventEmitter'; import useEventEmitter from './useEventEmitter';
import useRegisterNavigator from './useRegisterNavigator'; import useRegisterNavigator from './useRegisterNavigator';
import useDescriptors from './useDescriptors'; import useDescriptors from './useDescriptors';
@@ -30,6 +32,13 @@ import useOnGetState from './useOnGetState';
// eslint-disable-next-line babel/no-unused-expressions // eslint-disable-next-line babel/no-unused-expressions
PrivateValueStore; PrivateValueStore;
type NavigatorRoute = {
params?: {
screen?: string;
params?: object;
};
};
/** /**
* Compare two arrays with primitive values as the content. * Compare two arrays with primitive values as the content.
* We need to make sure that both values and order match. * We need to make sure that both values and order match.
@@ -89,39 +98,66 @@ export default function useNavigationBuilder<
State extends NavigationState, State extends NavigationState,
RouterOptions extends DefaultRouterOptions, RouterOptions extends DefaultRouterOptions,
ScreenOptions extends object, ScreenOptions extends object,
EventMap extends { [key: string]: any } EventMap extends Record<string, any>
>( >(
createRouter: RouterFactory<State, any, RouterOptions>, createRouter: RouterFactory<State, any, RouterOptions>,
options: DefaultNavigatorOptions<ScreenOptions> & RouterOptions options: DefaultNavigatorOptions<ScreenOptions> & RouterOptions
) { ) {
useRegisterNavigator(); useRegisterNavigator();
const route = React.useContext(NavigationRouteContext) as (
| NavigatorRoute
| undefined);
const previousRouteRef = React.useRef(route);
React.useEffect(() => {
previousRouteRef.current = route;
}, [route]);
const { children, ...rest } = options; const { children, ...rest } = options;
const { current: router } = React.useRef<Router<State, any>>( const { current: router } = React.useRef<Router<State, any>>(
createRouter((rest as unknown) as RouterOptions) createRouter({
...((rest as unknown) as RouterOptions),
...(route && route.params && typeof route.params.screen === 'string'
? { initialRouteName: route.params.screen }
: null),
})
); );
const screens = getRouteConfigsFromChildren<ScreenOptions>(children).reduce( const screens = getRouteConfigsFromChildren<ScreenOptions>(children).reduce<
(acc, curr) => { Record<string, RouteConfig<ParamListBase, string, ScreenOptions>>
if (curr.name in acc) { >((acc, curr) => {
throw new Error( if (curr.name in acc) {
`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${curr.name}')` throw new Error(
); `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${curr.name}')`
} );
}
acc[curr.name] = curr; acc[curr.name] = curr;
return acc; return acc;
}, }, {});
{} as { [key: string]: RouteConfig<ParamListBase, string, ScreenOptions> }
);
const routeNames = Object.keys(screens); const routeNames = Object.keys(screens);
const routeParamList = routeNames.reduce( const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
(acc, curr) => { (acc, curr) => {
acc[curr] = screens[curr].initialParams; const { initialParams } = screens[curr];
const initialParamsFromParams =
route && route.params && route.params.screen === curr
? route.params.params
: undefined;
acc[curr] =
initialParams !== undefined || initialParamsFromParams !== undefined
? {
...initialParams,
...initialParamsFromParams,
}
: undefined;
return acc; return acc;
}, },
{} as { [key: string]: object | undefined } {}
); );
if (!routeNames.length) { if (!routeNames.length) {
@@ -175,28 +211,53 @@ export default function useNavigationBuilder<
? (initializedStateRef.current as State) ? (initializedStateRef.current as State)
: (currentState as State); : (currentState as State);
let nextState: State = state;
if (!isArrayEqual(state.routeNames, routeNames)) { if (!isArrayEqual(state.routeNames, routeNames)) {
// When the list of route names change, the router should handle it to remove invalid routes // When the list of route names change, the router should handle it to remove invalid routes
const nextState = router.getStateForRouteNamesChange(state, { nextState = router.getStateForRouteNamesChange(state, {
routeNames, routeNames,
routeParamList, routeParamList,
}); });
if (state !== nextState) {
// If the state needs to be updated, we'll schedule an update with React
// setState in render seems hacky, but that's how React docs implement getDerivedPropsFromState
// https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
performTransaction(() => {
setState(nextState);
});
}
// The up-to-date state will come in next render, but we don't need to wait for it
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
// So we override the state objec we return to use the latest state as soon as possible
state = nextState;
} }
if (
previousRouteRef.current &&
route &&
route.params &&
typeof route.params.screen === 'string' &&
route.params !== previousRouteRef.current.params
) {
// If the route was updated with new name and/or params, we should navigate there
// The update should be limited to current navigator only, so we call the router manually
const updatedState = router.getStateForAction(
state,
navigate(route.params.screen, route.params.params)
);
nextState =
updatedState !== null
? router.getRehydratedState(updatedState, {
routeNames,
routeParamList,
})
: state;
}
if (state !== nextState) {
// If the state needs to be updated, we'll schedule an update with React
// setState in render seems hacky, but that's how React docs implement getDerivedPropsFromState
// https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
performTransaction(() => {
setState(nextState);
});
}
// The up-to-date state will come in next render, but we don't need to wait for it
// We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
// So we override the state objec we return to use the latest state as soon as possible
state = nextState;
React.useEffect(() => { React.useEffect(() => {
return () => { return () => {
// We need to clean up state for this navigator on unmount // We need to clean up state for this navigator on unmount

View File

@@ -18,7 +18,7 @@ type Options<State extends NavigationState> = {
navigation: NavigationHelpers<ParamListBase> & navigation: NavigationHelpers<ParamListBase> &
Partial<NavigationProp<ParamListBase, string, any, any, any>>; Partial<NavigationProp<ParamListBase, string, any, any, any>>;
setOptions: ( setOptions: (
cb: (options: { [key: string]: object }) => { [key: string]: object } cb: (options: Record<string, object>) => Record<string, object>
) => void; ) => void;
router: Router<State, NavigationAction>; router: Router<State, NavigationAction>;
emitter: NavigationEventEmitter; emitter: NavigationEventEmitter;
@@ -89,13 +89,13 @@ export default function useNavigationCache<
); );
}; };
const helpers = Object.keys(actions).reduce( const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
(acc, name) => { (acc, name) => {
// @ts-ignore // @ts-ignore
acc[name] = (...args: any) => dispatch(actions[name](...args)); acc[name] = (...args: any) => dispatch(actions[name](...args));
return acc; return acc;
}, },
{} as { [key: string]: () => void } {}
); );
acc[route.key] = { acc[route.key] = {

View File

@@ -35,7 +35,7 @@ type Options<State extends NavigationState, Action extends NavigationAction> = {
export default function useNavigationHelpers< export default function useNavigationHelpers<
State extends NavigationState, State extends NavigationState,
Action extends NavigationAction, Action extends NavigationAction,
EventMap extends { [key: string]: any } EventMap extends Record<string, any>
>({ onAction, getState, emitter, router }: Options<State, Action>) { >({ onAction, getState, emitter, router }: Options<State, Action>) {
const resetRoot = React.useContext(ResetRootContext); const resetRoot = React.useContext(ResetRootContext);
const parentNavigationHelpers = React.useContext(NavigationContext); const parentNavigationHelpers = React.useContext(NavigationContext);
@@ -55,13 +55,13 @@ export default function useNavigationHelpers<
...CommonActions, ...CommonActions,
}; };
const helpers = Object.keys(actions).reduce( const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
(acc, name) => { (acc, name) => {
// @ts-ignore // @ts-ignore
acc[name] = (...args: any) => dispatch(actions[name](...args)); acc[name] = (...args: any) => dispatch(actions[name](...args));
return acc; return acc;
}, },
{} as { [key: string]: () => void } {}
); );
return { return {

View File

@@ -3,6 +3,61 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.16](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.15...@react-navigation/drawer@5.0.0-alpha.16) (2019-10-22)
### Bug Fixes
* navigation drawer sometimes not closing when pressed outside ([0d8cdc8](https://github.com/react-navigation/navigation-ex/commit/0d8cdc8))
# [5.0.0-alpha.15](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.14...@react-navigation/drawer@5.0.0-alpha.15) (2019-10-17)
### Bug Fixes
* fix passing content options in drawer ([cab6160](https://github.com/react-navigation/navigation-ex/commit/cab6160))
# [5.0.0-alpha.14](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.13...@react-navigation/drawer@5.0.0-alpha.14) (2019-10-15)
### Bug Fixes
* add flex: 1 to drawer content ([2b57702](https://github.com/react-navigation/navigation-ex/commit/2b57702))
* fix content component not rendering in drawer ([0a5fb3e](https://github.com/react-navigation/navigation-ex/commit/0a5fb3e))
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.12...@react-navigation/drawer@5.0.0-alpha.13) (2019-10-06)
**Note:** Version bump only for package @react-navigation/drawer
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.11...@react-navigation/drawer@5.0.0-alpha.12) (2019-10-03)
**Note:** Version bump only for package @react-navigation/drawer
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.10...@react-navigation/drawer@5.0.0-alpha.11) (2019-10-03) # [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/drawer@5.0.0-alpha.10...@react-navigation/drawer@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/drawer **Note:** Version bump only for package @react-navigation/drawer

View File

@@ -10,44 +10,29 @@ Open a Terminal in your project's folder and run,
yarn add @react-navigation/core @react-navigation/drawer yarn add @react-navigation/core @react-navigation/drawer
``` ```
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated). Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler), [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
If you are using Expo, to ensure that you get the compatible versions of the libraries, run: If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
```sh ```sh
expo install react-native-gesture-handler react-native-reanimated expo install react-native-gesture-handler react-native-reanimated react-native-safe-area-context
``` ```
If you are not using Expo, run the following: If you are not using Expo, run the following:
```sh ```sh
yarn add react-native-reanimated react-native-gesture-handler yarn add react-native-reanimated react-native-gesture-handler react-native-safe-area-context
``` ```
If you are using Expo, you are done. Otherwise, continue to the next steps. If you are using Expo, you are done. Otherwise, continue to the next steps.
Next, we need to link these libraries. The steps depends on your React Native version: To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
- **React Native 0.60 and higher** ```sh
cd ios
On newer versions of React Native, [linking is automatic](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md). pod install
cd ..
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run: ```
```sh
cd ios
pod install
cd ..
```
- **React Native 0.59**
If you're on an older React Native version, you need to manually link the dependencies. To do that, run:
```sh
react-native link react-native-reanimated
react-native link react-native-gesture-handler
```
**IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation. **IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation.

View File

@@ -11,7 +11,7 @@
"material", "material",
"drawer" "drawer"
], ],
"version": "5.0.0-alpha.11", "version": "5.0.0-alpha.16",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -34,19 +34,19 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.9", "@react-navigation/routers": "^5.0.0-alpha.9"
"react-native-safe-area-view": "^0.14.7"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.7.0", "@react-native-community/bob": "^0.7.0",
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.17",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "16.8.3", "react": "~16.8.3",
"react-native": "0.59.10", "react-native": "~0.59.10",
"react-native-gesture-handler": "^1.3.0", "react-native-gesture-handler": "^1.3.0",
"react-native-reanimated": "^1.3.0", "react-native-reanimated": "^1.3.0",
"react-native-screens": "^1.0.0-alpha.22", "react-native-safe-area-context": "^0.3.6",
"react-native-screens": "^2.0.0-alpha.5",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"peerDependencies": { "peerDependencies": {
@@ -55,7 +55,8 @@
"react-native": "*", "react-native": "*",
"react-native-gesture-handler": "^1.0.0", "react-native-gesture-handler": "^1.0.0",
"react-native-reanimated": "^1.0.0", "react-native-reanimated": "^1.0.0",
"react-native-screens": "^1.0.0-alpha.0" "react-native-safe-area-context": "^0.3.6",
"react-native-screens": "^1.0.0-alpha.0 || ^2.0.0-alpha.0"
}, },
"@react-native-community/bob": { "@react-native-community/bob": {
"source": "src", "source": "src",

View File

@@ -8,8 +8,9 @@ export {
/** /**
* Views * Views
*/ */
export { default as DrawerNavigatorItems } from './views/DrawerNavigatorItems'; export { default as DrawerItem } from './views/DrawerItem';
export { default as DrawerSidebar } from './views/DrawerSidebar'; export { default as DrawerItemList } from './views/DrawerItemList';
export { default as DrawerContent } from './views/DrawerContent';
export { default as DrawerView } from './views/DrawerView'; export { default as DrawerView } from './views/DrawerView';
/** /**
@@ -23,5 +24,6 @@ export { default as DrawerGestureContext } from './utils/DrawerGestureContext';
export { export {
DrawerNavigationOptions, DrawerNavigationOptions,
DrawerNavigationProp, DrawerNavigationProp,
DrawerNavigationItemsProps, DrawerContentOptions,
DrawerContentComponentProps,
} from './types'; } from './types';

View File

@@ -12,16 +12,11 @@ import { PanGestureHandler } from 'react-native-gesture-handler';
export type Scene = { export type Scene = {
route: Route<string>; route: Route<string>;
index: number;
focused: boolean; focused: boolean;
tintColor?: string; color?: string;
}; };
export type DrawerNavigationConfig = { export type DrawerNavigationConfig<T = DrawerContentOptions> = {
/**
* Custom background color for the drawer. Defaults to `white`.
*/
drawerBackgroundColor: string;
/** /**
* Position of the drawer on the screen. Defaults to `left`. * Position of the drawer on the screen. Defaults to `left`.
*/ */
@@ -33,11 +28,6 @@ export type DrawerNavigationConfig = {
* - `slide`: Both the screen and the drawer slide on swipe to reveal the drawer. * - `slide`: Both the screen and the drawer slide on swipe to reveal the drawer.
*/ */
drawerType: 'front' | 'back' | 'slide'; drawerType: 'front' | 'back' | 'slide';
/**
* Number or a function which returns the width of the drawer.
* If a function is provided, it'll be called again when the screen's dimensions change.
*/
drawerWidth: number | (() => number);
/** /**
* How far from the edge of the screen the swipe gesture should activate. * How far from the edge of the screen the swipe gesture should activate.
*/ */
@@ -82,49 +72,51 @@ export type DrawerNavigationConfig = {
* Custom component used to render as the content of the drawer, for example, navigation items. * Custom component used to render as the content of the drawer, for example, navigation items.
* Defaults to `DrawerItems`. * Defaults to `DrawerItems`.
*/ */
contentComponent: React.ComponentType<ContentComponentProps>; contentComponent: React.ComponentType<DrawerContentComponentProps<T>>;
/** /**
* Options for the content component which will be passed as props. * Options for the content component which will be passed as props.
*/ */
contentOptions?: object; contentOptions?: T;
/** /**
* Style object for the component wrapping the screen content. * Style object for the component wrapping the screen content.
*/ */
sceneContainerStyle?: StyleProp<ViewStyle>; sceneContainerStyle?: StyleProp<ViewStyle>;
style?: StyleProp<ViewStyle>; /**
* Style object for the drawer component.
* You can pass a custom background color for a drawer or a custom width here.
*/
drawerStyle?: StyleProp<ViewStyle>;
}; };
export type DrawerNavigationOptions = { export type DrawerNavigationOptions = {
title?: string; title?: string;
drawerLabel?: drawerLabel?:
| string | string
| ((props: { tintColor?: string; focused: boolean }) => React.ReactElement); | ((props: { color: string; focused: boolean }) => React.ReactNode);
drawerIcon?: (props: { drawerIcon?: (props: {
tintColor?: string; color: string;
size: number;
focused: boolean; focused: boolean;
}) => React.ReactElement; }) => React.ReactNode;
drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open'; drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open';
}; };
export type ContentComponentProps = DrawerNavigationItemsProps & { export type DrawerContentComponentProps<T = DrawerContentOptions> = T & {
state: DrawerNavigationState;
navigation: NavigationHelpers<ParamListBase>; navigation: NavigationHelpers<ParamListBase>;
descriptors: { [key: string]: any }; descriptors: DrawerDescriptorMap;
/** /**
* Animated node which represents the current progress of the drawer's open state. * Animated node which represents the current progress of the drawer's open state.
* `0` is closed, `1` is open. * `0` is closed, `1` is open.
*/ */
drawerOpenProgress: Animated.Node<number>; progress: Animated.Node<number>;
/**
* Position of the drawer on the screen.
*/
drawerPosition: 'left' | 'right';
}; };
export type DrawerNavigationItemsProps = { export type DrawerContentOptions = {
/**
* The array of routes, can be modified or overridden to control what's shown in the drawer.
*/
items: Route<string>[];
/**
* Route key identifying the currently active route.
*/
activeItemKey?: string | null;
/** /**
* Color for the icon and label in the active item in the drawer. * Color for the icon and label in the active item in the drawer.
*/ */
@@ -141,37 +133,22 @@ export type DrawerNavigationItemsProps = {
* Background color for the inactive items in the drawer. * Background color for the inactive items in the drawer.
*/ */
inactiveBackgroundColor?: string; inactiveBackgroundColor?: string;
/**
* Style object for the content section.
*/
itemsContainerStyle?: ViewStyle;
/** /**
* Style object for the single item, which can contain an icon and/or a label. * Style object for the single item, which can contain an icon and/or a label.
*/ */
itemStyle?: StyleProp<ViewStyle>; itemStyle?: StyleProp<ViewStyle>;
/** /**
* Style object to overwrite `Text` style inside content section which renders a label. * Style object to apply to the `Text` inside content section which renders a label.
*/ */
labelStyle?: StyleProp<TextStyle>; labelStyle?: StyleProp<TextStyle>;
/** /**
* Style object to overwrite `Text` style of the active label. * Style object for the content section.
*/ */
activeLabelStyle?: StyleProp<TextStyle>; contentContainerStyle?: StyleProp<ViewStyle>;
/** /**
* Style object to overwrite `Text` style of the inactive label. * Style object for the wrapper view.
*/ */
inactiveLabelStyle?: StyleProp<TextStyle>; style?: StyleProp<ViewStyle>;
/**
* Style object for the wrapper `View` of the icon.
*/
iconContainerStyle?: StyleProp<ViewStyle>;
/**
* Position of the drawer on the screen.
*/
drawerPosition: 'left' | 'right';
getLabel: (scene: Scene) => React.ReactNode;
renderIcon: (scene: Scene) => React.ReactNode;
onItemPress: (scene: { route: Route<string>; focused: boolean }) => void;
}; };
export type DrawerNavigationEventMap = { export type DrawerNavigationEventMap = {

View File

@@ -428,7 +428,14 @@ export default class DrawerView extends React.PureComponent<Props> {
x: this.touchX, x: this.touchX,
translationX: this.gestureX, translationX: this.gestureX,
velocityX: this.velocityX, velocityX: this.velocityX,
state: this.gestureState, },
},
]);
private handleGestureStateChange = event([
{
nativeEvent: {
state: (s: Animated.Value<number>) => set(this.gestureState, s),
}, },
}, },
]); ]);
@@ -519,7 +526,7 @@ export default class DrawerView extends React.PureComponent<Props> {
activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]} activeOffsetX={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]} failOffsetY={[-SWIPE_DISTANCE_MINIMUM, SWIPE_DISTANCE_MINIMUM]}
onGestureEvent={this.handleGestureEvent} onGestureEvent={this.handleGestureEvent}
onHandlerStateChange={this.handleGestureEvent} onHandlerStateChange={this.handleGestureStateChange}
hitSlop={hitSlop} hitSlop={hitSlop}
enabled={!locked} enabled={!locked}
{...gestureHandlerProps} {...gestureHandlerProps}

View File

@@ -0,0 +1,36 @@
import * as React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useSafeArea } from 'react-native-safe-area-context';
import DrawerItemList from './DrawerItemList';
import { DrawerContentComponentProps } from '../types';
export default function DrawerContent({
contentContainerStyle,
style,
drawerPosition,
...rest
}: DrawerContentComponentProps) {
const insets = useSafeArea();
return (
<ScrollView
contentContainerStyle={[
{
paddingTop: insets.top + 4,
paddingLeft: drawerPosition === 'left' ? insets.left : 0,
paddingRight: drawerPosition === 'right' ? insets.right : 0,
},
contentContainerStyle,
]}
style={[styles.container, style]}
>
<DrawerItemList {...rest} />
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});

View File

@@ -0,0 +1,139 @@
import * as React from 'react';
import {
Text,
View,
StyleSheet,
StyleProp,
ViewStyle,
TextStyle,
} from 'react-native';
import TouchableItem from './TouchableItem';
type Props = {
/**
* The label text of the item.
*/
label:
| string
| ((props: { focused: boolean; color: string }) => React.ReactNode);
/**
* Icon to display for the `DrawerItem`.
*/
icon?: (props: {
focused: boolean;
size: number;
color: string;
}) => React.ReactNode;
/**
* Whether to highlight the drawer item as active.
*/
focused?: boolean;
/**
* Function to execute on press.
*/
onPress: () => void;
/**
* Color for the icon and label when the item is active.
*/
activeTintColor?: string;
/**
* Color for the icon and label when the item is inactive.
*/
inactiveTintColor?: string;
/**
* Background color for item when its active.
*/
activeBackgroundColor?: string;
/**
* Background color for item when its inactive.
*/
inactiveBackgroundColor?: string;
/**
* Style object for the label element.
*/
labelStyle?: StyleProp<TextStyle>;
/**
* Style object for the wrapper element.
*/
style?: StyleProp<ViewStyle>;
};
/**
* A component used to show an action item with an icon and a label in a navigation drawer.
*/
export default function DrawerItem({
icon,
label,
focused = false,
activeTintColor = '#6200ee',
inactiveTintColor = 'rgba(0, 0, 0, .68)',
activeBackgroundColor = 'rgba(98, 0, 238, 0.12)',
inactiveBackgroundColor = 'transparent',
style,
onPress,
...rest
}: Props) {
const { borderRadius = 4 } = StyleSheet.flatten(style || {});
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const iconNode = icon ? icon({ size: 24, focused, color }) : null;
return (
<View
collapsable={false}
{...rest}
style={[styles.container, { borderRadius, backgroundColor }, style]}
>
<TouchableItem
borderless
delayPressIn={0}
onPress={onPress}
style={[styles.wrapper, { borderRadius }]}
accessibilityTraits={focused ? ['button', 'selected'] : 'button'}
accessibilityComponentType="button"
accessibilityRole="button"
accessibilityStates={focused ? ['selected'] : []}
>
<React.Fragment>
{iconNode}
{typeof label === 'function' ? (
label({ color, focused })
) : (
<Text
numberOfLines={1}
style={[
styles.label,
{
color,
fontWeight: '500',
marginLeft: iconNode ? 32 : 0,
marginVertical: 5,
},
]}
>
{label}
</Text>
)}
</React.Fragment>
</TouchableItem>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginHorizontal: 10,
marginVertical: 4,
},
wrapper: {
flexDirection: 'row',
alignItems: 'center',
padding: 8,
},
label: {
marginRight: 32,
},
});

View File

@@ -0,0 +1,67 @@
import * as React from 'react';
import { CommonActions } from '@react-navigation/core';
import {
DrawerActions,
DrawerNavigationState,
} from '@react-navigation/routers';
import DrawerItem from './DrawerItem';
import {
DrawerNavigationHelpers,
DrawerDescriptorMap,
DrawerContentOptions,
} from '../types';
type Props = Omit<DrawerContentOptions, 'contentContainerStyle' | 'style'> & {
state: DrawerNavigationState;
navigation: DrawerNavigationHelpers;
descriptors: DrawerDescriptorMap;
};
/**
* Component that renders the navigation list in the drawer.
*/
export default function DrawerItemList({
state,
navigation,
descriptors,
activeTintColor,
inactiveTintColor,
activeBackgroundColor,
inactiveBackgroundColor,
itemStyle,
labelStyle,
}: Props) {
return (state.routes.map((route, i) => {
const focused = i === state.index;
const { title, drawerLabel, drawerIcon } = descriptors[route.key].options;
return (
<DrawerItem
key={route.key}
label={
drawerLabel !== undefined
? drawerLabel
: title !== undefined
? title
: route.name
}
icon={drawerIcon}
focused={focused}
activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor}
activeBackgroundColor={activeBackgroundColor}
inactiveBackgroundColor={inactiveBackgroundColor}
labelStyle={labelStyle}
style={itemStyle}
onPress={() => {
navigation.dispatch({
...(focused
? DrawerActions.closeDrawer()
: CommonActions.navigate(route.name)),
target: state.key,
});
}}
/>
);
}) as React.ReactNode) as React.ReactElement;
}

View File

@@ -1,120 +0,0 @@
import * as React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import SafeAreaView from 'react-native-safe-area-view';
import TouchableItem from './TouchableItem';
import { DrawerNavigationItemsProps } from '../types';
/**
* Component that renders the navigation list in the drawer.
*/
const DrawerNavigatorItems = ({
items,
activeItemKey,
activeTintColor,
activeBackgroundColor,
inactiveTintColor,
inactiveBackgroundColor,
getLabel,
renderIcon,
onItemPress,
itemsContainerStyle,
itemStyle,
labelStyle,
activeLabelStyle,
inactiveLabelStyle,
iconContainerStyle,
drawerPosition,
}: DrawerNavigationItemsProps) => (
<View style={[styles.container, itemsContainerStyle]}>
{items.map((route, index: number) => {
const focused = activeItemKey === route.key;
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const scene = { route, index, focused, tintColor: color };
const icon = renderIcon(scene);
const label = getLabel(scene);
const accessibilityLabel = typeof label === 'string' ? label : undefined;
const extraLabelStyle = focused ? activeLabelStyle : inactiveLabelStyle;
return (
<TouchableItem
key={route.key}
accessible
accessibilityLabel={accessibilityLabel}
onPress={() => {
onItemPress({ route, focused });
}}
delayPressIn={0}
>
<SafeAreaView
style={[{ backgroundColor }, styles.item, itemStyle]}
forceInset={{
[drawerPosition]: 'always',
[drawerPosition === 'left' ? 'right' : 'left']: 'never',
vertical: 'never',
}}
>
{icon ? (
<View
style={[
styles.icon,
focused ? null : styles.inactiveIcon,
iconContainerStyle,
]}
>
{icon}
</View>
) : null}
{typeof label === 'string' ? (
<Text
style={[styles.label, { color }, labelStyle, extraLabelStyle]}
>
{label}
</Text>
) : (
label
)}
</SafeAreaView>
</TouchableItem>
);
})}
</View>
);
/* Material design specs - https://material.io/guidelines/patterns/navigation-drawer.html#navigation-drawer-specs */
DrawerNavigatorItems.defaultProps = {
activeTintColor: '#2196f3',
activeBackgroundColor: 'rgba(0, 0, 0, .04)',
inactiveTintColor: 'rgba(0, 0, 0, .87)',
inactiveBackgroundColor: 'transparent',
};
const styles = StyleSheet.create({
container: {
paddingVertical: 4,
},
item: {
flexDirection: 'row',
alignItems: 'center',
},
icon: {
marginHorizontal: 16,
width: 24,
alignItems: 'center',
},
inactiveIcon: {
/*
* Icons have 0.54 opacity according to guidelines
* 100/87 * 54 ~= 62
*/
opacity: 0.62,
},
label: {
margin: 16,
fontWeight: 'bold',
},
});
export default DrawerNavigatorItems;

View File

@@ -1,128 +0,0 @@
import * as React from 'react';
import { StyleSheet, View, ViewStyle, StyleProp } from 'react-native';
import Animated from 'react-native-reanimated';
import { Route, CommonActions } from '@react-navigation/core';
import {
DrawerActions,
DrawerNavigationState,
} from '@react-navigation/routers';
import {
Scene,
ContentComponentProps,
DrawerDescriptorMap,
DrawerNavigationHelpers,
} from '../types';
type Props = {
contentComponent?: React.ComponentType<ContentComponentProps>;
contentOptions?: object;
state: DrawerNavigationState;
navigation: DrawerNavigationHelpers;
descriptors: DrawerDescriptorMap;
drawerOpenProgress: Animated.Node<number>;
drawerPosition: 'left' | 'right';
style?: StyleProp<ViewStyle>;
};
/**
* Component that renders the sidebar screen of the drawer.
*/
class DrawerSidebar extends React.PureComponent<Props> {
private getScreenOptions = (routeKey: string) => {
const descriptor = this.props.descriptors[routeKey];
if (!descriptor.options) {
throw new Error(
'Cannot access screen descriptor options from drawer sidebar'
);
}
return descriptor.options;
};
private getLabel = ({ focused, tintColor, route }: Scene) => {
const { drawerLabel, title } = this.getScreenOptions(route.key);
if (drawerLabel) {
return typeof drawerLabel === 'function'
? drawerLabel({ tintColor, focused })
: drawerLabel;
}
if (typeof title === 'string') {
return title;
}
return route.name;
};
private renderIcon = ({ focused, tintColor, route }: Scene) => {
const { drawerIcon } = this.getScreenOptions(route.key);
if (drawerIcon) {
return typeof drawerIcon === 'function'
? drawerIcon({ tintColor, focused })
: drawerIcon;
}
return null;
};
private handleItemPress = ({
route,
focused,
}: {
route: Route<string>;
focused: boolean;
}) => {
const { state, navigation } = this.props;
navigation.dispatch({
...(focused
? DrawerActions.closeDrawer()
: CommonActions.navigate(route.name)),
target: state.key,
});
};
render() {
const ContentComponent = this.props.contentComponent;
if (!ContentComponent) {
return null;
}
const { state } = this.props;
if (typeof state.index !== 'number') {
throw new Error(
'The index of the route should be state in the navigation state'
);
}
return (
<View style={[styles.container, this.props.style]}>
<ContentComponent
{...this.props.contentOptions}
navigation={this.props.navigation}
descriptors={this.props.descriptors}
drawerOpenProgress={this.props.drawerOpenProgress}
items={state.routes}
activeItemKey={
state.routes[state.index] ? state.routes[state.index].key : null
}
getLabel={this.getLabel}
renderIcon={this.renderIcon}
onItemPress={this.handleItemPress}
drawerPosition={this.props.drawerPosition}
/>
</View>
);
}
}
export default DrawerSidebar;
const styles = StyleSheet.create({
container: {
flex: 1,
},
});

View File

@@ -1,30 +1,35 @@
import * as React from 'react'; import * as React from 'react';
import { Dimensions, StyleSheet, I18nManager, Platform } from 'react-native'; import {
Dimensions,
StyleSheet,
I18nManager,
Platform,
ScaledSize,
} from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { ScreenContainer } from 'react-native-screens'; import { ScreenContainer } from 'react-native-screens';
import SafeAreaView from 'react-native-safe-area-view'; import { PanGestureHandler } from 'react-native-gesture-handler';
import { PanGestureHandler, ScrollView } from 'react-native-gesture-handler';
import { import {
DrawerNavigationState, DrawerNavigationState,
DrawerActions, DrawerActions,
} from '@react-navigation/routers'; } from '@react-navigation/routers';
import DrawerSidebar from './DrawerSidebar';
import DrawerGestureContext from '../utils/DrawerGestureContext'; import DrawerGestureContext from '../utils/DrawerGestureContext';
import ResourceSavingScene from './ResourceSavingScene'; import ResourceSavingScene from './ResourceSavingScene';
import DrawerNavigatorItems from './DrawerNavigatorItems'; import DrawerContent from './DrawerContent';
import Drawer from './Drawer'; import Drawer from './Drawer';
import { import {
DrawerDescriptorMap, DrawerDescriptorMap,
DrawerNavigationConfig, DrawerNavigationConfig,
ContentComponentProps,
DrawerNavigationHelpers, DrawerNavigationHelpers,
} from '../types'; } from '../types';
type Props = DrawerNavigationConfig & { type Props = Omit<DrawerNavigationConfig, 'overlayColor'> & {
state: DrawerNavigationState; state: DrawerNavigationState;
navigation: DrawerNavigationHelpers; navigation: DrawerNavigationHelpers;
descriptors: DrawerDescriptorMap; descriptors: DrawerDescriptorMap;
overlayColor: string;
}; };
type State = { type State = {
@@ -32,13 +37,26 @@ type State = {
drawerWidth: number; drawerWidth: number;
}; };
const DefaultContentComponent = (props: ContentComponentProps) => ( const getDefaultDrawerWidth = ({
<ScrollView alwaysBounceVertical={false}> height,
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}> width,
<DrawerNavigatorItems {...props} /> }: {
</SafeAreaView> height: number;
</ScrollView> width: number;
); }) => {
/*
* Default drawer width is screen width - header height
* with a max width of 280 on mobile and 320 on tablet
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const smallerAxisSize = Math.min(height, width);
const isLandscape = width > height;
const isTablet = smallerAxisSize >= 600;
const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
const maxWidth = isTablet ? 320 : 280;
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
};
/** /**
* Component that renders the drawer. * Component that renders the drawer.
@@ -46,25 +64,10 @@ const DefaultContentComponent = (props: ContentComponentProps) => (
export default class DrawerView extends React.PureComponent<Props, State> { export default class DrawerView extends React.PureComponent<Props, State> {
static defaultProps = { static defaultProps = {
lazy: true, lazy: true,
drawerWidth: () => { contentComponent: DrawerContent,
/*
* Default drawer width is screen width - header height
* with a max width of 280 on mobile and 320 on tablet
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const { height, width } = Dimensions.get('window');
const smallerAxisSize = Math.min(height, width);
const isLandscape = width > height;
const isTablet = smallerAxisSize >= 600;
const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
const maxWidth = isTablet ? 320 : 280;
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
},
contentComponent: DefaultContentComponent,
drawerPosition: I18nManager.isRTL ? 'right' : 'left', drawerPosition: I18nManager.isRTL ? 'right' : 'left',
keyboardDismissMode: 'on-drag', keyboardDismissMode: 'on-drag',
drawerBackgroundColor: 'white', overlayColor: 'rgba(0, 0, 0, 0.5)',
drawerType: 'front', drawerType: 'front',
hideStatusBar: false, hideStatusBar: false,
statusBarAnimation: 'slide', statusBarAnimation: 'slide',
@@ -83,10 +86,7 @@ export default class DrawerView extends React.PureComponent<Props, State> {
state: State = { state: State = {
loaded: [this.props.state.index], loaded: [this.props.state.index],
drawerWidth: drawerWidth: getDefaultDrawerWidth(Dimensions.get('window')),
typeof this.props.drawerWidth === 'function'
? this.props.drawerWidth()
: this.props.drawerWidth,
}; };
componentDidMount() { componentDidMount() {
@@ -121,11 +121,8 @@ export default class DrawerView extends React.PureComponent<Props, State> {
navigation.emit({ type: 'drawerClose' }); navigation.emit({ type: 'drawerClose' });
}; };
private updateWidth = () => { private updateWidth = ({ window }: { window: ScaledSize }) => {
const drawerWidth = const drawerWidth = getDefaultDrawerWidth(window);
typeof this.props.drawerWidth === 'function'
? this.props.drawerWidth()
: this.props.drawerWidth;
if (this.state.drawerWidth !== drawerWidth) { if (this.state.drawerWidth !== drawerWidth) {
this.setState({ drawerWidth }); this.setState({ drawerWidth });
@@ -133,7 +130,25 @@ export default class DrawerView extends React.PureComponent<Props, State> {
}; };
private renderNavigationView = ({ progress }: any) => { private renderNavigationView = ({ progress }: any) => {
return <DrawerSidebar drawerOpenProgress={progress} {...this.props} />; const {
state,
navigation,
descriptors,
drawerPosition,
contentComponent: ContentComponent,
contentOptions,
} = this.props;
return (
<ContentComponent
progress={progress}
state={state}
navigation={navigation}
descriptors={descriptors}
drawerPosition={drawerPosition}
{...contentOptions}
/>
);
}; };
private renderContent = () => { private renderContent = () => {
@@ -187,9 +202,9 @@ export default class DrawerView extends React.PureComponent<Props, State> {
descriptors, descriptors,
drawerType, drawerType,
drawerPosition, drawerPosition,
drawerBackgroundColor,
overlayColor, overlayColor,
sceneContainerStyle, sceneContainerStyle,
drawerStyle,
edgeWidth, edgeWidth,
minSwipeDistance, minSwipeDistance,
hideStatusBar, hideStatusBar,
@@ -197,6 +212,8 @@ export default class DrawerView extends React.PureComponent<Props, State> {
gestureHandlerProps, gestureHandlerProps,
} = this.props; } = this.props;
const { drawerWidth } = this.state;
const activeKey = state.routes[state.index].key; const activeKey = state.routes[state.index].key;
const { drawerLockMode } = descriptors[activeKey].options; const { drawerLockMode } = descriptors[activeKey].options;
@@ -208,35 +225,32 @@ export default class DrawerView extends React.PureComponent<Props, State> {
: state.isDrawerOpen; : state.isDrawerOpen;
return ( return (
<DrawerGestureContext.Provider value={this.drawerGestureRef}> <SafeAreaProvider>
<Drawer <DrawerGestureContext.Provider value={this.drawerGestureRef}>
open={isOpen} <Drawer
locked={ open={isOpen}
drawerLockMode === 'locked-open' || locked={
drawerLockMode === 'locked-closed' drawerLockMode === 'locked-open' ||
} drawerLockMode === 'locked-closed'
onOpen={this.handleDrawerOpen} }
onClose={this.handleDrawerClose} onOpen={this.handleDrawerOpen}
onGestureRef={this.setDrawerGestureRef} onClose={this.handleDrawerClose}
gestureHandlerProps={gestureHandlerProps} onGestureRef={this.setDrawerGestureRef}
drawerType={drawerType} gestureHandlerProps={gestureHandlerProps}
drawerPosition={drawerPosition} drawerType={drawerType}
sceneContainerStyle={sceneContainerStyle} drawerPosition={drawerPosition}
drawerStyle={{ sceneContainerStyle={sceneContainerStyle}
backgroundColor: drawerBackgroundColor || 'white', drawerStyle={[{ width: drawerWidth }, drawerStyle]}
width: this.state.drawerWidth, overlayStyle={{ backgroundColor: overlayColor }}
}} swipeEdgeWidth={edgeWidth}
overlayStyle={{ swipeDistanceThreshold={minSwipeDistance}
backgroundColor: overlayColor || 'rgba(0, 0, 0, 0.5)', hideStatusBar={hideStatusBar}
}} statusBarAnimation={statusBarAnimation}
swipeEdgeWidth={edgeWidth} renderDrawerContent={this.renderNavigationView}
swipeDistanceThreshold={minSwipeDistance} renderSceneContent={this.renderContent}
hideStatusBar={hideStatusBar} />
statusBarAnimation={statusBarAnimation} </DrawerGestureContext.Provider>
renderDrawerContent={this.renderNavigationView} </SafeAreaProvider>
renderSceneContent={this.renderContent}
/>
</DrawerGestureContext.Provider>
); );
} }
} }

View File

@@ -3,6 +3,66 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.15](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.14...@react-navigation/example@5.0.0-alpha.15) (2019-10-22)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.14](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.13...@react-navigation/example@5.0.0-alpha.14) (2019-10-17)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.12...@react-navigation/example@5.0.0-alpha.13) (2019-10-15)
### Bug Fixes
* block GH interactions in Native Stack example ([#126](https://github.com/react-navigation/navigation-ex/issues/126)) ([386d1c0](https://github.com/react-navigation/navigation-ex/commit/386d1c0))
* make it possible to run the example on web ([7a901af](https://github.com/react-navigation/navigation-ex/commit/7a901af))
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.12](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.11...@react-navigation/example@5.0.0-alpha.12) (2019-10-06)
### Features
* drop header: null in favor of more explitit headerShown option ([ba6b6ae](https://github.com/satya164/navigation-ex/commit/ba6b6ae))
# [5.0.0-alpha.11](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.10...@react-navigation/example@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.10](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.9...@react-navigation/example@5.0.0-alpha.10) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example
# [5.0.0-alpha.9](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.8...@react-navigation/example@5.0.0-alpha.9) (2019-10-03) # [5.0.0-alpha.9](https://github.com/satya164/navigation-ex/compare/@react-navigation/example@5.0.0-alpha.8...@react-navigation/example@5.0.0-alpha.9) (2019-10-03)
**Note:** Version bump only for package @react-navigation/example **Note:** Version bump only for package @react-navigation/example

View File

@@ -151,6 +151,7 @@
<orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.6.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.6.0@aar" level="project" />
<orderEntry type="library" name="Gradle: io.nlopez.smartlocation:library:3.2.11@aar" level="project" /> <orderEntry type="library" name="Gradle: io.nlopez.smartlocation:library:3.2.11@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" /> <orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" />
<orderEntry type="module" module-name="react-native-screens" />
<orderEntry type="module" module-name="react-native-reanimated" /> <orderEntry type="module" module-name="react-native-reanimated" />
<orderEntry type="module" module-name="react-native-gesture-handler" /> <orderEntry type="module" module-name="react-native-gesture-handler" />
<orderEntry type="module" module-name="expo-permissions" /> <orderEntry type="module" module-name="expo-permissions" />

View File

@@ -139,6 +139,8 @@ android {
} }
dependencies { dependencies {
implementation project(':react-native-safe-area-context')
implementation project(':react-native-screens')
implementation project(':react-native-reanimated') implementation project(':react-native-reanimated')
implementation project(':react-native-gesture-handler') implementation project(':react-native-gesture-handler')
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])

View File

@@ -3,6 +3,8 @@ package com.reactnavigationexample;
import android.app.Application; import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.th3rdwave.safeareacontext.SafeAreaContextPackage;
import com.swmansion.rnscreens.RNScreensPackage;
import com.swmansion.reanimated.ReanimatedPackage; import com.swmansion.reanimated.ReanimatedPackage;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
@@ -39,6 +41,8 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new SafeAreaContextPackage(),
new RNScreensPackage(),
new ReanimatedPackage(), new ReanimatedPackage(),
new RNGestureHandlerPackage(), new RNGestureHandlerPackage(),
new ModuleRegistryAdapter(mModuleRegistryProvider) new ModuleRegistryAdapter(mModuleRegistryProvider)

View File

@@ -16,3 +16,6 @@
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -1,8 +0,0 @@
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed Sep 18 15:42:44 CEST 2019
sdk.dir=/Users/osdnk/Library/Android/sdk

View File

@@ -1,4 +1,8 @@
apply from: '../node_modules/react-native-unimodules/gradle.groovy' apply from: '../node_modules/react-native-unimodules/gradle.groovy'
include ':react-native-safe-area-context'
project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
include ':react-native-screens'
project(':react-native-screens').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screens/android')
include ':react-native-reanimated' include ':react-native-reanimated'
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android') project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android')
includeUnimodulesProjects() includeUnimodulesProjects()

View File

@@ -3,14 +3,13 @@
"name": "@react-navigation/example", "name": "@react-navigation/example",
"slug": "react-navigation-example", "slug": "react-navigation-example",
"privacy": "public", "privacy": "public",
"sdkVersion": "34.0.0", "sdkVersion": "35.0.0",
"platforms": [ "platforms": [
"ios", "ios",
"android", "android",
"web" "web"
], ],
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
"splash": { "splash": {
"image": "./assets/splash.png", "image": "./assets/splash.png",
@@ -30,4 +29,4 @@
}, },
"displayName": "React Navigation Example", "displayName": "React Navigation Example",
"name": "ReactNavigationExample" "name": "ReactNavigationExample"
} }

View File

@@ -28,6 +28,9 @@ target 'ReactNavigationExample' do
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
pod 'RNGestureHandler', :podspec => '../node_modules/react-native-gesture-handler/RNGestureHandler.podspec' pod 'RNGestureHandler', :podspec => '../node_modules/react-native-gesture-handler/RNGestureHandler.podspec'
pod 'RNReanimated', :podspec => '../node_modules/react-native-reanimated/RNReanimated.podspec' pod 'RNReanimated', :podspec => '../node_modules/react-native-reanimated/RNReanimated.podspec'
pod 'RNScreens', :podspec => '../node_modules/react-native-screens/RNScreens.podspec'
use_unimodules! use_unimodules!
pod 'react-native-safe-area-context', :path => '../node_modules/react-native-safe-area-context'
end end

View File

@@ -1,31 +1,31 @@
PODS: PODS:
- boost-for-react-native (1.63.0) - boost-for-react-native (1.63.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EXAppLoaderProvider (6.0.0) - EXAppLoaderProvider (7.0.0)
- EXConstants (6.0.0): - EXConstants (7.0.0):
- UMConstantsInterface - UMConstantsInterface
- UMCore - UMCore
- EXFileSystem (6.0.2): - EXFileSystem (7.0.0):
- UMCore - UMCore
- UMFileSystemInterface - UMFileSystemInterface
- EXFont (6.0.1): - EXFont (7.0.0):
- UMCore - UMCore
- UMFontInterface - UMFontInterface
- EXKeepAwake (6.0.0): - EXKeepAwake (7.0.0):
- UMCore - UMCore
- EXLinearGradient (6.0.0): - EXLinearGradient (7.0.0):
- UMCore - UMCore
- EXLocation (6.0.0): - EXLocation (7.0.0):
- UMCore - UMCore
- UMPermissionsInterface - UMPermissionsInterface
- UMTaskManagerInterface - UMTaskManagerInterface
- EXPermissions (6.0.0): - EXPermissions (7.0.0):
- UMCore - UMCore
- UMPermissionsInterface - UMPermissionsInterface
- EXSQLite (6.0.0): - EXSQLite (7.0.0):
- UMCore - UMCore
- UMFileSystemInterface - UMFileSystemInterface
- EXWebBrowser (6.0.0): - EXWebBrowser (7.0.1):
- UMCore - UMCore
- Folly (2018.10.22.00): - Folly (2018.10.22.00):
- boost-for-react-native - boost-for-react-native
@@ -34,6 +34,8 @@ PODS:
- glog (0.3.5) - glog (0.3.5)
- React (0.59.10): - React (0.59.10):
- React/Core (= 0.59.10) - React/Core (= 0.59.10)
- react-native-safe-area-context (0.3.6):
- React
- React/Core (0.59.10): - React/Core (0.59.10):
- yoga (= 0.59.10.React) - yoga (= 0.59.10.React)
- React/CxxBridge (0.59.10): - React/CxxBridge (0.59.10):
@@ -89,23 +91,25 @@ PODS:
- React/RCTBlob - React/RCTBlob
- RNGestureHandler (1.3.0): - RNGestureHandler (1.3.0):
- React - React
- RNReanimated (1.1.0): - RNReanimated (1.2.0):
- React - React
- UMBarCodeScannerInterface (3.0.0) - RNScreens (2.0.0-alpha.4):
- UMCameraInterface (3.0.0) - React
- UMConstantsInterface (3.0.0) - UMBarCodeScannerInterface (4.0.0)
- UMCore (3.0.2) - UMCameraInterface (4.0.0)
- UMFaceDetectorInterface (3.0.0) - UMConstantsInterface (4.0.0)
- UMFileSystemInterface (3.0.0) - UMCore (4.0.0)
- UMFontInterface (3.0.0) - UMFaceDetectorInterface (4.0.0)
- UMImageLoaderInterface (3.0.0) - UMFileSystemInterface (4.0.0)
- UMPermissionsInterface (3.0.0) - UMFontInterface (4.0.0)
- UMReactNativeAdapter (3.0.0): - UMImageLoaderInterface (4.0.0)
- UMPermissionsInterface (4.0.0)
- UMReactNativeAdapter (5.0.0-alpha.0):
- React - React
- UMCore - UMCore
- UMFontInterface - UMFontInterface
- UMSensorsInterface (3.0.0) - UMSensorsInterface (4.0.0)
- UMTaskManagerInterface (3.0.0) - UMTaskManagerInterface (4.0.0)
- yoga (0.59.10.React) - yoga (0.59.10.React)
DEPENDENCIES: DEPENDENCIES:
@@ -122,6 +126,7 @@ DEPENDENCIES:
- EXWebBrowser (from `../node_modules/expo-web-browser/ios`) - EXWebBrowser (from `../node_modules/expo-web-browser/ios`)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React/Core (from `../node_modules/react-native`) - React/Core (from `../node_modules/react-native`)
- React/CxxBridge (from `../node_modules/react-native`) - React/CxxBridge (from `../node_modules/react-native`)
- React/DevSupport (from `../node_modules/react-native`) - React/DevSupport (from `../node_modules/react-native`)
@@ -138,6 +143,7 @@ DEPENDENCIES:
- React/RCTWebSocket (from `../node_modules/react-native`) - React/RCTWebSocket (from `../node_modules/react-native`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler/RNGestureHandler.podspec`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler/RNGestureHandler.podspec`)
- RNReanimated (from `../node_modules/react-native-reanimated/RNReanimated.podspec`) - RNReanimated (from `../node_modules/react-native-reanimated/RNReanimated.podspec`)
- RNScreens (from `../node_modules/react-native-screens/RNScreens.podspec`)
- UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`) - UMBarCodeScannerInterface (from `../node_modules/unimodules-barcode-scanner-interface/ios`)
- UMCameraInterface (from `../node_modules/unimodules-camera-interface/ios`) - UMCameraInterface (from `../node_modules/unimodules-camera-interface/ios`)
- UMConstantsInterface (from `../node_modules/unimodules-constants-interface/ios`) - UMConstantsInterface (from `../node_modules/unimodules-constants-interface/ios`)
@@ -147,7 +153,7 @@ DEPENDENCIES:
- UMFontInterface (from `../node_modules/unimodules-font-interface/ios`) - UMFontInterface (from `../node_modules/unimodules-font-interface/ios`)
- UMImageLoaderInterface (from `../node_modules/unimodules-image-loader-interface/ios`) - UMImageLoaderInterface (from `../node_modules/unimodules-image-loader-interface/ios`)
- UMPermissionsInterface (from `../node_modules/unimodules-permissions-interface/ios`) - UMPermissionsInterface (from `../node_modules/unimodules-permissions-interface/ios`)
- "UMReactNativeAdapter (from `../node_modules/@unimodules/react-native-adapter/ios`)" - "UMReactNativeAdapter (from `../node_modules/react-native-unimodules/node_modules/@unimodules/react-native-adapter/ios`)"
- UMSensorsInterface (from `../node_modules/unimodules-sensors-interface/ios`) - UMSensorsInterface (from `../node_modules/unimodules-sensors-interface/ios`)
- UMTaskManagerInterface (from `../node_modules/unimodules-task-manager-interface/ios`) - UMTaskManagerInterface (from `../node_modules/unimodules-task-manager-interface/ios`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`) - yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -195,10 +201,14 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
React: React:
:path: "../node_modules/react-native" :path: "../node_modules/react-native"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
RNGestureHandler: RNGestureHandler:
:podspec: "../node_modules/react-native-gesture-handler/RNGestureHandler.podspec" :podspec: "../node_modules/react-native-gesture-handler/RNGestureHandler.podspec"
RNReanimated: RNReanimated:
:podspec: "../node_modules/react-native-reanimated/RNReanimated.podspec" :podspec: "../node_modules/react-native-reanimated/RNReanimated.podspec"
RNScreens:
:podspec: "../node_modules/react-native-screens/RNScreens.podspec"
UMBarCodeScannerInterface: UMBarCodeScannerInterface:
:path: !ruby/object:Pathname :path: !ruby/object:Pathname
path: "../node_modules/unimodules-barcode-scanner-interface/ios" path: "../node_modules/unimodules-barcode-scanner-interface/ios"
@@ -228,7 +238,7 @@ EXTERNAL SOURCES:
path: "../node_modules/unimodules-permissions-interface/ios" path: "../node_modules/unimodules-permissions-interface/ios"
UMReactNativeAdapter: UMReactNativeAdapter:
:path: !ruby/object:Pathname :path: !ruby/object:Pathname
path: "../node_modules/@unimodules/react-native-adapter/ios" path: "../node_modules/react-native-unimodules/node_modules/@unimodules/react-native-adapter/ios"
UMSensorsInterface: UMSensorsInterface:
:path: !ruby/object:Pathname :path: !ruby/object:Pathname
path: "../node_modules/unimodules-sensors-interface/ios" path: "../node_modules/unimodules-sensors-interface/ios"
@@ -241,35 +251,37 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
EXAppLoaderProvider: 7a8185228d8ba9e689a0e2d6d957fe9bdd49c8a0 EXAppLoaderProvider: 5d348813a9cf09b03bbe5b8b55437bc1bfbddbd1
EXConstants: 5d81e84ca71b9a552529889cc798b4a04e9e22b3 EXConstants: 31e5318521be6175009af6ccd3e97dedf39da96a
EXFileSystem: 091907902fcec9f9182b656fdead41a82f30986a EXFileSystem: 7e53a2c30a2eb6987ba6d5158ab908f947523228
EXFont: c862449210fc86aa11d24a202cb22c71a0d39609 EXFont: 71d07dc5d2153db7d1a23f1e0cc1b6341d55c432
EXKeepAwake: e7cb6516675052b12a7d23291e33078b4239653a EXKeepAwake: d4caf9a1a7691126ade4ca0b76592e93250a8f29
EXLinearGradient: 40781b77e58f844c8dc4ad310dc9755b4d3792a7 EXLinearGradient: ebfd46cb98a46330213e4945b96227d98f624054
EXLocation: 4eb76115832f08b1e78003b335c210e18fa60424 EXLocation: 452a1d9edceb1b93deb86fd785236606d33742dd
EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5 EXPermissions: df10ad83df2f6b647aec304619354f8ab48d5f63
EXSQLite: 8dab6a5ab1b78be7925073d6071eb22095d4dda6 EXSQLite: ddc1e6727bd7d36e649f07590fe3ae5511c1f039
EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30 EXWebBrowser: 18924c3d2a3a1aa95d413672f058beff589c80f4
Folly: de497beb10f102453a1afa9edbf8cf8a251890de Folly: de497beb10f102453a1afa9edbf8cf8a251890de
glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d glog: aefd1eb5dda2ab95ba0938556f34b98e2da3a60d
React: 36d0768f9e93be2473b37e7fa64f92c1d5341eef React: 36d0768f9e93be2473b37e7fa64f92c1d5341eef
react-native-safe-area-context: e380a6f783ccaec848e2f3cc8eb205a62362950d
RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0 RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0
RNReanimated: 7a52c90473b5e81c13408d40d797b98387eaddde RNReanimated: 1b52415c4302f198cb581282a0166690bad62c43
UMBarCodeScannerInterface: 84ea2d6b58ff0dc27ef9b68bab71286be18ee020 RNScreens: 84be1a2369a580beb2fea0755cc62ef16b83fffe
UMCameraInterface: 26b26005d1756a0d5f4f04f1e168e39ea9154535 UMBarCodeScannerInterface: d5a6fdc98ed6241225b0a8432a7f4e2b397668bc
UMConstantsInterface: 038bacb19de12b6fd328c589122c8dc977cccf61 UMCameraInterface: 68870a3197fee85bd5afca5609ba4a5b7257d19d
UMCore: 733094f43f7244c60ce1f0592d00013ed68fa52c UMConstantsInterface: d25b8e8887ca7aaf568c06caf08f4d40734ee4ef
UMFaceDetectorInterface: c9c3ae4cb045421283667a1698c2f31331f55e3f UMCore: 402cee150324974974f5c32b5404d8af65e4cff5
UMFileSystemInterface: e9adc71027017de38eaf7d05fa58b2848ecb3797 UMFaceDetectorInterface: 7b4f1a92f0c726b58b086296048efe193b570678
UMFontInterface: f0c5846977ee8a93d7cfa8ae7e666772c727d195 UMFileSystemInterface: aadb9a67aa6470d7ebc06cf04dc54fee6781ac48
UMImageLoaderInterface: 36e54e570acc4d720856f03ceebc441f73ea472c UMFontInterface: 2d3c128285086bbed3d2a650f1d698323ef3b25a
UMPermissionsInterface: 938d010c74c43fcefc9bb990633a7c5a1631267e UMImageLoaderInterface: 2829a7571a12d2e754c73c55ffe7e327d8402c7d
UMReactNativeAdapter: 131ea2b944ade8035f0b54c6570c405f6000548d UMPermissionsInterface: b6a6e96db0f4011a25aaca14e6022529dd3d6e4e
UMSensorsInterface: 0ed023ce9b96f2ca6fada7bda05b7760da60b293 UMReactNativeAdapter: e93109c6de5ea830d4a78d08f3a65b8d33a143a3
UMTaskManagerInterface: 8664abd37a00715727e60df9ecd65e42ba47b548 UMSensorsInterface: cf59dd7602764a2419e00167429be3e4be39c61d
UMTaskManagerInterface: 1e70fe58b872355f0ecb44fb81bb1a16484047f0
yoga: 684513b14b03201579ba3cee20218c9d1298b0cc yoga: 684513b14b03201579ba3cee20218c9d1298b0cc
PODFILE CHECKSUM: 41592ff50a43d56f905dbf9c7eded4b358264eba PODFILE CHECKSUM: 1276a2dd000c142ccc03272023bb8a6b2d5b9933
COCOAPODS: 1.7.5 COCOAPODS: 1.7.5

View File

@@ -36,7 +36,7 @@ module.exports = {
'react-native', 'react-native',
'react-native-gesture-handler', 'react-native-gesture-handler',
'react-native-reanimated', 'react-native-reanimated',
'react-native-safe-area-view', 'react-native-safe-area-context',
'react-native-screens', 'react-native-screens',
'react-native-paper', 'react-native-paper',
'react-native-tab-view', 'react-native-tab-view',

View File

@@ -1,7 +1,7 @@
{ {
"name": "@react-navigation/example", "name": "@react-navigation/example",
"description": "Demo app to showcase various functionality of React Navigation", "description": "Demo app to showcase various functionality of React Navigation",
"version": "5.0.0-alpha.9", "version": "5.0.0-alpha.15",
"private": true, "private": true,
"workspaces": { "workspaces": {
"nohoist": [ "nohoist": [
@@ -11,9 +11,11 @@
}, },
"scripts": { "scripts": {
"start": "expo start", "start": "expo start",
"web": "expo start --web",
"native": "react-native start", "native": "react-native start",
"android": "react-native run-android", "android": "react-native run-android",
"ios": "react-native run-ios" "ios": "react-native run-ios",
"postinstall": "jetify"
}, },
"dependencies": { "dependencies": {
"@expo/vector-icons": "^10.0.0", "@expo/vector-icons": "^10.0.0",
@@ -21,28 +23,29 @@
"expo": "^35.0.0", "expo": "^35.0.0",
"expo-asset": "~7.0.0", "expo-asset": "~7.0.0",
"query-string": "^6.8.3", "query-string": "^6.8.3",
"react": "^16.10.1", "react": "~16.8.3",
"react-dom": "^16.8.3", "react-dom": "~16.8.3",
"react-native": "^0.61.2", "react-native": "~0.59.10",
"react-native-gesture-handler": "~1.3.0", "react-native-gesture-handler": "~1.3.0",
"react-native-paper": "^3.0.0-alpha.3", "react-native-paper": "^3.0.0-alpha.7",
"react-native-reanimated": "~1.2.0", "react-native-reanimated": "~1.2.0",
"react-native-screens": "~1.0.0-alpha.23", "react-native-safe-area-context": "~0.3.6",
"react-native-screens": "^2.0.0-alpha.5",
"react-native-tab-view": "2.10.0", "react-native-tab-view": "2.10.0",
"react-native-unimodules": "^0.7.0-rc.1", "react-native-unimodules": "^0.7.0-rc.1",
"react-native-web": "^0.11.7",
"scheduler": "^0.16.1", "scheduler": "^0.16.1",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"use-subscription": "^1.1.1" "use-subscription": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.6.2", "@babel/core": "^7.6.2",
"@expo/webpack-config": "^0.7.12",
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.19",
"babel-preset-expo": "^7.0.0", "babel-preset-expo": "^7.0.0",
"expo-cli": "^3.1.2", "expo-cli": "^3.3.0",
"jetifier": "^1.6.4",
"typescript": "^3.6.3" "typescript": "^3.6.3"
},
"resolutions": {
"react-native-safe-area-view": "0.14.7"
} }
} }

View File

@@ -14,11 +14,13 @@ type AuthStackParams = {
'sign-in': undefined; 'sign-in': undefined;
}; };
const SplashScreen = () => ( const SplashScreen = () => {
<View style={styles.content}> return (
<ActivityIndicator /> <View style={styles.content}>
</View> <ActivityIndicator />
); </View>
);
};
const SignInScreen = ({ const SignInScreen = ({
setUserToken, setUserToken,
@@ -74,7 +76,7 @@ export default function SimpleStackScreen({ navigation }: Props) {
}, []); }, []);
navigation.setOptions({ navigation.setOptions({
header: null, headerShown: false,
}); });
return ( return (

View File

@@ -1,21 +1,24 @@
import * as React from 'react'; import * as React from 'react';
import { MaterialIcons } from '@expo/vector-icons'; import { MaterialCommunityIcons } from '@expo/vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
// @ts-ignore import TouchableBounce from '../Shared/TouchableBounce';
import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
import Contacts from '../Shared/Contacts'; import Contacts from '../Shared/Contacts';
import Chat from '../Shared/Chat'; import Chat from '../Shared/Chat';
import SimpleStackScreen from './SimpleStack'; import SimpleStackScreen from './SimpleStack';
const getTabBarIcon = (name: string) => ({ const getTabBarIcon = (name: string) => ({
tintColor, color,
horizontal, horizontal,
}: { }: {
tintColor: string; color: string;
horizontal: boolean; horizontal: boolean;
}) => ( }) => (
<MaterialIcons name={name} color={tintColor} size={horizontal ? 17 : 24} /> <MaterialCommunityIcons
name={name}
color={color}
size={horizontal ? 17 : 24}
/>
); );
type BottomTabParams = { type BottomTabParams = {
@@ -34,7 +37,7 @@ export default function BottomTabsScreen() {
name="article" name="article"
options={{ options={{
title: 'Article', title: 'Article',
tabBarIcon: getTabBarIcon('chrome-reader-mode'), tabBarIcon: getTabBarIcon('file-document-box'),
tabBarButtonComponent: TouchableBounce, tabBarButtonComponent: TouchableBounce,
}} }}
> >
@@ -47,7 +50,7 @@ export default function BottomTabsScreen() {
component={Chat} component={Chat}
options={{ options={{
title: 'Chat', title: 'Chat',
tabBarIcon: getTabBarIcon('chat-bubble'), tabBarIcon: getTabBarIcon('message-reply'),
tabBarButtonComponent: TouchableBounce, tabBarButtonComponent: TouchableBounce,
}} }}
/> />
@@ -65,7 +68,7 @@ export default function BottomTabsScreen() {
component={Albums} component={Albums}
options={{ options={{
title: 'Albums', title: 'Albums',
tabBarIcon: getTabBarIcon('photo-album'), tabBarIcon: getTabBarIcon('image-album'),
tabBarButtonComponent: TouchableBounce, tabBarButtonComponent: TouchableBounce,
}} }}
/> />

View File

@@ -19,27 +19,29 @@ type CompatStackParams = {
const ArticleScreen: CompatScreenType< const ArticleScreen: CompatScreenType<
StackNavigationProp<CompatStackParams, 'Article'> StackNavigationProp<CompatStackParams, 'Article'>
> = ({ navigation }) => ( > = ({ navigation }) => {
<React.Fragment> return (
<View style={styles.buttons}> <React.Fragment>
<Button <View style={styles.buttons}>
mode="contained" <Button
onPress={() => navigation.push('Album')} mode="contained"
style={styles.button} onPress={() => navigation.push('Album')}
> style={styles.button}
Push album >
</Button> Push album
<Button </Button>
mode="outlined" <Button
onPress={() => navigation.goBack()} mode="outlined"
style={styles.button} onPress={() => navigation.goBack()}
> style={styles.button}
Go back >
</Button> Go back
</View> </Button>
<Article author={{ name: navigation.getParam('author') }} /> </View>
</React.Fragment> <Article author={{ name: navigation.getParam('author') }} />
); </React.Fragment>
);
};
ArticleScreen.navigationOptions = ({ navigation }) => ({ ArticleScreen.navigationOptions = ({ navigation }) => ({
title: `Article by ${navigation.getParam('author')}`, title: `Article by ${navigation.getParam('author')}`,
@@ -47,27 +49,29 @@ ArticleScreen.navigationOptions = ({ navigation }) => ({
const AlbumsScreen: CompatScreenType< const AlbumsScreen: CompatScreenType<
StackNavigationProp<CompatStackParams> StackNavigationProp<CompatStackParams>
> = ({ navigation }) => ( > = ({ navigation }) => {
<React.Fragment> return (
<View style={styles.buttons}> <React.Fragment>
<Button <View style={styles.buttons}>
mode="contained" <Button
onPress={() => navigation.push('Article', { author: 'Babel fish' })} mode="contained"
style={styles.button} onPress={() => navigation.push('Article', { author: 'Babel fish' })}
> style={styles.button}
Push article >
</Button> Push article
<Button </Button>
mode="outlined" <Button
onPress={() => navigation.goBack()} mode="outlined"
style={styles.button} onPress={() => navigation.goBack()}
> style={styles.button}
Go back >
</Button> Go back
</View> </Button>
<Albums /> </View>
</React.Fragment> <Albums />
); </React.Fragment>
);
};
const CompatStack = createCompatNavigatorFactory(createStackNavigator)< const CompatStack = createCompatNavigatorFactory(createStackNavigator)<
StackNavigationProp<CompatStackParams> StackNavigationProp<CompatStackParams>
@@ -92,7 +96,7 @@ export default function CompatStackScreen({
navigation: StackNavigationProp<{}>; navigation: StackNavigationProp<{}>;
}) { }) {
navigation.setOptions({ navigation.setOptions({
header: null, headerShown: false,
}); });
return <CompatStack />; return <CompatStack />;

View File

@@ -24,7 +24,7 @@ export default function MaterialBottomTabsScreen() {
name="article" name="article"
options={{ options={{
tabBarLabel: 'Article', tabBarLabel: 'Article',
tabBarIcon: 'chrome-reader-mode', tabBarIcon: 'file-document-box',
tabBarColor: '#C9E7F8', tabBarColor: '#C9E7F8',
}} }}
> >
@@ -37,7 +37,7 @@ export default function MaterialBottomTabsScreen() {
component={Chat} component={Chat}
options={{ options={{
tabBarLabel: 'Chat', tabBarLabel: 'Chat',
tabBarIcon: 'chat-bubble', tabBarIcon: 'message-reply',
tabBarColor: '#9FD5C9', tabBarColor: '#9FD5C9',
tabBarBadge: true, tabBarBadge: true,
}} }}
@@ -56,7 +56,7 @@ export default function MaterialBottomTabsScreen() {
component={Albums} component={Albums}
options={{ options={{
tabBarLabel: 'Albums', tabBarLabel: 'Albums',
tabBarIcon: 'photo-album', tabBarIcon: 'image-album',
tabBarColor: '#FAD4D6', tabBarColor: '#FAD4D6',
}} }}
/> />

View File

@@ -0,0 +1,131 @@
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { Button } from 'react-native-paper';
import { useSafeArea } from 'react-native-safe-area-context';
import { RouteProp, ParamListBase } from '@react-navigation/core';
import {
createStackNavigator,
StackNavigationProp,
TransitionPresets,
} from '@react-navigation/stack';
import Article from '../Shared/Article';
import Albums from '../Shared/Albums';
type SimpleStackParams = {
article: { author: string };
album: undefined;
};
type SimpleStackNavigation = StackNavigationProp<SimpleStackParams>;
const ArticleScreen = ({
navigation,
route,
}: {
navigation: SimpleStackNavigation;
route: RouteProp<SimpleStackParams, 'article'>;
}) => {
const insets = useSafeArea();
return (
<React.Fragment>
<View style={[styles.buttons, { marginTop: insets.top }]}>
<Button
mode="contained"
onPress={() => navigation.push('album')}
style={styles.button}
>
Push album
</Button>
<Button
mode="outlined"
onPress={() => navigation.goBack()}
style={styles.button}
>
Go back
</Button>
</View>
<Article author={{ name: route.params.author }} />
</React.Fragment>
);
};
const AlbumsScreen = ({
navigation,
}: {
navigation: SimpleStackNavigation;
}) => {
const insets = useSafeArea();
return (
<React.Fragment>
<View style={[styles.buttons, { marginTop: insets.top }]}>
<Button
mode="contained"
onPress={() => navigation.push('article', { author: 'Babel fish' })}
style={styles.button}
>
Push article
</Button>
<Button
mode="outlined"
onPress={() => navigation.goBack()}
style={styles.button}
>
Go back
</Button>
</View>
<Albums />
</React.Fragment>
);
};
const ModalPresentationStack = createStackNavigator<SimpleStackParams>();
type Props = {
options?: React.ComponentProps<typeof ModalPresentationStack.Navigator>;
navigation: StackNavigationProp<ParamListBase>;
};
export default function SimpleStackScreen({ navigation, options }: Props) {
navigation.setOptions({
headerShown: false,
});
return (
<ModalPresentationStack.Navigator
mode="modal"
headerMode="none"
screenOptions={{
...TransitionPresets.ModalPresentationIOS,
cardOverlayEnabled: true,
gestureEnabled: true,
}}
{...options}
>
<ModalPresentationStack.Screen
name="article"
component={ArticleScreen}
options={({ route }) => ({
title: `Article by ${route.params.author}`,
})}
initialParams={{ author: 'Gandalf' }}
/>
<ModalPresentationStack.Screen
name="album"
component={AlbumsScreen}
options={{ title: 'Album' }}
/>
</ModalPresentationStack.Navigator>
);
}
const styles = StyleSheet.create({
buttons: {
flexDirection: 'row',
padding: 8,
},
button: {
margin: 8,
},
});

View File

@@ -0,0 +1,23 @@
import * as React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function NativeStack() {
return (
<View style={styles.container}>
<Text style={styles.text}>Currently not supported on Expo :(</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#eceff1',
},
text: {
fontSize: 16,
color: '#999',
},
});

View File

@@ -0,0 +1,138 @@
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { Button } from 'react-native-paper';
// eslint-disable-next-line import/no-unresolved
import { useScreens } from 'react-native-screens';
import {
RouteProp,
ParamListBase,
useFocusEffect,
} from '@react-navigation/core';
import { DrawerNavigationProp } from '@react-navigation/drawer';
import { StackNavigationProp } from '@react-navigation/stack';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from '@react-navigation/native-stack';
import Article from '../Shared/Article';
import Albums from '../Shared/Albums';
type NativeStackParams = {
article: { author: string };
album: undefined;
};
type NativeStackNavigation = NativeStackNavigationProp<NativeStackParams>;
const ArticleScreen = ({
navigation,
route,
}: {
navigation: NativeStackNavigation;
route: RouteProp<NativeStackParams, 'article'>;
}) => (
<React.Fragment>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.push('album')}
style={styles.button}
>
Push album
</Button>
<Button
mode="outlined"
onPress={() => navigation.goBack()}
style={styles.button}
>
Go back
</Button>
</View>
<Article author={{ name: route.params.author }} />
</React.Fragment>
);
const AlbumsScreen = ({
navigation,
}: {
navigation: NativeStackNavigation;
}) => (
<React.Fragment>
<View style={styles.buttons}>
<Button
mode="contained"
onPress={() => navigation.push('article', { author: 'Babel fish' })}
style={styles.button}
>
Push article
</Button>
<Button
mode="outlined"
onPress={() => navigation.goBack()}
style={styles.button}
>
Go back
</Button>
</View>
<Albums />
</React.Fragment>
);
const NativeStack = createNativeStackNavigator<NativeStackParams>();
type Props = {
navigation: StackNavigationProp<ParamListBase>;
};
export default function NativeStackScreen({ navigation }: Props) {
navigation.setOptions({
headerShown: false,
});
useFocusEffect(
React.useCallback(() => {
const drawer = navigation.dangerouslyGetParent() as DrawerNavigationProp<
ParamListBase
>;
navigation.setOptions({ gestureEnabled: false });
drawer.setOptions({ drawerLockMode: 'locked-closed' });
return () => {
navigation.setOptions({ gestureEnabled: true });
drawer.setOptions({ drawerLockMode: 'unlocked' });
};
}, [navigation])
);
return (
<NativeStack.Navigator>
<NativeStack.Screen
name="article"
component={ArticleScreen}
options={({ route }) => ({
title: `Article by ${route.params.author}`,
})}
initialParams={{ author: 'Gandalf' }}
/>
<NativeStack.Screen
name="album"
component={AlbumsScreen}
options={{ title: 'Album' }}
/>
</NativeStack.Navigator>
);
}
// eslint-disable-next-line react-hooks/rules-of-hooks
useScreens(true);
const styles = StyleSheet.create({
buttons: {
flexDirection: 'row',
padding: 8,
},
button: {
margin: 8,
},
});

View File

@@ -5,6 +5,7 @@ import { RouteProp, ParamListBase } from '@react-navigation/core';
import { import {
createStackNavigator, createStackNavigator,
StackNavigationProp, StackNavigationProp,
StackNavigationOptions,
} from '@react-navigation/stack'; } from '@react-navigation/stack';
import Article from '../Shared/Article'; import Article from '../Shared/Article';
import Albums from '../Shared/Albums'; import Albums from '../Shared/Albums';
@@ -22,64 +23,68 @@ const ArticleScreen = ({
}: { }: {
navigation: SimpleStackNavigation; navigation: SimpleStackNavigation;
route: RouteProp<SimpleStackParams, 'article'>; route: RouteProp<SimpleStackParams, 'article'>;
}) => ( }) => {
<React.Fragment> return (
<View style={styles.buttons}> <React.Fragment>
<Button <View style={styles.buttons}>
mode="contained" <Button
onPress={() => navigation.push('album')} mode="contained"
style={styles.button} onPress={() => navigation.push('album')}
> style={styles.button}
Push album >
</Button> Push album
<Button </Button>
mode="outlined" <Button
onPress={() => navigation.goBack()} mode="outlined"
style={styles.button} onPress={() => navigation.goBack()}
> style={styles.button}
Go back >
</Button> Go back
</View> </Button>
<Article author={{ name: route.params.author }} /> </View>
</React.Fragment> <Article author={{ name: route.params.author }} />
); </React.Fragment>
);
};
const AlbumsScreen = ({ const AlbumsScreen = ({
navigation, navigation,
}: { }: {
navigation: SimpleStackNavigation; navigation: SimpleStackNavigation;
}) => ( }) => {
<React.Fragment> return (
<View style={styles.buttons}> <React.Fragment>
<Button <View style={styles.buttons}>
mode="contained" <Button
onPress={() => navigation.push('article', { author: 'Babel fish' })} mode="contained"
style={styles.button} onPress={() => navigation.push('article', { author: 'Babel fish' })}
> style={styles.button}
Push article >
</Button> Push article
<Button </Button>
mode="outlined" <Button
onPress={() => navigation.goBack()} mode="outlined"
style={styles.button} onPress={() => navigation.goBack()}
> style={styles.button}
Go back >
</Button> Go back
</View> </Button>
<Albums /> </View>
</React.Fragment> <Albums />
); </React.Fragment>
);
};
const SimpleStack = createStackNavigator<SimpleStackParams>(); const SimpleStack = createStackNavigator<SimpleStackParams>();
type Props = { type Props = {
options?: React.ComponentProps<typeof SimpleStack.Navigator>; options?: StackNavigationOptions;
navigation: StackNavigationProp<ParamListBase>; navigation: StackNavigationProp<ParamListBase>;
}; };
export default function SimpleStackScreen({ navigation, options }: Props) { export default function SimpleStackScreen({ navigation, options }: Props) {
navigation.setOptions({ navigation.setOptions({
header: null, headerShown: false,
}); });
return ( return (

View File

@@ -28,6 +28,10 @@ export default function Albums() {
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
<Image key={i} source={source} style={styles.cover} /> <Image key={i} source={source} style={styles.cover} />
))} ))}
{COVERS.map((source, i) => (
// eslint-disable-next-line react/no-array-index-key
<Image key={i + 'F'} source={source} style={styles.cover} />
))}
</ScrollView> </ScrollView>
); );
} }

View File

@@ -0,0 +1,4 @@
// @ts-ignore
import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce';
export default TouchableBounce;

View File

@@ -0,0 +1,3 @@
import { TouchableOpacity } from 'react-native';
export default TouchableOpacity;

View File

@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { ScrollView, AsyncStorage, YellowBox } from 'react-native'; import { ScrollView, AsyncStorage, YellowBox } from 'react-native';
import LinkingPrefixes from './LinkingPrefixes'; import { MaterialIcons } from '@expo/vector-icons';
import { Appbar, List } from 'react-native-paper'; import { Appbar, List } from 'react-native-paper';
import { Asset } from 'expo-asset'; import { Asset } from 'expo-asset';
import { import {
@@ -22,8 +22,11 @@ import {
StackNavigationProp, StackNavigationProp,
} from '@react-navigation/stack'; } from '@react-navigation/stack';
import SimpleStackScreen from './Screens/SimpleStack'; import LinkingPrefixes from './LinkingPrefixes';
import BottomTabsScreen from './Screens/BottomTabs'; import SimpleStack from './Screens/SimpleStack';
import NativeStack from './Screens/NativeStack';
import ModalPresentationStack from './Screens/ModalPresentationStack';
import BottomTabs from './Screens/BottomTabs';
import MaterialTopTabsScreen from './Screens/MaterialTopTabs'; import MaterialTopTabsScreen from './Screens/MaterialTopTabs';
import MaterialBottomTabs from './Screens/MaterialBottomTabs'; import MaterialBottomTabs from './Screens/MaterialBottomTabs';
import AuthFlow from './Screens/AuthFlow'; import AuthFlow from './Screens/AuthFlow';
@@ -32,31 +35,37 @@ import CompatAPI from './Screens/CompatAPI';
YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']); YellowBox.ignoreWarnings(['Require cycle:', 'Warning: Async Storage']);
type RootDrawerParamList = { type RootDrawerParamList = {
root: undefined; Root: undefined;
Another: undefined;
}; };
type RootStackParamList = { type RootStackParamList = {
home: undefined; Home: undefined;
} & { } & {
[P in keyof typeof SCREENS]: undefined; [P in keyof typeof SCREENS]: undefined;
}; };
const SCREENS = { const SCREENS = {
'simple-stack': { title: 'Simple Stack', component: SimpleStackScreen }, SimpleStack: { title: 'Simple Stack', component: SimpleStack },
'bottom-tabs': { title: 'Bottom Tabs', component: BottomTabsScreen }, NativeStack: { title: 'Native Stack', component: NativeStack },
'material-top-tabs': { ModalPresentationStack: {
title: 'Modal Presentation Stack',
component: ModalPresentationStack,
},
BottomTabs: { title: 'Bottom Tabs', component: BottomTabs },
MaterialTopTabs: {
title: 'Material Top Tabs', title: 'Material Top Tabs',
component: MaterialTopTabsScreen, component: MaterialTopTabsScreen,
}, },
'material-bottom-tabs': { MaterialBottomTabs: {
title: 'Material Bottom Tabs', title: 'Material Bottom Tabs',
component: MaterialBottomTabs, component: MaterialBottomTabs,
}, },
'auth-flow': { AuthFlow: {
title: 'Auth Flow', title: 'Auth Flow',
component: AuthFlow, component: AuthFlow,
}, },
'compat-api': { CompatAPI: {
title: 'Compat Layer', title: 'Compat Layer',
component: CompatAPI, component: CompatAPI,
}, },
@@ -134,7 +143,15 @@ export default function App() {
} }
> >
<Drawer.Navigator> <Drawer.Navigator>
<Drawer.Screen name="root" options={{ title: 'Examples' }}> <Drawer.Screen
name="Root"
options={{
title: 'Examples',
drawerIcon: ({ size, color }) => (
<MaterialIcons size={size} color={color} name="folder" />
),
}}
>
{({ {({
navigation, navigation,
}: { }: {
@@ -142,7 +159,7 @@ export default function App() {
}) => ( }) => (
<Stack.Navigator> <Stack.Navigator>
<Stack.Screen <Stack.Screen
name="home" name="Home"
options={{ options={{
title: 'Examples', title: 'Examples',
headerLeft: () => ( headerLeft: () => (

View File

@@ -0,0 +1,50 @@
const path = require('path');
const fs = require('fs');
const createExpoWebpackConfigAsync = require('@expo/webpack-config');
// eslint-disable-next-line import/no-extraneous-dependencies
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
module.exports = async function(env, argv) {
const config = await createExpoWebpackConfigAsync(env, argv);
config.module.rules.push({
test: /\.(js|ts|tsx)$/,
include: /packages\/.+/,
use: 'babel-loader',
});
config.resolve.plugins = config.resolve.plugins.filter(
p => !(p instanceof ModuleScopePlugin)
);
config.resolve.alias['react'] = path.resolve(
__dirname,
'node_modules',
'react'
);
config.resolve.alias['react-native'] = path.resolve(
__dirname,
'node_modules',
'react-native-web'
);
config.resolve.alias['react-native-web'] = path.resolve(
__dirname,
'node_modules',
'react-native-web'
);
config.resolve.alias[
'@expo/vector-icons/MaterialCommunityIcons'
] = require.resolve('@expo/vector-icons/MaterialCommunityIcons');
fs.readdirSync(path.join(__dirname, '..')).forEach(name => {
config.resolve.alias[`@react-navigation/${name}`] = path.resolve(
__dirname,
'..',
name,
'src'
);
});
return config;
};

View File

@@ -3,6 +3,57 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.15](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.14...@react-navigation/material-bottom-tabs@5.0.0-alpha.15) (2019-10-22)
### Bug Fixes
* navigation drawer sometimes not closing when pressed outside ([0d8cdc8](https://github.com/react-navigation/navigation-ex/commit/0d8cdc8))
# [5.0.0-alpha.14](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.13...@react-navigation/material-bottom-tabs@5.0.0-alpha.14) (2019-10-17)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.12...@react-navigation/material-bottom-tabs@5.0.0-alpha.13) (2019-10-15)
### Bug Fixes
* make it possible to run the example on web ([7a901af](https://github.com/react-navigation/navigation-ex/commit/7a901af))
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.11...@react-navigation/material-bottom-tabs@5.0.0-alpha.12) (2019-10-06)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.10...@react-navigation/material-bottom-tabs@5.0.0-alpha.11) (2019-10-03)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
# [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.9...@react-navigation/material-bottom-tabs@5.0.0-alpha.10) (2019-10-03) # [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-bottom-tabs@5.0.0-alpha.9...@react-navigation/material-bottom-tabs@5.0.0-alpha.10) (2019-10-03)
**Note:** Version bump only for package @react-navigation/material-bottom-tabs **Note:** Version bump only for package @react-navigation/material-bottom-tabs

View File

@@ -27,7 +27,7 @@ export default function App() {
component={Article} component={Article}
options={{ options={{
tabBarLabel: 'Article', tabBarLabel: 'Article',
tabBarIcon: 'chrome-reader-mode', tabBarIcon: 'file-document-box',
}} }}
/> />
<MaterialBottomTabs.Screen <MaterialBottomTabs.Screen
@@ -35,7 +35,7 @@ export default function App() {
component={Chat} component={Chat}
options={{ options={{
tabBarLabel: 'Chat', tabBarLabel: 'Chat',
tabBarIcon: 'chat-bubble', tabBarIcon: 'message-reply',
}} }}
/> />
<MaterialBottomTabs.Screen <MaterialBottomTabs.Screen
@@ -51,7 +51,7 @@ export default function App() {
component={Albums} component={Albums}
options={{ options={{
tabBarLabel: 'Albums', tabBarLabel: 'Albums',
tabBarIcon: 'photo-album', tabBarIcon: 'image-album',
}} }}
/> />
</MaterialBottomTabs.Navigator> </MaterialBottomTabs.Navigator>

View File

@@ -11,7 +11,7 @@
"material", "material",
"tab" "tab"
], ],
"version": "5.0.0-alpha.10", "version": "5.0.0-alpha.15",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -42,9 +42,9 @@
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.17",
"@types/react-native-vector-icons": "^6.4.4", "@types/react-native-vector-icons": "^6.4.4",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "16.8.3", "react": "~16.8.3",
"react-native": "0.59.10", "react-native": "~0.59.10",
"react-native-paper": "^3.0.0-alpha.3", "react-native-paper": "^3.0.0-alpha.7",
"react-native-vector-icons": "^6.6.0", "react-native-vector-icons": "^6.6.0",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
@@ -52,7 +52,7 @@
"@react-navigation/core": "^5.0.0-alpha.0", "@react-navigation/core": "^5.0.0-alpha.0",
"react": "*", "react": "*",
"react-native": "*", "react-native": "*",
"react-native-paper": "^3.0.0-alpha.3", "react-native-paper": "^3.0.0-alpha.0",
"react-native-vector-icons": "^6.0.0" "react-native-vector-icons": "^6.0.0"
}, },
"@react-native-community/bob": { "@react-native-community/bob": {

View File

@@ -60,7 +60,7 @@ export type MaterialBottomTabNavigationOptions = {
/** /**
* String referring to an icon in the `MaterialCommunityIcons` set, or a * String referring to an icon in the `MaterialCommunityIcons` set, or a
* function that given { focused: boolean, tintColor: string } returns a React.Node to display in the navigation bar. * function that given { focused: boolean, color: string } returns a React.Node to display in the navigation bar.
*/ */
tabBarIcon?: tabBarIcon?:
| string | string
@@ -96,6 +96,16 @@ export type MaterialBottomTabDescriptorMap = {
export type MaterialBottomTabNavigationConfig = Partial< export type MaterialBottomTabNavigationConfig = Partial<
Omit< Omit<
React.ComponentProps<typeof BottomNavigation>, React.ComponentProps<typeof BottomNavigation>,
'navigationState' | 'onIndexChange' | 'renderScene' | 'navigationState'
| 'onIndexChange'
| 'onTabPress'
| 'renderScene'
| 'renderLabel'
| 'renderIcon'
| 'getAccessibilityLabel'
| 'getBadge'
| 'getColor'
| 'getLabelText'
| 'getTestID'
> >
>; >;

View File

@@ -1,8 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import { BottomNavigation } from 'react-native-paper'; import { BottomNavigation } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { Route } from '@react-navigation/core';
import { TabNavigationState, TabActions } from '@react-navigation/routers'; import { TabNavigationState, TabActions } from '@react-navigation/routers';
import { import {
@@ -17,7 +16,7 @@ type Props = MaterialBottomTabNavigationConfig & {
descriptors: MaterialBottomTabDescriptorMap; descriptors: MaterialBottomTabDescriptorMap;
}; };
type Scene = { route: Route<string> }; type Scene = { route: { key: string } };
export default class MaterialBottomTabView extends React.PureComponent<Props> { export default class MaterialBottomTabView extends React.PureComponent<Props> {
private getColor = ({ route }: Scene) => { private getColor = ({ route }: Scene) => {
@@ -35,7 +34,7 @@ export default class MaterialBottomTabView extends React.PureComponent<Props> {
? options.tabBarLabel ? options.tabBarLabel
: typeof options.title === 'string' : typeof options.title === 'string'
? options.title ? options.title
: route.name; : ((route as any) as { name: string }).name;
}; };
private getAccessibilityLabel = ({ route }: Scene) => { private getAccessibilityLabel = ({ route }: Scene) => {
@@ -49,9 +48,9 @@ export default class MaterialBottomTabView extends React.PureComponent<Props> {
const label = this.getLabelText({ route }); const label = this.getLabelText({ route });
if (typeof label === 'string') { if (typeof label === 'string') {
return `${label}, tab, ${state.routes.indexOf(route) + 1} of ${ return `${label}, tab, ${state.routes.findIndex(
state.routes.length r => r.key === route.key
}`; ) + 1} of ${state.routes.length}`;
} }
return undefined; return undefined;
@@ -75,7 +74,7 @@ export default class MaterialBottomTabView extends React.PureComponent<Props> {
focused, focused,
color, color,
}: { }: {
route: Route<string>; route: { key: string };
focused: boolean; focused: boolean;
color: string; color: string;
}) => { }) => {

View File

@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.11...@react-navigation/material-top-tabs@5.0.0-alpha.12) (2019-10-15)
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.10...@react-navigation/material-top-tabs@5.0.0-alpha.11) (2019-10-06)
**Note:** Version bump only for package @react-navigation/material-top-tabs
# [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.9...@react-navigation/material-top-tabs@5.0.0-alpha.10) (2019-10-03)
**Note:** Version bump only for package @react-navigation/material-top-tabs
# [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.8...@react-navigation/material-top-tabs@5.0.0-alpha.9) (2019-10-03) # [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/material-top-tabs@5.0.0-alpha.8...@react-navigation/material-top-tabs@5.0.0-alpha.9) (2019-10-03)
**Note:** Version bump only for package @react-navigation/material-top-tabs **Note:** Version bump only for package @react-navigation/material-top-tabs

View File

@@ -10,7 +10,7 @@ Open a Terminal in your project's folder and run,
yarn add @react-navigation/core @react-navigation/material-top-tabs react-native-tab-view yarn add @react-navigation/core @react-navigation/material-top-tabs react-native-tab-view
``` ```
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated). Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated)..
If you are using Expo, to ensure that you get the compatible versions of the libraries, run: If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
@@ -26,28 +26,13 @@ yarn add react-native-reanimated react-native-gesture-handler
If you are using Expo, you are done. Otherwise, continue to the next steps. If you are using Expo, you are done. Otherwise, continue to the next steps.
Next, we need to link these libraries. The steps depends on your React Native version: To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
- **React Native 0.60 and higher** ```sh
cd ios
On newer versions of React Native, [linking is automatic](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md). pod install
cd ..
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run: ```
```sh
cd ios
pod install
cd ..
```
- **React Native 0.59**
If you're on an older React Native version, you need to manually link the dependencies. To do that, run:
```sh
react-native link react-native-reanimated
react-native link react-native-gesture-handler
```
**IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation. **IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation.

View File

@@ -11,7 +11,7 @@
"material", "material",
"tab" "tab"
], ],
"version": "5.0.0-alpha.9", "version": "5.0.0-alpha.12",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -41,8 +41,8 @@
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.17",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "16.8.3", "react": "~16.8.3",
"react-native": "^0.59.8", "react-native": "~0.59.10",
"react-native-gesture-handler": "^1.3.0", "react-native-gesture-handler": "^1.3.0",
"react-native-reanimated": "^1.3.0", "react-native-reanimated": "^1.3.0",
"react-native-tab-view": "^2.10.0", "react-native-tab-view": "^2.10.0",

View File

@@ -64,19 +64,19 @@ export type MaterialTopTabNavigationOptions = {
/** /**
* Title string of a tab displayed in the tab bar or React Element * Title string of a tab displayed in the tab bar or React Element
* or a function that given { focused: boolean, tintColor: string } returns a React.Node, to display in tab bar. * or a function that given { focused: boolean, color: string } returns a React.Node, to display in tab bar.
* When undefined, scene title is used. To hide, see tabBarOptions.showLabel in the previous section. * When undefined, scene title is used. To hide, see tabBarOptions.showLabel in the previous section.
*/ */
tabBarLabel?: tabBarLabel?:
| React.ReactNode | React.ReactNode
| ((props: { focused: boolean; tintColor: string }) => React.ReactNode); | ((props: { focused: boolean; color: string }) => React.ReactNode);
/** /**
* React Element or a function that given { focused: boolean, tintColor: string } returns a React.Node, to display in the tab bar. * React Element or a function that given { focused: boolean, color: string } returns a React.Node, to display in the tab bar.
*/ */
tabBarIcon?: tabBarIcon?:
| React.ReactNode | React.ReactNode
| ((props: { focused: boolean; tintColor: string }) => React.ReactNode); | ((props: { focused: boolean; color: string }) => React.ReactNode);
/** /**
* Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. * Accessibility label for the tab button. This is read by the screen reader when the user taps the tab.
@@ -196,7 +196,7 @@ export type MaterialTopTabBarProps = MaterialTopTabBarOptions &
getLabelText: (props: { getLabelText: (props: {
route: Route<string>; route: Route<string>;
}) => }) =>
| ((scene: { focused: boolean; tintColor: string }) => React.ReactNode) | ((scene: { focused: boolean; color: string }) => React.ReactNode)
| React.ReactNode; | React.ReactNode;
getAccessibilityLabel: (props: { getAccessibilityLabel: (props: {
route: Route<string>; route: Route<string>;

View File

@@ -53,7 +53,7 @@ export default class TabBarTop extends React.PureComponent<
} }
if (typeof label === 'function') { if (typeof label === 'function') {
return label({ focused, tintColor: color }); return label({ focused, color });
} }
return label; return label;
@@ -79,7 +79,7 @@ export default class TabBarTop extends React.PureComponent<
if (options.tabBarIcon !== undefined) { if (options.tabBarIcon !== undefined) {
const icon = const icon =
typeof options.tabBarIcon === 'function' typeof options.tabBarIcon === 'function'
? options.tabBarIcon({ focused, tintColor: color }) ? options.tabBarIcon({ focused, color })
: options.tabBarIcon; : options.tabBarIcon;
return <View style={[styles.icon, iconStyle]}>{icon}</View>; return <View style={[styles.icon, iconStyle]}>{icon}</View>;

View File

@@ -0,0 +1,50 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.4](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.3...@react-navigation/native-stack@5.0.0-alpha.4) (2019-10-22)
### Features
* **native-stack:** add support for large title attributes ([#135](https://github.com/react-navigation/navigation-ex/issues/135)) ([6cf1a04](https://github.com/react-navigation/navigation-ex/commit/6cf1a04))
# [5.0.0-alpha.3](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.2...@react-navigation/native-stack@5.0.0-alpha.3) (2019-10-18)
### Bug Fixes
* remove top margin when header is hidden in native stack. fixes [#131](https://github.com/react-navigation/navigation-ex/issues/131) ([fb726ee](https://github.com/react-navigation/navigation-ex/commit/fb726ee))
# [5.0.0-alpha.2](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native-stack@5.0.0-alpha.1...@react-navigation/native-stack@5.0.0-alpha.2) (2019-10-18)
### Bug Fixes
* fix header font size config in native stack ([#128](https://github.com/react-navigation/navigation-ex/issues/128)) ([477c088](https://github.com/react-navigation/navigation-ex/commit/477c088))
# 5.0.0-alpha.1 (2019-10-15)
### Bug Fixes
* increase hitSlop of back button on Android ([c7da1e4](https://github.com/react-navigation/navigation-ex/commit/c7da1e4))
* update supported options for native stack ([b71f4e5](https://github.com/react-navigation/navigation-ex/commit/b71f4e5))
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))

View File

@@ -0,0 +1,64 @@
# `@react-navigation/native-stack`
Stack navigator for React Native using native primitives for navigation. Uses [`react-native-screens`](https://github.com/kmagiera/react-native-screens) under the hood.
Expo is currently not supported as it includes an older version of `react-native-screens`.
## Installation
Open a Terminal in your project's folder and run,
```sh
yarn add @react-navigation/core @react-navigation/native-stack
```
Now we need to install [`react-native-screens`](https://github.com/kmagiera/react-native-screens).
```sh
yarn add react-native-screens
```
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
```sh
cd ios
pod install
cd ..
```
To finalize installation of `react-native-screens` for Android, add the following two lines to dependencies section in `android/app/build.gradle`:
```gradle
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
```
Make sure to enable `react-native-screens`. This needs to be done before our app renders. To do it, add the following code in your entry file (e.g. `App.js`):
```js
import { useScreens } from 'react-native-screens';
useScreens();
```
## Usage
```js
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<Stack.Navigator>
<Stack.Screen name="home" component={Home} options={{ title: 'Home' }} />
<Stack.Screen name="feed" component={Feed} options={{ title: 'Feed' }} />
<Stack.Screen
name="profile"
component={Profile}
options={{ title: 'Profile' }}
/>
</Stack.Navigator>
);
}
```

View File

@@ -0,0 +1,55 @@
{
"name": "@react-navigation/native-stack",
"description": "Native stack navigator component for iOS and Android",
"keywords": [
"react",
"react-native",
"react-navigation"
],
"version": "5.0.0-alpha.4",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/react-navigation/navigation-ex.git",
"directory": "packages/native-stack"
},
"main": "lib/commonjs/index.js",
"react-native": "src/index.tsx",
"module": "lib/module/index.js",
"types": "lib/typescript/native-stack/src/index.d.ts",
"files": [
"src",
"lib"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"prepare": "bob build",
"clean": "del lib"
},
"dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.9"
},
"devDependencies": {
"@react-native-community/bob": "^0.7.0",
"del-cli": "^2.0.0",
"react-native-screens": "^2.0.0-alpha.5",
"typescript": "^3.5.3"
},
"peerDependencies": {
"@react-navigation/core": "^5.0.0-alpha.0",
"react": "*",
"react-native": "*",
"react-native-screens": "^2.0.0-alpha.5"
},
"@react-native-community/bob": {
"source": "src",
"output": "lib",
"targets": [
"commonjs",
"module",
"typescript"
]
}
}

View File

@@ -0,0 +1,14 @@
/**
* Navigators
*/
export {
default as createNativeStackNavigator,
} from './navigators/createNativeStackNavigator';
/**
* Types
*/
export {
NativeStackNavigationOptions,
NativeStackNavigationProp,
} from './types';

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { createNavigator, useNavigationBuilder } from '@react-navigation/core';
import {
StackRouter,
StackNavigationState,
StackRouterOptions,
} from '@react-navigation/routers';
import {
screensEnabled,
// eslint-disable-next-line import/no-unresolved
} from 'react-native-screens';
import StackView from '../views/StackView';
import {
NativeStackNavigatorProps,
NativeStackNavigationOptions,
} from '../types';
function NativeStackNavigator(props: NativeStackNavigatorProps) {
if (!screensEnabled()) {
throw new Error(
'Native stack is only available if React Native Screens is enabled.'
);
}
const { initialRouteName, children, screenOptions, ...rest } = props;
const { state, descriptors, navigation } = useNavigationBuilder<
StackNavigationState,
StackRouterOptions,
NativeStackNavigationOptions,
{}
>(StackRouter, {
initialRouteName,
children,
screenOptions,
});
return (
<StackView
state={state}
navigation={navigation}
descriptors={descriptors}
{...rest}
/>
);
}
export default createNavigator<
NativeStackNavigationOptions,
typeof NativeStackNavigator
>(NativeStackNavigator);

View File

@@ -0,0 +1,191 @@
import * as React from 'react';
import { StyleProp, ViewStyle } from 'react-native';
import {
DefaultNavigatorOptions,
Descriptor,
NavigationHelpers,
NavigationProp,
ParamListBase,
} from '@react-navigation/core';
import {
StackNavigationState,
StackRouterOptions,
} from '@react-navigation/routers';
export type NativeStackNavigationProp<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = string
> = NavigationProp<
ParamList,
RouteName,
StackNavigationState,
NativeStackNavigationOptions,
{}
> & {
/**
* Push a new screen onto the stack.
*
* @param name Name of the route for the tab.
* @param [params] Params object for the route.
*/
push<RouteName extends keyof ParamList>(
...args: ParamList[RouteName] extends (undefined | any)
? [RouteName] | [RouteName, ParamList[RouteName]]
: [RouteName, ParamList[RouteName]]
): void;
/**
* Pop a screen from the stack.
*/
pop(count?: number): void;
/**
* Pop to the first route in the stack, dismissing all other screens.
*/
popToTop(): void;
};
export type NativeStackNavigationHelpers = NavigationHelpers<ParamListBase, {}>;
export type NativeStackNavigationConfig = {};
export type NativeStackNavigationOptions = {
/**
* String that can be displayed in the header as a fallback for `headerTitle`.
*/
title?: string;
/**
* String to display in the header as title. Defaults to scene `title`.
*/
headerTitle?: string;
/**
* Title to display in the back button.
* Only supported on iOS.
*
* @platform ios
*/
headerBackTitle?: string;
/**
* Whether to show the header.
*/
headerShown?: boolean;
/**
* Boolean indicating whether the navigation bar is translucent.
* Only supported on iOS.
*
* @platform ios
*/
headerTranslucent?: boolean;
/**
* Boolean to set native property to prefer large title header (like in iOS setting).
* Only supported on iOS.
*
* @platform ios
*/
headerLargeTitle?: boolean;
/**
* Function which returns a React Element to display on the right side of the header.
*/
headerRight?: () => React.ReactNode;
/**
* Tint color for the header. Changes the color of back button and title.
*/
headerTintColor?: string;
/**
* Boolean indicating whether to hide the back button in header.
* Only supported on Android.
*
* @platform android
*/
headerHideBackButton?: boolean;
/**
* Boolean indicating whether to hide the elevation shadow on the header.
* Only supported on Android.
*
* @platform android
*/
headerHideShadow?: boolean;
/**
* Style object for header title. Supported properties:
* - backgroundColor
*/
headerStyle?: {
backgroundColor?: string;
};
/**
* Style object for header title. Supported properties:
* - fontFamily
* - fontSize
* - color
*/
headerTitleStyle?: {
fontFamily?: string;
fontSize?: number;
color?: string;
};
/**
* Style object for header large title. Supported properties:
* - fontFamily
* - fontSize
*
* Only supported on iOS.
*
* @platform ios
*/
headerLargeTitleStyle?: {
fontFamily?: string;
fontSize?: number;
};
/**
* Style object for header back title. Supported properties:
* - fontFamily
* - fontSize
*
* Only supported on iOS.
*
* @platform ios
*/
headerBackTitleStyle?: {
fontFamily?: string;
fontSize?: number;
};
/**
* Style object for the scene content.
*/
contentStyle?: StyleProp<ViewStyle>;
/**
* Whether you can use gestures to dismiss this screen. Defaults to `true`.
* Only supported on iOS.
*
* @platform ios
*/
gestureEnabled?: boolean;
/**
* How should the screen be presented.
*/
presentation?: 'modal' | 'transparentModal' | 'push';
/**
* How should the screen should be animated.
* Only supported on Android.
*
* @platform android
*/
animation?: 'default' | 'fade' | 'none';
};
export type NativeStackNavigatorProps = DefaultNavigatorOptions<
NativeStackNavigationOptions
> &
StackRouterOptions &
NativeStackNavigationConfig;
export type NativeStackDescriptor = Descriptor<
ParamListBase,
string,
StackNavigationState,
NativeStackNavigationOptions
>;
export type NativeStackDescriptorMap = {
[key: string]: NativeStackDescriptor;
};

View File

@@ -0,0 +1,71 @@
import * as React from 'react';
import {
// @ts-ignore
ScreenStackHeaderConfig,
// @ts-ignore
ScreenStackHeaderRightView,
// eslint-disable-next-line import/no-unresolved
} from 'react-native-screens';
import { Route } from '@react-navigation/core';
import { NativeStackNavigationOptions } from '../types';
type Props = NativeStackNavigationOptions & {
route: Route<string>;
};
export default function HeaderConfig(props: Props) {
const {
route,
title,
headerRight,
headerTitle,
headerBackTitle,
headerHideBackButton,
headerHideShadow,
headerTintColor,
headerLargeTitle,
headerTranslucent,
headerStyle = {},
headerTitleStyle = {},
headerLargeTitleStyle = {},
headerBackTitleStyle = {},
headerShown,
gestureEnabled,
} = props;
return (
<ScreenStackHeaderConfig
hidden={headerShown === false}
translucent={headerTranslucent === true}
hideShadow={headerHideShadow}
hideBackButton={headerHideBackButton}
title={
headerTitle !== undefined
? headerTitle
: title !== undefined
? title
: route.name
}
titleFontFamily={headerTitleStyle.fontFamily}
titleFontSize={headerTitleStyle.fontSize}
titleColor={
headerTitleStyle.color !== undefined
? headerTitleStyle.color
: headerTintColor
}
backTitle={headerBackTitle}
backTitleFontFamily={headerBackTitleStyle.fontFamily}
backTitleFontSize={headerBackTitleStyle.fontSize}
color={headerTintColor}
gestureEnabled={gestureEnabled === undefined ? true : gestureEnabled}
largeTitle={headerLargeTitle}
largeTitleFontFamily={headerLargeTitleStyle.fontFamily}
largeTitleFontSize={headerLargeTitleStyle.fontSize}
backgroundColor={headerStyle.backgroundColor}
>
{headerRight !== undefined ? (
<ScreenStackHeaderRightView>{headerRight()}</ScreenStackHeaderRightView>
) : null}
</ScreenStackHeaderConfig>
);
}

View File

@@ -0,0 +1,75 @@
import * as React from 'react';
import { View, StyleSheet, Platform } from 'react-native';
import { StackNavigationState, StackActions } from '@react-navigation/routers';
import {
// @ts-ignore
ScreenStack,
Screen,
// eslint-disable-next-line import/no-unresolved
} from 'react-native-screens';
import HeaderConfig from './HeaderConfig';
import {
NativeStackNavigationHelpers,
NativeStackDescriptorMap,
} from '../types';
type Props = {
state: StackNavigationState;
navigation: NativeStackNavigationHelpers;
descriptors: NativeStackDescriptorMap;
};
export default function StackView({ state, navigation, descriptors }: Props) {
return (
<ScreenStack style={styles.scenes}>
{state.routes.map(route => {
const { options, render: renderScene } = descriptors[route.key];
const { presentation = 'push', animation, contentStyle } = options;
return (
// @ts-ignore
<Screen
key={route.key}
style={StyleSheet.absoluteFill}
stackPresentation={presentation}
stackAnimation={animation}
onDismissed={() => {
navigation.dispatch({
...StackActions.pop(),
source: route.key,
target: state.key,
});
}}
>
<HeaderConfig {...options} route={route} />
<View
style={[
styles.content,
{
marginTop:
Platform.OS === 'android' && options.headerShown !== false
? 56
: 0,
},
contentStyle,
]}
>
{renderScene()}
</View>
</Screen>
);
})}
</ScreenStack>
);
}
const styles = StyleSheet.create({
content: {
flex: 1,
backgroundColor: '#eee',
},
scenes: {
flex: 1,
},
});

View File

@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig"
}

View File

@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.13](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.12...@react-navigation/native@5.0.0-alpha.13) (2019-10-22)
**Note:** Version bump only for package @react-navigation/native
# [5.0.0-alpha.12](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.11...@react-navigation/native@5.0.0-alpha.12) (2019-10-15)
### Features
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.11](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.10...@react-navigation/native@5.0.0-alpha.11) (2019-10-06)
**Note:** Version bump only for package @react-navigation/native
# [5.0.0-alpha.10](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.9...@react-navigation/native@5.0.0-alpha.10) (2019-10-03)
**Note:** Version bump only for package @react-navigation/native
# [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.8...@react-navigation/native@5.0.0-alpha.9) (2019-10-03) # [5.0.0-alpha.9](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/native@5.0.0-alpha.8...@react-navigation/native@5.0.0-alpha.9) (2019-10-03)
**Note:** Version bump only for package @react-navigation/native **Note:** Version bump only for package @react-navigation/native

View File

@@ -7,7 +7,7 @@
"ios", "ios",
"android" "android"
], ],
"version": "5.0.0-alpha.9", "version": "5.0.0-alpha.13",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -34,8 +34,8 @@
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.17",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "16.8.3", "react": "~16.8.3",
"react-native": "0.59.10", "react-native": "~0.59.10",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -10,7 +10,7 @@ import {
type Config = { type Config = {
[routeName: string]: [routeName: string]:
| string | string
| { path: string; parse?: { [key: string]: (value: string) => any } }; | { path: string; parse?: Record<string, (value: string) => any> };
}; };
type Options = { type Options = {

View File

@@ -3,6 +3,103 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [5.0.0-alpha.28](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.27...@react-navigation/stack@5.0.0-alpha.28) (2019-10-22)
### Bug Fixes
* don't fire onOpen when screen is unmounting ([#137](https://github.com/react-navigation/navigation-ex/issues/137)) ([f22abb7](https://github.com/react-navigation/navigation-ex/commit/f22abb7)), closes [#136](https://github.com/react-navigation/navigation-ex/issues/136)
* don't keep unfocused header backgrounds visible ([031c4d2](https://github.com/react-navigation/navigation-ex/commit/031c4d2))
# [5.0.0-alpha.27](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.26...@react-navigation/stack@5.0.0-alpha.27) (2019-10-18)
### Features
* add an option to override safe area insets ([300791a](https://github.com/react-navigation/navigation-ex/commit/300791a))
# [5.0.0-alpha.26](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.25...@react-navigation/stack@5.0.0-alpha.26) (2019-10-17)
### Bug Fixes
* don't fade incoming background when fading header ([#127](https://github.com/react-navigation/navigation-ex/issues/127)) ([c6d0c19](https://github.com/react-navigation/navigation-ex/commit/c6d0c19))
* fix incorrect type ([731cf7d](https://github.com/react-navigation/navigation-ex/commit/731cf7d))
* use header height from style if specified ([442b95d](https://github.com/react-navigation/navigation-ex/commit/442b95d))
# [5.0.0-alpha.25](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.24...@react-navigation/stack@5.0.0-alpha.25) (2019-10-15)
### Bug Fixes
* don't ignore descriptors change ([9d9fe31](https://github.com/react-navigation/navigation-ex/commit/9d9fe31))
* increase hitSlop of back button on Android ([c7da1e4](https://github.com/react-navigation/navigation-ex/commit/c7da1e4))
* interpolation in iOS modal presentation ([b32cda2](https://github.com/react-navigation/navigation-ex/commit/b32cda2))
* make modal presentation mode fullscreen on landscape ([#124](https://github.com/react-navigation/navigation-ex/issues/124)) ([e789846](https://github.com/react-navigation/navigation-ex/commit/e789846))
### Features
* add a headerTitleAlign option to center or left align title ([6a0ca90](https://github.com/react-navigation/navigation-ex/commit/6a0ca90))
* export TransitionSpecs ([708dde0](https://github.com/react-navigation/navigation-ex/commit/708dde0))
* initial version of native stack ([#102](https://github.com/react-navigation/navigation-ex/issues/102)) ([ba3f718](https://github.com/react-navigation/navigation-ex/commit/ba3f718))
# [5.0.0-alpha.24](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.23...@react-navigation/stack@5.0.0-alpha.24) (2019-10-06)
### Bug Fixes
* actually expose gestureVelocityImpact in the public API ([16079d1](https://github.com/react-navigation/navigation-ex/commit/16079d1))
* don't recompute if routes didn't change ([615b523](https://github.com/react-navigation/navigation-ex/commit/615b523))
* handling vertical gesture in RTL ([#122](https://github.com/react-navigation/navigation-ex/issues/122)) ([a27ade8](https://github.com/react-navigation/navigation-ex/commit/a27ade8))
* use next screen's animation when not focused. fixes [#87](https://github.com/react-navigation/navigation-ex/issues/87) ([b4a7681](https://github.com/react-navigation/navigation-ex/commit/b4a7681))
### Features
* add gestureVelocityImpact as a prop for stack ([#123](https://github.com/react-navigation/navigation-ex/issues/123)) ([8294efc](https://github.com/react-navigation/navigation-ex/commit/8294efc))
* drop header: null in favor of more explitit headerShown option ([ba6b6ae](https://github.com/react-navigation/navigation-ex/commit/ba6b6ae))
# [5.0.0-alpha.23](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.22...@react-navigation/stack@5.0.0-alpha.23) (2019-10-03)
### Bug Fixes
* fix passing insets to interpolator ([6f5f4b7](https://github.com/react-navigation/navigation-ex/commit/6f5f4b7))
* fix vertical gesture ([a7c4a4d](https://github.com/react-navigation/navigation-ex/commit/a7c4a4d))
# [5.0.0-alpha.22](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.21...@react-navigation/stack@5.0.0-alpha.22) (2019-10-03)
**Note:** Version bump only for package @react-navigation/stack
# [5.0.0-alpha.21](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.20...@react-navigation/stack@5.0.0-alpha.21) (2019-10-03) # [5.0.0-alpha.21](https://github.com/react-navigation/navigation-ex/compare/@react-navigation/stack@5.0.0-alpha.20...@react-navigation/stack@5.0.0-alpha.21) (2019-10-03)

View File

@@ -1,6 +1,6 @@
# `@react-navigation/stack` # `@react-navigation/stack`
Bottom tab navigator for React Navigation following iOS design guidelines. Stack navigator for React Navigation.
## Installation ## Installation
@@ -10,44 +10,36 @@ Open a Terminal in your project's folder and run,
yarn add @react-navigation/core @react-navigation/stack @react-native-community/masked-view yarn add @react-navigation/core @react-navigation/stack @react-native-community/masked-view
``` ```
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated). Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler), [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated), [`react-native-screens`](https://github.com/kmagiera/react-native-screens) and [`react-native-safe-area-context`](https://github.com/th3rdwave/react-native-safe-area-context).
If you are using Expo, to ensure that you get the compatible versions of the libraries, run: If you are using Expo, to ensure that you get the compatible versions of the libraries, run:
```sh ```sh
expo install react-native-gesture-handler react-native-reanimated expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context
``` ```
If you are not using Expo, run the following: If you are not using Expo, run the following:
```sh ```sh
yarn add react-native-reanimated react-native-gesture-handler yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context
``` ```
If you are using Expo, you are done. Otherwise, continue to the next steps. If you are using Expo, you are done. Otherwise, continue to the next steps.
Next, we need to link these libraries. The steps depends on your React Native version: To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run:
- **React Native 0.60 and higher** ```sh
cd ios
pod install
cd ..
```
On newer versions of React Native, [linking is automatic](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md). To finalize installation of `react-native-screens` for Android, add the following two lines to dependencies section in `android/app/build.gradle`:
To complete the linking on iOS, make sure you have [Cocoapods](https://cocoapods.org/) installed. Then run: ```gradle
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
```sh implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
cd ios ```
pod install
cd ..
```
- **React Native 0.59**
If you're on an older React Native version, you need to manually link the dependencies. To do that, run:
```sh
react-native link react-native-reanimated
react-native link react-native-gesture-handler
```
**IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation. **IMPORTANT:** There are additional steps required for `react-native-gesture-handler` on Android after linking (for all React Native versions). Check the [this guide](https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html) to complete the installation.

View File

@@ -10,7 +10,7 @@
"android", "android",
"stack" "stack"
], ],
"version": "5.0.0-alpha.21", "version": "5.0.0-alpha.28",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -33,8 +33,7 @@
"clean": "del lib" "clean": "del lib"
}, },
"dependencies": { "dependencies": {
"@react-navigation/routers": "^5.0.0-alpha.9", "@react-navigation/routers": "^5.0.0-alpha.9"
"react-native-safe-area-view": "^0.14.7"
}, },
"devDependencies": { "devDependencies": {
"@react-native-community/bob": "^0.7.0", "@react-native-community/bob": "^0.7.0",
@@ -42,11 +41,12 @@
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-native": "^0.60.17", "@types/react-native": "^0.60.17",
"del-cli": "^3.0.0", "del-cli": "^3.0.0",
"react": "16.8.3", "react": "~16.8.3",
"react-native": "0.59.10", "react-native": "~0.59.10",
"react-native-gesture-handler": "^1.3.0", "react-native-gesture-handler": "^1.3.0",
"react-native-reanimated": "^1.3.0", "react-native-reanimated": "^1.3.0",
"react-native-screens": "^1.0.0-alpha.22", "react-native-safe-area-context": "^0.3.6",
"react-native-screens": "^2.0.0-alpha.5",
"typescript": "^3.6.3" "typescript": "^3.6.3"
}, },
"peerDependencies": { "peerDependencies": {
@@ -56,7 +56,8 @@
"react-native": "*", "react-native": "*",
"react-native-gesture-handler": "^1.0.0", "react-native-gesture-handler": "^1.0.0",
"react-native-reanimated": "^1.0.0", "react-native-reanimated": "^1.0.0",
"react-native-screens": "^1.0.0-alpha.0" "react-native-safe-area-context": "^0.3.6",
"react-native-screens": "^1.0.0-alpha.0 || ^2.0.0-alpha.0"
}, },
"@react-native-community/bob": { "@react-native-community/bob": {
"source": "src", "source": "src",

View File

@@ -1,6 +1,5 @@
import { I18nManager } from 'react-native'; import { I18nManager } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import getStatusBarHeight from '../utils/getStatusBarHeight';
import { import {
StackCardInterpolationProps, StackCardInterpolationProps,
StackCardInterpolatedStyle, StackCardInterpolatedStyle,
@@ -84,9 +83,11 @@ export function forModalPresentationIOS({
current, current,
next, next,
layouts: { screen }, layouts: { screen },
insets,
}: StackCardInterpolationProps): StackCardInterpolatedStyle { }: StackCardInterpolationProps): StackCardInterpolatedStyle {
const topOffset = 10; const isLandscape = screen.width > screen.height;
const statusBarHeight = getStatusBarHeight(screen.width > screen.height); const topOffset = isLandscape ? 0 : 10;
const statusBarHeight = insets.top;
const aspectRatio = screen.height / screen.width; const aspectRatio = screen.height / screen.width;
const progress = add(current.progress, next ? next.progress : 0); const progress = add(current.progress, next ? next.progress : 0);
@@ -101,22 +102,29 @@ export function forModalPresentationIOS({
}); });
const overlayOpacity = interpolate(progress, { const overlayOpacity = interpolate(progress, {
inputRange: [0, 1, 2], inputRange: [0, 1, 1.0001, 2],
outputRange: [0, 0.3, 1], outputRange: [0, 0.3, 1, 1],
}); });
const scale = interpolate(progress, { const scale = isLandscape
inputRange: [0, 1, 2], ? 1
outputRange: [1, 1, screen.width ? 1 - (topOffset * 2) / screen.width : 1], : interpolate(progress, {
}); inputRange: [0, 1, 2],
outputRange: [
1,
1,
screen.width ? 1 - (topOffset * 2) / screen.width : 1,
],
});
const borderRadius = const borderRadius = isLandscape
index === 0 ? 0
? interpolate(progress, { : index === 0
inputRange: [0, 1, 2], ? interpolate(progress, {
outputRange: [0, 0, 10], inputRange: [0, 1, 2],
}) outputRange: [0, 0, 10],
: 10; })
: 10;
return { return {
cardStyle: { cardStyle: {

View File

@@ -109,7 +109,12 @@ export function forFade({
leftButtonStyle: { opacity }, leftButtonStyle: { opacity },
rightButtonStyle: { opacity }, rightButtonStyle: { opacity },
titleStyle: { opacity }, titleStyle: { opacity },
backgroundStyle: { opacity }, backgroundStyle: {
opacity: interpolate(progress, {
inputRange: [0, 1, 1.9, 2],
outputRange: [0, 1, 1, 0],
}),
},
}; };
} }

View File

@@ -1,5 +1,6 @@
import * as CardStyleInterpolators from './TransitionConfigs/CardStyleInterpolators'; import * as CardStyleInterpolators from './TransitionConfigs/CardStyleInterpolators';
import * as HeaderStyleInterpolators from './TransitionConfigs/HeaderStyleInterpolators'; import * as HeaderStyleInterpolators from './TransitionConfigs/HeaderStyleInterpolators';
import * as TransitionSpecs from './TransitionConfigs/TransitionSpecs';
import * as TransitionPresets from './TransitionConfigs/TransitionPresets'; import * as TransitionPresets from './TransitionConfigs/TransitionPresets';
/** /**
@@ -24,7 +25,12 @@ export { default as HeaderBackButton } from './views/Header/HeaderBackButton';
/** /**
* Transition presets * Transition presets
*/ */
export { CardStyleInterpolators, HeaderStyleInterpolators, TransitionPresets }; export {
CardStyleInterpolators,
HeaderStyleInterpolators,
TransitionSpecs,
TransitionPresets,
};
/** /**
* Utilities * Utilities

View File

@@ -5,6 +5,7 @@ import {
LayoutChangeEvent, LayoutChangeEvent,
} from 'react-native'; } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { EdgeInsets } from 'react-native-safe-area-context';
import { import {
NavigationProp, NavigationProp,
ParamListBase, ParamListBase,
@@ -107,6 +108,11 @@ export type StackHeaderOptions = {
* The title string is passed in `children`. * The title string is passed in `children`.
*/ */
headerTitle?: string | ((props: StackHeaderTitleProps) => React.ReactNode); headerTitle?: string | ((props: StackHeaderTitleProps) => React.ReactNode);
/**
* How to align the the header title.
* Defaults to `center` on iOS and `left` on Android.
*/
headerTitleAlign?: 'left' | 'center';
/** /**
* Style object for the title component. * Style object for the title component.
*/ */
@@ -184,10 +190,6 @@ export type StackHeaderOptions = {
* Style object for the header. You can specify a custom background color here, for example. * Style object for the header. You can specify a custom background color here, for example.
*/ */
headerStyle?: StyleProp<ViewStyle>; headerStyle?: StyleProp<ViewStyle>;
/**
* Custom status bar height to set as the top padding in the header.
*/
headerStatusBarHeight?: number;
/** /**
* Defaults to `false`. If `true`, the header will not have a background unless you explicitly provide it with `headerBackground`. * Defaults to `false`. If `true`, the header will not have a background unless you explicitly provide it with `headerBackground`.
* The header will also float over the screen so that it overlaps the content underneath. * The header will also float over the screen so that it overlaps the content underneath.
@@ -206,6 +208,10 @@ export type StackHeaderProps = {
* Layout of the screen. * Layout of the screen.
*/ */
layout: Layout; layout: Layout;
/**
* Safe area insets to use in the header, e.g. to apply extra spacing for statusbar and notch.
*/
insets: EdgeInsets;
/** /**
* Object representing the current scene, such as the route object and animation progress. * Object representing the current scene, such as the route object and animation progress.
*/ */
@@ -245,7 +251,12 @@ export type StackNavigationOptions = StackHeaderOptions &
* Function that given `HeaderProps` returns a React Element to display as a header. * Function that given `HeaderProps` returns a React Element to display as a header.
* Setting to `null` hides header. * Setting to `null` hides header.
*/ */
header?: null | ((props: StackHeaderProps) => React.ReactNode); header?: (props: StackHeaderProps) => React.ReactNode;
/**
* Whether to show the header. The header is shown by default unless `headerMode` was set to `none`.
* Setting this to `false` hides the header.
*/
headerShown?: boolean;
/** /**
* Whether a shadow is visible for the card during transitions. Defaults to `true`. * Whether a shadow is visible for the card during transitions. Defaults to `true`.
*/ */
@@ -291,6 +302,22 @@ export type StackNavigationOptions = StackHeaderOptions &
*/ */
horizontal?: number; horizontal?: number;
}; };
/**
* Number which determines the relevance of velocity for the gesture.
* Defaults to 0.3.
*/
gestureVelocityImpact?: number;
/**
* Safe area insets for the screen. This is used to avoid elements like notch and status bar.
* By default, the device's safe area insets are automatically detected. You can override the behavior with this option.
* For example, to remove the extra spacing for status bar, pass `safeAreaInsets: { top: 0 }`.
*/
safeAreaInsets?: {
top?: number;
right?: number;
bottom?: number;
left?: number;
};
}; };
export type StackNavigationConfig = { export type StackNavigationConfig = {
@@ -445,6 +472,15 @@ export type StackCardInterpolationProps = {
*/ */
screen: Layout; screen: Layout;
}; };
/**
* Safe area insets
*/
insets: {
top: number;
right: number;
bottom: number;
left: number;
};
}; };
export type StackCardInterpolatedStyle = { export type StackCardInterpolatedStyle = {

View File

@@ -1,9 +0,0 @@
import { Platform } from 'react-native';
import { getStatusBarHeight as getStatusBarHeightNative } from 'react-native-safe-area-view';
const getStatusBarHeight = Platform.select({
default: getStatusBarHeightNative,
web: () => 0,
});
export default getStatusBarHeight;

View File

@@ -1,11 +1,19 @@
import * as React from 'react'; import * as React from 'react';
import { StackActions } from '@react-navigation/routers'; import { StackActions } from '@react-navigation/routers';
import HeaderSegment from './HeaderSegment'; import HeaderSegment from './HeaderSegment';
import { StackHeaderProps, StackHeaderTitleProps } from '../../types'; import { StackHeaderProps, StackHeaderTitleProps } from '../../types';
import HeaderTitle from './HeaderTitle'; import HeaderTitle from './HeaderTitle';
export default React.memo(function Header(props: StackHeaderProps) { export default React.memo(function Header(props: StackHeaderProps) {
const { scene, previous, layout, navigation, styleInterpolator } = props; const {
scene,
previous,
layout,
insets,
navigation,
styleInterpolator,
} = props;
const { options } = scene.descriptor; const { options } = scene.descriptor;
const title = const title =
typeof options.headerTitle !== 'function' && typeof options.headerTitle !== 'function' &&
@@ -35,6 +43,7 @@ export default React.memo(function Header(props: StackHeaderProps) {
return ( return (
<HeaderSegment <HeaderSegment
{...options} {...options}
insets={insets}
layout={layout} layout={layout}
scene={scene} scene={scene}
title={title} title={title}

View File

@@ -170,7 +170,7 @@ class HeaderBackButton extends React.Component<Props, State> {
style={[styles.container, disabled && styles.disabled]} style={[styles.container, disabled && styles.disabled]}
hitSlop={Platform.select({ hitSlop={Platform.select({
ios: undefined, ios: undefined,
default: { top: 8, right: 8, bottom: 8, left: 8 }, default: { top: 16, right: 16, bottom: 16, left: 16 },
})} })}
borderless borderless
> >

View File

@@ -6,6 +6,7 @@ import {
ParamListBase, ParamListBase,
} from '@react-navigation/core'; } from '@react-navigation/core';
import { StackNavigationState } from '@react-navigation/routers'; import { StackNavigationState } from '@react-navigation/routers';
import { EdgeInsets } from 'react-native-safe-area-context';
import Header from './Header'; import Header from './Header';
import { forStatic } from '../../TransitionConfigs/HeaderStyleInterpolators'; import { forStatic } from '../../TransitionConfigs/HeaderStyleInterpolators';
@@ -19,6 +20,7 @@ import {
export type Props = { export type Props = {
mode: 'float' | 'screen'; mode: 'float' | 'screen';
layout: Layout; layout: Layout;
insets: EdgeInsets;
scenes: Array<Scene<Route<string>> | undefined>; scenes: Array<Scene<Route<string>> | undefined>;
state: StackNavigationState; state: StackNavigationState;
getPreviousRoute: (props: { getPreviousRoute: (props: {
@@ -36,6 +38,7 @@ export default function HeaderContainer({
mode, mode,
scenes, scenes,
layout, layout,
insets,
state, state,
getPreviousRoute, getPreviousRoute,
onContentHeightChange, onContentHeightChange,
@@ -77,16 +80,17 @@ export default function HeaderContainer({
const isHeaderStatic = const isHeaderStatic =
mode === 'float' mode === 'float'
? (previousScene && ? (previousScene &&
previousScene.descriptor.options.header === null && previousScene.descriptor.options.headerShown === false &&
// We still need to animate when coming back from next scene // We still need to animate when coming back from next scene
// A hacky way to check this is if the next scene exists // A hacky way to check this is if the next scene exists
!nextScene) || !nextScene) ||
(nextScene && nextScene.descriptor.options.header === null) (nextScene && nextScene.descriptor.options.headerShown === false)
: false; : false;
const props = { const props = {
mode, mode,
layout, layout,
insets,
scene, scene,
previous, previous,
navigation: scene.descriptor.navigation as StackNavigationProp< navigation: scene.descriptor.navigation as StackNavigationProp<
@@ -121,13 +125,13 @@ export default function HeaderContainer({
: null : null
} }
> >
{options.header !== undefined ? ( {options.headerShown !== false ? (
options.header === null ? null : ( options.header !== undefined ? (
options.header(props) options.header(props)
) : (
<Header {...props} />
) )
) : ( ) : null}
<Header {...props} />
)}
</View> </View>
</NavigationContext.Provider> </NavigationContext.Provider>
); );

View File

@@ -7,10 +7,10 @@ import {
ViewStyle, ViewStyle,
} from 'react-native'; } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { EdgeInsets } from 'react-native-safe-area-context';
import { Route } from '@react-navigation/core'; import { Route } from '@react-navigation/core';
import HeaderBackButton from './HeaderBackButton'; import HeaderBackButton from './HeaderBackButton';
import HeaderBackground from './HeaderBackground'; import HeaderBackground from './HeaderBackground';
import getStatusBarHeight from '../../utils/getStatusBarHeight';
import memoize from '../../utils/memoize'; import memoize from '../../utils/memoize';
import { import {
Layout, Layout,
@@ -29,6 +29,7 @@ export type Scene<T> = {
type Props = StackHeaderOptions & { type Props = StackHeaderOptions & {
headerTitle: (props: StackHeaderTitleProps) => React.ReactNode; headerTitle: (props: StackHeaderTitleProps) => React.ReactNode;
layout: Layout; layout: Layout;
insets: EdgeInsets;
onGoBack?: () => void; onGoBack?: () => void;
title?: string; title?: string;
leftLabel?: string; leftLabel?: string;
@@ -41,7 +42,7 @@ type State = {
leftLabelLayout?: Layout; leftLabelLayout?: Layout;
}; };
const warnIfHeaderStylesDefined = (styles: { [key: string]: any }) => { const warnIfHeaderStylesDefined = (styles: Record<string, any>) => {
Object.keys(styles).forEach(styleProp => { Object.keys(styles).forEach(styleProp => {
const value = styles[styleProp]; const value = styles[styleProp];
@@ -57,7 +58,7 @@ const warnIfHeaderStylesDefined = (styles: { [key: string]: any }) => {
}); });
}; };
export const getDefaultHeaderHeight = (layout: Layout) => { export const getDefaultHeaderHeight = (layout: Layout, insets: EdgeInsets) => {
const isLandscape = layout.width > layout.height; const isLandscape = layout.width > layout.height;
let headerHeight; let headerHeight;
@@ -75,7 +76,7 @@ export const getDefaultHeaderHeight = (layout: Layout) => {
headerHeight = 64; headerHeight = 64;
} }
return headerHeight + getStatusBarHeight(isLandscape); return headerHeight + insets.top;
}; };
export default class HeaderSegment extends React.Component<Props, State> { export default class HeaderSegment extends React.Component<Props, State> {
@@ -135,15 +136,18 @@ export default class HeaderSegment extends React.Component<Props, State> {
const { const {
scene, scene,
layout, layout,
insets,
title: currentTitle, title: currentTitle,
leftLabel: previousTitle, leftLabel: previousTitle,
onGoBack, onGoBack,
headerTitle, headerTitle,
headerTitleAlign = Platform.select({
ios: 'center',
default: 'left',
}),
headerLeft: left = onGoBack headerLeft: left = onGoBack
? (props: StackHeaderLeftButtonProps) => <HeaderBackButton {...props} /> ? (props: StackHeaderLeftButtonProps) => <HeaderBackButton {...props} />
: undefined, : undefined,
// @ts-ignore
headerStatusBarHeight = getStatusBarHeight(layout.width > layout.height),
headerTransparent, headerTransparent,
headerTintColor, headerTintColor,
headerBackground, headerBackground,
@@ -182,7 +186,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
); );
const { const {
height = getDefaultHeaderHeight(layout), height = getDefaultHeaderHeight(layout, insets),
minHeight, minHeight,
maxHeight, maxHeight,
backgroundColor, backgroundColor,
@@ -306,15 +310,17 @@ export default class HeaderSegment extends React.Component<Props, State> {
pointerEvents="box-none" pointerEvents="box-none"
style={[{ height, minHeight, maxHeight, opacity }]} style={[{ height, minHeight, maxHeight, opacity }]}
> >
<View <View pointerEvents="none" style={{ height: insets.top }} />
pointerEvents="none"
style={{ height: headerStatusBarHeight }}
/>
<View pointerEvents="box-none" style={styles.content}> <View pointerEvents="box-none" style={styles.content}>
{leftButton ? ( {leftButton ? (
<Animated.View <Animated.View
pointerEvents="box-none" pointerEvents="box-none"
style={[styles.left, leftButtonStyle, leftContainerStyle]} style={[
styles.left,
{ left: insets.left },
leftButtonStyle,
leftContainerStyle,
]}
> >
{leftButton} {leftButton}
</Animated.View> </Animated.View>
@@ -322,11 +328,10 @@ export default class HeaderSegment extends React.Component<Props, State> {
<Animated.View <Animated.View
pointerEvents="box-none" pointerEvents="box-none"
style={[ style={[
Platform.select({ headerTitleAlign === 'left' && {
ios: null, position: 'absolute',
default: { left: leftButton ? 72 : 16 }, left: leftButton ? 72 : 16,
}), },
styles.title,
titleStyle, titleStyle,
titleContainerStyle, titleContainerStyle,
]} ]}
@@ -341,7 +346,12 @@ export default class HeaderSegment extends React.Component<Props, State> {
{right ? ( {right ? (
<Animated.View <Animated.View
pointerEvents="box-none" pointerEvents="box-none"
style={[styles.right, rightButtonStyle, rightContainerStyle]} style={[
styles.right,
{ right: insets.right },
rightButtonStyle,
rightContainerStyle,
]}
> >
{right({ tintColor: headerTintColor })} {right({ tintColor: headerTintColor })}
</Animated.View> </Animated.View>
@@ -377,8 +387,4 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
alignItems: 'flex-end', alignItems: 'flex-end',
}, },
title: Platform.select({
ios: {},
default: { position: 'absolute' },
}),
}); });

View File

@@ -14,6 +14,10 @@ import {
PanGestureHandler, PanGestureHandler,
State as GestureState, State as GestureState,
} from 'react-native-gesture-handler'; } from 'react-native-gesture-handler';
import { EdgeInsets } from 'react-native-safe-area-context';
import PointerEventsView from './PointerEventsView';
import memoize from '../../utils/memoize';
import StackGestureContext from '../../utils/StackGestureContext';
import { import {
TransitionSpec, TransitionSpec,
StackCardStyleInterpolator, StackCardStyleInterpolator,
@@ -21,9 +25,6 @@ import {
SpringConfig, SpringConfig,
TimingConfig, TimingConfig,
} from '../../types'; } from '../../types';
import memoize from '../../utils/memoize';
import StackGestureContext from '../../utils/StackGestureContext';
import PointerEventsView from './PointerEventsView';
type Props = ViewProps & { type Props = ViewProps & {
index: number; index: number;
@@ -33,6 +34,7 @@ type Props = ViewProps & {
next?: Animated.Node<number>; next?: Animated.Node<number>;
current: Animated.Value<number>; current: Animated.Value<number>;
layout: Layout; layout: Layout;
insets: EdgeInsets;
gestureDirection: 'horizontal' | 'vertical'; gestureDirection: 'horizontal' | 'vertical';
onOpen: (isFinished: boolean) => void; onOpen: (isFinished: boolean) => void;
onClose: (isFinished: boolean) => void; onClose: (isFinished: boolean) => void;
@@ -48,6 +50,7 @@ type Props = ViewProps & {
vertical?: number; vertical?: number;
horizontal?: number; horizontal?: number;
}; };
gestureVelocityImpact: number;
transitionSpec: { transitionSpec: {
open: TransitionSpec; open: TransitionSpec;
close: TransitionSpec; close: TransitionSpec;
@@ -86,7 +89,7 @@ const MINUS_ONE_NODE = UNSET_NODE;
const DIRECTION_VERTICAL = -1; const DIRECTION_VERTICAL = -1;
const DIRECTION_HORIZONTAL = 1; const DIRECTION_HORIZONTAL = 1;
const SWIPE_VELOCITY_IMPACT = 0.3; const GESTURE_VELOCITY_IMPACT = 0.3;
/** /**
* The distance of touch start from the edge of the screen where the gesture will be recognized * The distance of touch start from the edge of the screen where the gesture will be recognized
@@ -228,10 +231,16 @@ export default class Card extends React.Component<Props> {
overlayEnabled: Platform.OS !== 'ios', overlayEnabled: Platform.OS !== 'ios',
shadowEnabled: true, shadowEnabled: true,
gestureEnabled: true, gestureEnabled: true,
gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
}; };
componentDidUpdate(prevProps: Props) { componentDidUpdate(prevProps: Props) {
const { layout, gestureDirection, closing } = this.props; const {
layout,
gestureDirection,
gestureVelocityImpact,
closing,
} = this.props;
const { width, height } = layout; const { width, height } = layout;
if (width !== prevProps.layout.width) { if (width !== prevProps.layout.width) {
@@ -242,6 +251,10 @@ export default class Card extends React.Component<Props> {
this.layout.height.setValue(height); this.layout.height.setValue(height);
} }
if (gestureVelocityImpact !== prevProps.gestureVelocityImpact) {
this.gestureVelocityImpact.setValue(gestureVelocityImpact);
}
if (gestureDirection !== prevProps.gestureDirection) { if (gestureDirection !== prevProps.gestureDirection) {
this.direction.setValue( this.direction.setValue(
gestureDirection === 'vertical' gestureDirection === 'vertical'
@@ -268,16 +281,11 @@ export default class Card extends React.Component<Props> {
// during running. However, we need to invoke listener onClose // during running. However, we need to invoke listener onClose
// manually in this case // manually in this case
if (this.isRunningAnimation || this.noAnimationStartedSoFar) { if (this.isRunningAnimation || this.noAnimationStartedSoFar) {
if (this.isVisibleValue) { this.props.onClose(false);
this.props.onOpen(false);
} else {
this.props.onClose(false);
}
} }
} }
private isVisible = new Value<Binary>(TRUE); private isVisible = new Value<Binary>(TRUE);
private isVisibleValue: Binary = TRUE;
private nextIsVisible = new Value<Binary | -1>(UNSET); private nextIsVisible = new Value<Binary | -1>(UNSET);
private isClosing = new Value<Binary>(FALSE); private isClosing = new Value<Binary>(FALSE);
@@ -297,7 +305,11 @@ export default class Card extends React.Component<Props> {
height: new Value(this.props.layout.height), height: new Value(this.props.layout.height),
}; };
openingSpecConfig = private gestureVelocityImpact = new Value<number>(
this.props.gestureVelocityImpact
);
private openingSpecConfig =
this.props.transitionSpec.open.animation === 'timing' this.props.transitionSpec.open.animation === 'timing'
? transformTimingConfigToAnimatedValues( ? transformTimingConfigToAnimatedValues(
this.props.transitionSpec.open.config this.props.transitionSpec.open.config
@@ -306,7 +318,7 @@ export default class Card extends React.Component<Props> {
this.props.transitionSpec.open.config this.props.transitionSpec.open.config
); );
closingSpecConfig = private closingSpecConfig =
this.props.transitionSpec.close.animation === 'timing' this.props.transitionSpec.close.animation === 'timing'
? transformTimingConfigToAnimatedValues( ? transformTimingConfigToAnimatedValues(
this.props.transitionSpec.close.config this.props.transitionSpec.close.config
@@ -368,7 +380,11 @@ export default class Card extends React.Component<Props> {
this.props.index, this.props.index,
this.props.current, this.props.current,
this.props.next, this.props.next,
this.props.layout this.props.layout,
this.props.insets.top,
this.props.insets.right,
this.props.insets.bottom,
this.props.insets.left
); );
}; };
@@ -471,18 +487,19 @@ export default class Card extends React.Component<Props> {
private extrapolatedPosition = add( private extrapolatedPosition = add(
this.gesture, this.gesture,
multiply(this.velocity, SWIPE_VELOCITY_IMPACT) multiply(this.velocity, this.gestureVelocityImpact)
); );
private exec = [ private exec = [
cond( set(
eq(this.direction, DIRECTION_HORIZONTAL), this.gesture,
set( cond(
this.gesture, eq(this.direction, DIRECTION_HORIZONTAL),
multiply( multiply(
this.gestureUntraversed, this.gestureUntraversed,
I18nManager.isRTL ? MINUS_ONE_NODE : TRUE_NODE I18nManager.isRTL ? MINUS_ONE_NODE : TRUE_NODE
) ),
this.gestureUntraversed
) )
), ),
set( set(
@@ -512,19 +529,9 @@ export default class Card extends React.Component<Props> {
), ),
]; ];
private changeVisiblityExec = onChange(
this.isVisible,
call([this.isVisible], ([isVisible]) => (this.isVisibleValue = isVisible))
);
private execNoGesture = block([ private execNoGesture = block([
...this.exec, ...this.exec,
this.runTransition(this.isVisible), this.runTransition(this.isVisible),
onChange(
this.isVisible,
call([this.isVisible], ([isVisible]) => (this.isVisibleValue = isVisible))
),
this.changeVisiblityExec,
]); ]);
private execWithGesture = block([ private execWithGesture = block([
@@ -577,9 +584,13 @@ export default class Card extends React.Component<Props> {
cond( cond(
this.distance, this.distance,
divide( divide(
multiply( cond(
this.gestureUntraversed, eq(this.direction, DIRECTION_HORIZONTAL),
I18nManager.isRTL ? MINUS_ONE_NODE : TRUE_NODE multiply(
this.gestureUntraversed,
I18nManager.isRTL ? MINUS_ONE_NODE : TRUE_NODE
),
this.gestureUntraversed
), ),
this.distance this.distance
), ),
@@ -629,7 +640,6 @@ export default class Card extends React.Component<Props> {
), ),
] ]
), ),
this.changeVisiblityExec,
]); ]);
private handleGestureEventHorizontal = Animated.event([ private handleGestureEventHorizontal = Animated.event([
@@ -645,8 +655,8 @@ export default class Card extends React.Component<Props> {
private handleGestureEventVertical = Animated.event([ private handleGestureEventVertical = Animated.event([
{ {
nativeEvent: { nativeEvent: {
translationY: this.gesture, translationY: this.gestureUntraversed,
velocityY: this.velocity, velocityY: this.velocityUntraversed,
state: this.gestureState, state: this.gestureState,
}, },
}, },
@@ -661,7 +671,11 @@ export default class Card extends React.Component<Props> {
index: number, index: number,
current: Animated.Node<number>, current: Animated.Node<number>,
next: Animated.Node<number> | undefined, next: Animated.Node<number> | undefined,
layout: Layout layout: Layout,
insetTop: number,
insetRight: number,
insetBottom: number,
insetLeft: number
) => ) =>
styleInterpolator({ styleInterpolator({
index, index,
@@ -671,6 +685,12 @@ export default class Card extends React.Component<Props> {
layouts: { layouts: {
screen: layout, screen: layout,
}, },
insets: {
top: insetTop,
right: insetRight,
bottom: insetBottom,
left: insetLeft,
},
}) })
); );
@@ -683,7 +703,11 @@ export default class Card extends React.Component<Props> {
this.props.index, this.props.index,
this.props.current, this.props.current,
this.props.next, this.props.next,
this.props.layout this.props.layout,
this.props.insets.top,
this.props.insets.right,
this.props.insets.bottom,
this.props.insets.left
); );
private gestureActivationCriteria() { private gestureActivationCriteria() {
@@ -733,6 +757,7 @@ export default class Card extends React.Component<Props> {
current, current,
next, next,
layout, layout,
insets,
overlayEnabled, overlayEnabled,
shadowEnabled, shadowEnabled,
gestureEnabled, gestureEnabled,
@@ -749,7 +774,11 @@ export default class Card extends React.Component<Props> {
index, index,
current, current,
next, next,
layout layout,
insets.top,
insets.right,
insets.bottom,
insets.left
); );
} }

View File

@@ -8,6 +8,7 @@ import {
ViewProps, ViewProps,
} from 'react-native'; } from 'react-native';
import Animated from 'react-native-reanimated'; import Animated from 'react-native-reanimated';
import { EdgeInsets } from 'react-native-safe-area-context';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import * as Screens from 'react-native-screens'; // Import with * as to prevent getters being called import * as Screens from 'react-native-screens'; // Import with * as to prevent getters being called
import { Route } from '@react-navigation/core'; import { Route } from '@react-navigation/core';
@@ -36,12 +37,13 @@ type ProgressValues = {
type Props = { type Props = {
mode: 'card' | 'modal'; mode: 'card' | 'modal';
insets: EdgeInsets;
state: StackNavigationState; state: StackNavigationState;
navigation: StackNavigationHelpers; navigation: StackNavigationHelpers;
descriptors: StackDescriptorMap; descriptors: StackDescriptorMap;
routes: Route<string>[]; routes: Route<string>[];
openingRoutes: string[]; openingRouteKeys: string[];
closingRoutes: string[]; closingRouteKeys: string[];
onGoBack: (props: { route: Route<string> }) => void; onGoBack: (props: { route: Route<string> }) => void;
onOpenRoute: (props: { route: Route<string> }) => void; onOpenRoute: (props: { route: Route<string> }) => void;
onCloseRoute: (props: { route: Route<string> }) => void; onCloseRoute: (props: { route: Route<string> }) => void;
@@ -63,7 +65,7 @@ type State = {
scenes: Scene<Route<string>>[]; scenes: Scene<Route<string>>[];
progress: ProgressValues; progress: ProgressValues;
layout: Layout; layout: Layout;
floatingHeaderHeights: { [key: string]: number }; floatingHeaderHeights: Record<string, number>;
}; };
const dimensions = Dimensions.get('window'); const dimensions = Dimensions.get('window');
@@ -114,19 +116,23 @@ const FALLBACK_DESCRIPTOR = Object.freeze({ options: {} });
const getFloatingHeaderHeights = ( const getFloatingHeaderHeights = (
routes: Route<string>[], routes: Route<string>[],
insets: EdgeInsets,
descriptors: StackDescriptorMap,
layout: Layout, layout: Layout,
previous: { [key: string]: number } previous: Record<string, number>
) => { ) => {
const defaultHeaderHeight = getDefaultHeaderHeight(layout); const defaultHeaderHeight = getDefaultHeaderHeight(layout, insets);
return routes.reduce( return routes.reduce<Record<string, number>>((acc, curr) => {
(acc, curr) => { const { options = {} } = descriptors[curr.key] || {};
acc[curr.key] = previous[curr.key] || defaultHeaderHeight; const { height = previous[curr.key] } = StyleSheet.flatten(
options.headerStyle || {}
);
return acc; acc[curr.key] = typeof height === 'number' ? height : defaultHeaderHeight;
},
{} as { [key: string]: number } return acc;
); }, {});
}; };
export default class Stack extends React.Component<Props, State> { export default class Stack extends React.Component<Props, State> {
@@ -138,24 +144,21 @@ export default class Stack extends React.Component<Props, State> {
return null; return null;
} }
const progress = props.routes.reduce( const progress = props.routes.reduce<ProgressValues>((acc, curr) => {
(acc, curr) => { const descriptor = props.descriptors[curr.key];
const descriptor = props.descriptors[curr.key];
acc[curr.key] = acc[curr.key] =
state.progress[curr.key] || state.progress[curr.key] ||
new Animated.Value( new Animated.Value(
props.openingRoutes.includes(curr.key) && props.openingRouteKeys.includes(curr.key) &&
descriptor && descriptor &&
descriptor.options.animationEnabled !== false descriptor.options.animationEnabled !== false
? 0 ? 0
: 1 : 1
); );
return acc; return acc;
}, }, {});
{} as ProgressValues
);
return { return {
routes: props.routes, routes: props.routes,
@@ -201,6 +204,8 @@ export default class Stack extends React.Component<Props, State> {
descriptors: props.descriptors, descriptors: props.descriptors,
floatingHeaderHeights: getFloatingHeaderHeights( floatingHeaderHeights: getFloatingHeaderHeights(
props.routes, props.routes,
props.insets,
state.descriptors,
state.layout, state.layout,
state.floatingHeaderHeights state.floatingHeaderHeights
), ),
@@ -233,14 +238,16 @@ export default class Stack extends React.Component<Props, State> {
const layout = { width, height }; const layout = { width, height };
this.setState({ this.setState(state => ({
layout, layout,
floatingHeaderHeights: getFloatingHeaderHeights( floatingHeaderHeights: getFloatingHeaderHeights(
this.props.routes, this.props.routes,
this.props.insets,
state.descriptors,
layout, layout,
{} {}
), ),
}); }));
}; };
private handleFloatingHeaderLayout = ({ private handleFloatingHeaderLayout = ({
@@ -287,11 +294,12 @@ export default class Stack extends React.Component<Props, State> {
render() { render() {
const { const {
mode, mode,
insets,
descriptors, descriptors,
state, state,
navigation, navigation,
routes, routes,
closingRoutes, closingRouteKeys,
onOpenRoute, onOpenRoute,
onCloseRoute, onCloseRoute,
onGoBack, onGoBack,
@@ -321,6 +329,13 @@ export default class Stack extends React.Component<Props, State> {
}; };
} }
const {
top = insets.top,
right = insets.right,
bottom = insets.bottom,
left = insets.left,
} = focusedOptions.safeAreaInsets || {};
return ( return (
<React.Fragment> <React.Fragment>
<MaybeScreenContainer <MaybeScreenContainer
@@ -332,7 +347,6 @@ export default class Stack extends React.Component<Props, State> {
const focused = focusedRoute.key === route.key; const focused = focusedRoute.key === route.key;
const current = progress[route.key]; const current = progress[route.key];
const scene = scenes[index]; const scene = scenes[index];
const descriptor = scene.descriptor;
const next = self[index + 1] const next = self[index + 1]
? progress[self[index + 1].key] ? progress[self[index + 1].key]
: ANIMATED_ONE; : ANIMATED_ONE;
@@ -348,21 +362,62 @@ export default class Stack extends React.Component<Props, State> {
: 0; : 0;
const { const {
header, safeAreaInsets,
headerShown,
headerTransparent, headerTransparent,
cardTransparent, cardTransparent,
cardShadowEnabled, cardShadowEnabled,
cardOverlayEnabled, cardOverlayEnabled,
cardStyle, cardStyle,
gestureResponseDistance, gestureResponseDistance,
gestureVelocityImpact,
gestureDirection = defaultTransitionPreset.gestureDirection, gestureDirection = defaultTransitionPreset.gestureDirection,
transitionSpec = defaultTransitionPreset.transitionSpec, transitionSpec = defaultTransitionPreset.transitionSpec,
cardStyleInterpolator = defaultTransitionPreset.cardStyleInterpolator, cardStyleInterpolator = defaultTransitionPreset.cardStyleInterpolator,
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator, headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
} = descriptor } = scene.descriptor
? descriptor.options ? scene.descriptor.options
: ({} as StackNavigationOptions); : ({} as StackNavigationOptions);
let transitionConfig = {
transitionSpec,
cardStyleInterpolator,
headerStyleInterpolator,
};
// When a screen is not the last, it should use next screen's transition config
// Many transitions also animate the previous screen, so using 2 different transitions doesn't look right
// For example combining a slide and a modal transition would look wrong otherwise
// With this approach, combining different transition styles in the same navigator mostly looks right
// This will still be broken when 2 transitions have different idle state (e.g. modal presentation),
// but majority of the transitions look alright
if (index !== self.length - 1) {
const nextScene = scenes[index + 1];
if (nextScene) {
const {
transitionSpec = defaultTransitionPreset.transitionSpec,
cardStyleInterpolator = defaultTransitionPreset.cardStyleInterpolator,
headerStyleInterpolator = defaultTransitionPreset.headerStyleInterpolator,
} = nextScene.descriptor
? nextScene.descriptor.options
: ({} as StackNavigationOptions);
transitionConfig = {
transitionSpec,
cardStyleInterpolator,
headerStyleInterpolator,
};
}
}
const {
top: safeAreaInsetTop = insets.top,
right: safeAreaInsetRight = insets.right,
bottom: safeAreaInsetBottom = insets.bottom,
left: safeAreaInsetLeft = insets.left,
} = safeAreaInsets || {};
return ( return (
<MaybeScreen <MaybeScreen
key={route.key} key={route.key}
@@ -375,26 +430,29 @@ export default class Stack extends React.Component<Props, State> {
index={index} index={index}
active={index === self.length - 1} active={index === self.length - 1}
focused={focused} focused={focused}
closing={closingRoutes.includes(route.key)} closing={closingRouteKeys.includes(route.key)}
layout={layout} layout={layout}
current={current} current={current}
scene={scene} scene={scene}
previousScene={scenes[index - 1]} previousScene={scenes[index - 1]}
navigation={navigation} navigation={navigation}
state={state} state={state}
safeAreaInsetTop={safeAreaInsetTop}
safeAreaInsetRight={safeAreaInsetRight}
safeAreaInsetBottom={safeAreaInsetBottom}
safeAreaInsetLeft={safeAreaInsetLeft}
cardTransparent={cardTransparent} cardTransparent={cardTransparent}
cardOverlayEnabled={cardOverlayEnabled} cardOverlayEnabled={cardOverlayEnabled}
cardShadowEnabled={cardShadowEnabled} cardShadowEnabled={cardShadowEnabled}
cardStyle={cardStyle} cardStyle={cardStyle}
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
onPageChangeStart={onPageChangeStart} onPageChangeStart={onPageChangeStart}
onPageChangeConfirm={onPageChangeConfirm} onPageChangeConfirm={onPageChangeConfirm}
onPageChangeCancel={onPageChangeCancel} onPageChangeCancel={onPageChangeCancel}
gestureResponseDistance={gestureResponseDistance} gestureResponseDistance={gestureResponseDistance}
floatingHeaderHeight={floatingHeaderHeights[route.key]} floatingHeaderHeight={floatingHeaderHeights[route.key]}
hasCustomHeader={header === null}
getPreviousRoute={getPreviousRoute} getPreviousRoute={getPreviousRoute}
headerMode={headerMode} headerMode={headerMode}
headerShown={headerShown}
headerTransparent={headerTransparent} headerTransparent={headerTransparent}
renderHeader={renderHeader} renderHeader={renderHeader}
renderScene={renderScene} renderScene={renderScene}
@@ -403,10 +461,10 @@ export default class Stack extends React.Component<Props, State> {
onTransitionStart={this.handleTransitionStart} onTransitionStart={this.handleTransitionStart}
onTransitionEnd={this.handleTransitionEnd} onTransitionEnd={this.handleTransitionEnd}
onGoBack={onGoBack} onGoBack={onGoBack}
gestureEnabled={index !== 0 && getGesturesEnabled({ route })}
gestureVelocityImpact={gestureVelocityImpact}
gestureDirection={gestureDirection} gestureDirection={gestureDirection}
transitionSpec={transitionSpec} {...transitionConfig}
cardStyleInterpolator={cardStyleInterpolator}
headerStyleInterpolator={headerStyleInterpolator}
/> />
</MaybeScreen> </MaybeScreen>
); );
@@ -416,6 +474,7 @@ export default class Stack extends React.Component<Props, State> {
? renderHeader({ ? renderHeader({
mode: 'float', mode: 'float',
layout, layout,
insets: { top, right, bottom, left },
scenes, scenes,
state, state,
getPreviousRoute, getPreviousRoute,

Some files were not shown because too many files have changed in this diff Show More