mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-04-26 13:35:32 +08:00
Compare commits
27 Commits
v1.0.0-bet
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac3bb90ed | ||
|
|
627c487936 | ||
|
|
f46bdff703 | ||
|
|
bc75a5b7b9 | ||
|
|
50d5c8bc0a | ||
|
|
c7b73cd8b4 | ||
|
|
e2e540c32d | ||
|
|
7a57c4e779 | ||
|
|
4373544257 | ||
|
|
8329e269b6 | ||
|
|
450a1e3ba5 | ||
|
|
a83220b511 | ||
|
|
6af770d644 | ||
|
|
ef63b230b2 | ||
|
|
41b587ca65 | ||
|
|
faed4731bc | ||
|
|
7fe76fb7c6 | ||
|
|
89a6668595 | ||
|
|
9cf9e25661 | ||
|
|
363f93fc4d | ||
|
|
928f632c89 | ||
|
|
950d0c6877 | ||
|
|
5a26506595 | ||
|
|
5cb42bdf5d | ||
|
|
d5618ebd41 | ||
|
|
368bc615c1 | ||
|
|
f332b6bdf7 |
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,10 +1,10 @@
|
||||
## The issue list is reserved for bugs and feature requests, not for questions.
|
||||
## The issue tracker is reserved for bug reports only.
|
||||
|
||||
For usage questions, try to:
|
||||
If you have a question or feature request, please try one of the following resources:
|
||||
|
||||
- [Read the docs](https://reactnavigation.org/)
|
||||
- [Ask on the Reactiflux (#react-navigation)](https://discord.gg/reactiflux)
|
||||
- [Look for / ask questions on StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation)
|
||||
- [Read the documentation](https://reactnavigation.org/)
|
||||
- [Post a feature request to Canny](https://react-navigation.canny.io/feature-requests)
|
||||
- [Get help on Discord chat (#react-navigation on Reactiflux)](https://discord.gg/4xEK3nD) or [on StackOverflow](https://stackoverflow.com/questions/tagged/react-navigation)
|
||||
- Search for your issue - it may have already been answered or even fixed in the development branch. However, if you find that an old, closed issue still persists in the latest version, you should open a new issue.
|
||||
|
||||
---
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# React Navigation [](https://circleci.com/gh/react-community/react-navigation/tree/master) [](https://badge.fury.io/js/react-navigation) [](https://codecov.io/gh/react-community/react-navigation) [](https://reactnavigation.org/docs/guides/contributors)
|
||||
# React Navigation
|
||||
|
||||
[](https://badge.fury.io/js/react-navigation) [](https://codecov.io/gh/react-community/react-navigation) [](https://reactnavigation.org/docs/guides/contributors)
|
||||
|
||||
*Learn once, navigate anywhere.*
|
||||
|
||||
|
||||
@@ -158,6 +158,10 @@ Object to override the distance of touch start from the edge of the screen to re
|
||||
- `horizontal` - *number* - Distance for horizontal direction. Defaults to 25.
|
||||
- `vertical` - *number* - Distance for vertical direction. Defaults to 135.
|
||||
|
||||
#### `gestureDirection`
|
||||
|
||||
String to override the direction for dismiss gesture. `default` for normal behaviour or `inverted` for right-to-left swipes.
|
||||
|
||||
### Navigator Props
|
||||
|
||||
The navigator component created by `StackNavigator(...)` takes the following props:
|
||||
@@ -222,3 +226,5 @@ const ModalNavigator = StackNavigator(
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Header transitions can also be configured using `headerLeftInterpolator`, `headerTitleInterpolator` and `headerRightInterpolator` fields under `transitionConfig`.
|
||||
|
||||
@@ -88,7 +88,6 @@ The route configs object is a mapping from route name to a route config, which t
|
||||
- `swipeEnabled` - Whether to allow swiping between tabs.
|
||||
- `animationEnabled` - Whether to animate when changing tabs.
|
||||
- `configureTransition` - a function that, given `currentTransitionProps` and `nextTransitionProps`, returns a configuration object that describes the animation between tabs.
|
||||
- `lazy` - Whether to lazily render tabs as needed as opposed to rendering them upfront.
|
||||
- `initialLayout` - Optional object containing the initial `height` and `width`, can be passed to prevent the one frame delay in [react-native-tab-view](https://github.com/react-native-community/react-native-tab-view#avoid-one-frame-delay) rendering.
|
||||
- `tabBarOptions` - Configure the tab bar, see below.
|
||||
|
||||
@@ -168,6 +167,10 @@ Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`
|
||||
|
||||
True or false to show or hide the tab bar, if not set then defaults to true.
|
||||
|
||||
#### `swipeEnabled`
|
||||
|
||||
True or false to enable or disable swiping between tabs, if not set then defaults to TabNavigatorConfig option swipeEnabled.
|
||||
|
||||
#### `tabBarIcon`
|
||||
|
||||
React Element or a function that given `{ focused: boolean, tintColor: string }` returns a React.Node, to display in tab bar.
|
||||
|
||||
@@ -166,20 +166,24 @@ render: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?Navig
|
||||
### `onTransitionStart` function
|
||||
Invoked when the transition animation is about to start.
|
||||
|
||||
If you return a promise from `onTransitionStart`, the transition animation will begin after the promise is resolved.
|
||||
|
||||
#### Flow definition
|
||||
```js
|
||||
onTransitionStart: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?NavigationTransitionProps) => void,
|
||||
onTransitionStart: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?NavigationTransitionProps) => (Promise | void),
|
||||
```
|
||||
#### Parameters
|
||||
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/react-community/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current state and props
|
||||
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/react-community/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous state and props
|
||||
|
||||
#### Returns
|
||||
- none.
|
||||
- `Promise` to delay the start of the transition animation, or none to begin the transition animation immediately.
|
||||
|
||||
### `onTransitionEnd` function
|
||||
Invoked once the transition animation completes.
|
||||
|
||||
If you return a promise from `onTransitionEnd`, any queued transition animations will begin after the promise is resolved.
|
||||
|
||||
#### Flow definition
|
||||
```js
|
||||
onTransitionEnd: () => void
|
||||
|
||||
@@ -6,7 +6,7 @@ Note that if you want to dispatch react-navigation actions you should use the ac
|
||||
|
||||
The following actions are supported:
|
||||
* [Navigate](#Navigate) - Navigate to another route
|
||||
* [Reset](#Reset) - Replace current state with a new state
|
||||
* [Reset](#reset) - Replace current state with a new state
|
||||
* [Back](#Back) - Go back to previous state
|
||||
* [Set Params](#SetParams) - Set Params for given route
|
||||
* [Init](#Init) - Used to initialize first state if state is undefined
|
||||
|
||||
@@ -61,7 +61,7 @@ class Root extends React.Component {
|
||||
|
||||
Once you do this, your navigation state is stored within your redux store, at which point you can fire navigation actions using your redux dispatch function.
|
||||
|
||||
Keep in mind that when a navigator is given a `navigation` prop, it relinquishes control of its internal state. That means you are now responsible for persisting its state, handling any deep linking, [Handling the Hardware Back Button in Android](#handling-the-hardware-back-button-in-android), etc.
|
||||
Keep in mind that when a navigator is given a `navigation` prop, it relinquishes control of its internal state. That means you are now responsible for persisting its state, handling any deep linking, [Handling the Hardware Back Button in Android](#Handling-the-Hardware-Back-Button-in-Android), etc.
|
||||
|
||||
Navigation state is automatically passed down from one navigator to another when you nest them. Note that in order for a child navigator to receive the state from a parent navigator, it should be defined as a `screen`.
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@ A playground for experimenting with react-navigation in a pure-JS React Native a
|
||||
|
||||
## Usage
|
||||
|
||||
Please see the [Contributors Guide](https://github.com/react-community/react-navigation/blob/master/docs/guides/Contributors.md#development) for instructions on running these example apps.
|
||||
Please see the [Contributors Guide](https://reactnavigation.org/docs/guides/contributors#Run-the-Example-App) for instructions on running these example apps.
|
||||
|
||||
You can view this example application directly on your phone by visiting [our expo demo](https://exp.host/@react-navigation/NavigationPlayground).
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"icon": "./assets/icons/react-navigation.png",
|
||||
"hideExponentText": false
|
||||
},
|
||||
"sdkVersion": "23.0.0",
|
||||
"sdkVersion": "24.0.0",
|
||||
"entryPoint": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
|
||||
"packagerOpts": {
|
||||
"assetExts": [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import { ScreenOrientation } from 'expo';
|
||||
import { Constants, ScreenOrientation } from 'expo';
|
||||
|
||||
ScreenOrientation.allow(ScreenOrientation.Orientation.ALL);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
StatusBar,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView, StackNavigator } from 'react-navigation';
|
||||
@@ -19,6 +20,7 @@ import Banner from './Banner';
|
||||
import CustomTabs from './CustomTabs';
|
||||
import CustomTransitioner from './CustomTransitioner';
|
||||
import Drawer from './Drawer';
|
||||
import MultipleDrawer from './MultipleDrawer';
|
||||
import TabsInDrawer from './TabsInDrawer';
|
||||
import ModalStack from './ModalStack';
|
||||
import StacksInTabs from './StacksInTabs';
|
||||
@@ -43,6 +45,11 @@ const ExampleRoutes = {
|
||||
description: 'Android-style drawer navigation',
|
||||
screen: Drawer,
|
||||
},
|
||||
// MultipleDrawer: {
|
||||
// name: 'Multiple Drawer Example',
|
||||
// description: 'Add any drawer you need',
|
||||
// screen: MultipleDrawer,
|
||||
// },
|
||||
TabsInDrawer: {
|
||||
name: 'Drawer + Tabs Example',
|
||||
description: 'A drawer combined with tabs',
|
||||
@@ -98,34 +105,47 @@ const ExampleRoutes = {
|
||||
},
|
||||
};
|
||||
|
||||
const MainScreen = ({ navigation }) => (
|
||||
<ScrollView style={{ flex: 1 }} contentInsetAdjustmentBehavior="automatic">
|
||||
<Banner />
|
||||
{Object.keys(ExampleRoutes).map((routeName: string) => (
|
||||
<TouchableOpacity
|
||||
key={routeName}
|
||||
onPress={() => {
|
||||
const { path, params, screen } = ExampleRoutes[routeName];
|
||||
const { router } = screen;
|
||||
const action = path && router.getActionForPathAndParams(path, params);
|
||||
navigation.navigate(routeName, {}, action);
|
||||
}}
|
||||
>
|
||||
<SafeAreaView
|
||||
style={styles.itemContainer}
|
||||
forceInset={{ vertical: 'never' }}
|
||||
>
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.title}>{ExampleRoutes[routeName].name}</Text>
|
||||
<Text style={styles.description}>
|
||||
{ExampleRoutes[routeName].description}
|
||||
</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
class MainScreen extends React.Component<*> {
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
<Banner />
|
||||
{Object.keys(ExampleRoutes).map((routeName: string) => (
|
||||
<TouchableOpacity
|
||||
key={routeName}
|
||||
onPress={() => {
|
||||
const { path, params, screen } = ExampleRoutes[routeName];
|
||||
const { router } = screen;
|
||||
const action =
|
||||
path && router.getActionForPathAndParams(path, params);
|
||||
navigation.navigate(routeName, {}, action);
|
||||
}}
|
||||
>
|
||||
<SafeAreaView
|
||||
style={styles.itemContainer}
|
||||
forceInset={{ vertical: 'never' }}
|
||||
>
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.title}>
|
||||
{ExampleRoutes[routeName].name}
|
||||
</Text>
|
||||
<Text style={styles.description}>
|
||||
{ExampleRoutes[routeName].description}
|
||||
</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<View style={styles.statusBarUnderlay} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AppNavigator = StackNavigator(
|
||||
{
|
||||
@@ -165,6 +185,14 @@ const styles = StyleSheet.create({
|
||||
marginBottom: 20,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
statusBarUnderlay: {
|
||||
backgroundColor: '#673ab7',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: Constants.statusBarHeight,
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
|
||||
@@ -22,6 +22,7 @@ export default Banner;
|
||||
const styles = StyleSheet.create({
|
||||
bannerContainer: {
|
||||
backgroundColor: '#673ab7',
|
||||
paddingTop: 20,
|
||||
},
|
||||
banner: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Platform,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
StatusBar,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
@@ -32,6 +33,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
title="Go back"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Platform,
|
||||
Easing,
|
||||
View,
|
||||
Animated,
|
||||
Image,
|
||||
Button,
|
||||
Easing,
|
||||
Image,
|
||||
Platform,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {
|
||||
Transitioner,
|
||||
@@ -30,6 +31,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
)}
|
||||
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Platform, ScrollView } from 'react-native';
|
||||
import { Button, Platform, ScrollView, StatusBar } from 'react-native';
|
||||
import { DrawerNavigator, SafeAreaView } from 'react-navigation';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
import SampleText from './SampleText';
|
||||
@@ -18,6 +18,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
@@ -57,6 +58,9 @@ const DrawerExample = DrawerNavigator(
|
||||
},
|
||||
},
|
||||
{
|
||||
drawerOpenRoute: 'DrawerOpen',
|
||||
drawerCloseRoute: 'DrawerClose',
|
||||
drawerToggleRoute: 'DrawerToggle',
|
||||
initialRouteName: 'Drafts',
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ScrollView, Text } from 'react-native';
|
||||
import { Button, ScrollView, StatusBar, Text } from 'react-native';
|
||||
import { SafeAreaView, StackNavigator } from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
@@ -36,6 +36,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
)}
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
|
||||
84
examples/NavigationPlayground/js/MultipleDrawer.js
Normal file
84
examples/NavigationPlayground/js/MultipleDrawer.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Platform, ScrollView, StyleSheet } from 'react-native';
|
||||
import { DrawerNavigator } from 'react-navigation';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
const MyNavScreen = ({ navigation, banner }) => (
|
||||
<ScrollView style={styles.container}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('DrawerOpen')}
|
||||
title="Open drawer"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
const InboxScreen = ({ navigation }) => (
|
||||
<MyNavScreen banner={'Inbox Screen'} navigation={navigation} />
|
||||
);
|
||||
InboxScreen.navigationOptions = {
|
||||
drawerLabel: 'Inbox',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="move-to-inbox"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const DraftsScreen = ({ navigation }) => (
|
||||
<MyNavScreen banner={'Drafts Screen'} navigation={navigation} />
|
||||
);
|
||||
DraftsScreen.navigationOptions = {
|
||||
drawerLabel: 'Drafts',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
const DrawerExample = DrawerNavigator(
|
||||
{
|
||||
Inbox: {
|
||||
path: '/',
|
||||
screen: InboxScreen,
|
||||
},
|
||||
Drafts: {
|
||||
path: '/sent',
|
||||
screen: DraftsScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
drawerOpenRoute: 'DrawerOpen',
|
||||
drawerCloseRoute: 'DrawerClose',
|
||||
drawerToggleRoute: 'DrawerToggle',
|
||||
initialRouteName: 'Drafts',
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const MainDrawerExample = DrawerNavigator({
|
||||
Drafts: {
|
||||
screen: DrawerExample,
|
||||
},
|
||||
}, {
|
||||
drawerOpenRoute: 'DrawerOpen',
|
||||
drawerCloseRoute: 'DrawerClose',
|
||||
drawerToggleRoute: 'DrawerToggle',
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginTop: Platform.OS === 'ios' ? 20 : 0,
|
||||
},
|
||||
});
|
||||
|
||||
export default MainDrawerExample;
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ScrollView } from 'react-native';
|
||||
import { Button, ScrollView, StatusBar } from 'react-native';
|
||||
import { StackNavigator, SafeAreaView } from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
@@ -19,6 +19,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
title="Go to a photos screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Platform, ScrollView, View } from 'react-native';
|
||||
import { Button, Platform, ScrollView, StatusBar, View } from 'react-native';
|
||||
import { SafeAreaView, TabNavigator } from 'react-navigation';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import SampleText from './SampleText';
|
||||
@@ -20,6 +20,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
title="Go to settings tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ScrollView } from 'react-native';
|
||||
import { Button, ScrollView, StatusBar } from 'react-native';
|
||||
import { SafeAreaView, StackNavigator, TabNavigator } from 'react-navigation';
|
||||
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
@@ -27,6 +27,8 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ScrollView } from 'react-native';
|
||||
import { Button, ScrollView, StatusBar } from 'react-native';
|
||||
import { SafeAreaView, StackNavigator, TabNavigator } from 'react-navigation';
|
||||
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
@@ -27,6 +27,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
</SafeAreaView>
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ScrollView, Animated } from 'react-native';
|
||||
import { Animated, Button, ScrollView, StatusBar } from 'react-native';
|
||||
import { StackNavigator, TabNavigator } from 'react-navigation';
|
||||
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
@@ -25,6 +25,7 @@ const MyNavScreen = ({ navigation, banner }) => (
|
||||
title="Go to settings tab"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"private": true,
|
||||
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
|
||||
"scripts": {
|
||||
"postinstall": "rm -rf node_modules/react-navigation/{node_modules,examples}",
|
||||
"postinstall":
|
||||
"rm -rf node_modules/react-navigation/{node_modules,examples}",
|
||||
"start": "react-native-scripts start",
|
||||
"eject": "react-native-scripts eject",
|
||||
"android": "react-native-scripts android",
|
||||
@@ -12,29 +13,22 @@
|
||||
"test": "node node_modules/jest/bin/jest.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"expo": "^23.0.0",
|
||||
"expo": "^24.0.2",
|
||||
"react": "16.0.0",
|
||||
"react-native": "^0.50.3",
|
||||
"react-native": "^0.51.0",
|
||||
"react-navigation": "file:../.."
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "^21.0.0",
|
||||
"flow-bin": "^0.56.0",
|
||||
"jest": "^21.0.1",
|
||||
"jest-expo": "^23.0.0",
|
||||
"react-addons-test-utils": "16.0.0-alpha.3",
|
||||
"jest-expo": "^24.0.0",
|
||||
"react-native-scripts": "^1.5.0",
|
||||
"react-test-renderer": "16.0.0-alpha.12"
|
||||
"react-test-renderer": "16.0.0"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-expo",
|
||||
"moduleFileExtensions": [
|
||||
"web.js",
|
||||
"js",
|
||||
"json",
|
||||
"jsx",
|
||||
"node"
|
||||
],
|
||||
"moduleFileExtensions": ["web.js", "js", "json", "jsx", "node"],
|
||||
"modulePathIgnorePatterns": [
|
||||
"/node_modules/.*/react-native/",
|
||||
"/node_modules/.*/react/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,4 +2,4 @@
|
||||
|
||||
## Usage
|
||||
|
||||
Please see the [Contributors Guide](https://github.com/react-community/react-navigation/blob/master/docs/guides/Contributors.md#development) for instructions on running these example apps.
|
||||
Please see the [Contributors Guide](https://reactnavigation.org/docs/guides/contributors#Run-the-Example-App) for instructions on running these example apps.
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
## Usage
|
||||
|
||||
Please see the [Contributors Guide](https://github.com/react-community/react-navigation/blob/master/docs/guides/Contributors.md#development) for instructions on running these example apps.
|
||||
Please see the [Contributors Guide](https://reactnavigation.org/docs/guides/contributors#Run-the-Example-App) for instructions on running these example apps.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"icon": "./assets/icons/react-navigation.png",
|
||||
"hideExponentText": false
|
||||
},
|
||||
"sdkVersion": "22.0.0",
|
||||
"sdkVersion": "23.0.0",
|
||||
"entryPoint": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
|
||||
"packagerOpts": {
|
||||
"assetExts": [
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"private": true,
|
||||
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
|
||||
"scripts": {
|
||||
"postinstall": "rm -rf node_modules/react-navigation/{node_modules,examples}",
|
||||
"postinstall":
|
||||
"rm -rf node_modules/react-navigation/{node_modules,examples}",
|
||||
"start": "react-native-scripts start",
|
||||
"eject": "react-native-scripts eject",
|
||||
"android": "react-native-scripts android",
|
||||
@@ -35,6 +36,6 @@
|
||||
"jest-expo": "^23.0.0",
|
||||
"react-native-scripts": "^1.3.1",
|
||||
"react-navigation": "file:../..",
|
||||
"react-test-renderer": "16.0.0-alpha.12"
|
||||
"react-test-renderer": "16.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation",
|
||||
"version": "1.0.0-beta.21",
|
||||
"version": "1.0.0-beta.25",
|
||||
"description": "React Navigation",
|
||||
"main": "src/react-navigation.js",
|
||||
"sources": {
|
||||
@@ -55,7 +55,7 @@
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-native-drawer-layout-polyfill": "^1.3.2",
|
||||
"react-native-tab-view": "^0.0.70"
|
||||
"react-native-tab-view": "^0.0.74"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
|
||||
@@ -228,9 +228,9 @@ export type NavigationScreenDetails<T> = {
|
||||
navigation: NavigationScreenProp<NavigationRoute>,
|
||||
};
|
||||
|
||||
export type NavigationScreenOptions = {|
|
||||
export type NavigationScreenOptions = {
|
||||
title?: string,
|
||||
|};
|
||||
};
|
||||
|
||||
export type NavigationScreenConfigProps = {
|
||||
navigation: NavigationScreenProp<NavigationRoute>,
|
||||
@@ -300,6 +300,9 @@ export type HeaderProps = NavigationSceneRendererProps & {
|
||||
getScreenDetails: NavigationScene => NavigationScreenDetails<
|
||||
NavigationStackScreenOptions
|
||||
>,
|
||||
leftInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
titleInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
rightInterpolator: (props: NavigationSceneRendererProps) => {},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -321,6 +324,7 @@ export type NavigationStackScreenOptions = NavigationScreenOptions & {
|
||||
headerStyle?: ViewStyleProp,
|
||||
gesturesEnabled?: boolean,
|
||||
gestureResponseDistance?: { vertical?: number, horizontal?: number },
|
||||
gestureDirection?: 'default' | 'inverted',
|
||||
};
|
||||
|
||||
export type NavigationStackRouterConfig = {
|
||||
@@ -421,6 +425,31 @@ export type NavigationNavigatorProps<O: {}, S: {}> = {
|
||||
navigationOptions?: O,
|
||||
};
|
||||
|
||||
/**
|
||||
* Navigation container
|
||||
*/
|
||||
|
||||
export type NavigationContainer<
|
||||
State: NavigationState,
|
||||
Options: {},
|
||||
Props: {}
|
||||
> = React.ComponentType<NavigationContainerProps<State, Options> & Props> & {
|
||||
router: NavigationRouter<State, Options>,
|
||||
navigationOptions?: ?NavigationScreenConfig<Options>,
|
||||
};
|
||||
|
||||
export type NavigationContainerProps<S: {}, O: {}> = {
|
||||
uriPrefix?: string | RegExp,
|
||||
onNavigationStateChange?: (
|
||||
NavigationState,
|
||||
NavigationState,
|
||||
NavigationAction
|
||||
) => void,
|
||||
navigation?: NavigationScreenProp<S>,
|
||||
screenProps?: *,
|
||||
navigationOptions?: O,
|
||||
};
|
||||
|
||||
/**
|
||||
* Gestures, Animations, and Interpolators
|
||||
*/
|
||||
@@ -496,6 +525,11 @@ export type TransitionConfig = {
|
||||
// How to animate position and opacity of the screen
|
||||
// based on the value generated by the transitionSpec
|
||||
screenInterpolator?: (props: NavigationSceneRendererProps) => {},
|
||||
// How to animate position and opacity of the header componetns
|
||||
// based on the value generated by the transitionSpec
|
||||
headerLeftInterpolator?: (props: NavigationSceneRendererProps) => {},
|
||||
headerTitleInterpolator?: (props: NavigationSceneRendererProps) => {},
|
||||
headerRightInterpolator?: (props: NavigationSceneRendererProps) => {},
|
||||
// The style of the container. Useful when a scene doesn't have
|
||||
// 100% opacity and the underlying container is visible.
|
||||
containerStyle?: ViewStyleProp,
|
||||
|
||||
@@ -13,20 +13,10 @@ import type {
|
||||
NavigationNavigator,
|
||||
PossiblyDeprecatedNavigationAction,
|
||||
NavigationInitAction,
|
||||
NavigationContainerProps,
|
||||
NavigationContainer,
|
||||
} from './TypeDefinition';
|
||||
|
||||
type Props<S, O> = {
|
||||
uriPrefix?: string | RegExp,
|
||||
onNavigationStateChange?: (
|
||||
NavigationState,
|
||||
NavigationState,
|
||||
NavigationAction
|
||||
) => void,
|
||||
navigation?: NavigationScreenProp<S>,
|
||||
screenProps?: *,
|
||||
navigationOptions?: O,
|
||||
};
|
||||
|
||||
type State<NavState> = {
|
||||
nav: ?NavState,
|
||||
};
|
||||
@@ -40,15 +30,19 @@ type State<NavState> = {
|
||||
export default function createNavigationContainer<S: NavigationState, O: {}>(
|
||||
// Let the NavigationNavigator props flowwwww
|
||||
Component: NavigationNavigator<S, O, *>
|
||||
) {
|
||||
class NavigationContainer extends React.Component<Props<S, O>, State<S>> {
|
||||
): NavigationContainer<S, O, *> {
|
||||
class NavigationContainer extends React.Component<
|
||||
NavigationContainerProps<S, O>,
|
||||
State<S>
|
||||
> {
|
||||
subs: ?{
|
||||
remove: () => void,
|
||||
} = null;
|
||||
|
||||
static router = Component.router;
|
||||
static navigationOptions = null;
|
||||
|
||||
constructor(props: Props<S, O>) {
|
||||
constructor(props: NavigationContainerProps<S, O>) {
|
||||
super(props);
|
||||
|
||||
this._validateProps(props);
|
||||
@@ -64,7 +58,7 @@ export default function createNavigationContainer<S: NavigationState, O: {}>(
|
||||
return !this.props.navigation;
|
||||
}
|
||||
|
||||
_validateProps(props: Props<S, O>) {
|
||||
_validateProps(props: NavigationContainerProps<S, O>) {
|
||||
if (this._isStateful()) {
|
||||
return;
|
||||
}
|
||||
@@ -91,6 +85,8 @@ export default function createNavigationContainer<S: NavigationState, O: {}>(
|
||||
let path = url.split(delimiter)[1];
|
||||
if (typeof path === 'undefined') {
|
||||
path = url;
|
||||
} else if (path === '') {
|
||||
path = '/';
|
||||
}
|
||||
return {
|
||||
path,
|
||||
@@ -142,7 +138,7 @@ export default function createNavigationContainer<S: NavigationState, O: {}>(
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props<S, O>) {
|
||||
componentWillReceiveProps(nextProps: NavigationContainerProps<S, O>) {
|
||||
this._validateProps(nextProps);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ const DefaultDrawerConfig = {
|
||||
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
|
||||
},
|
||||
contentComponent: defaultContentComponent,
|
||||
drawerOpenRoute: 'DrawerOpen',
|
||||
drawerCloseRoute: 'DrawerClose',
|
||||
drawerToggleRoute: 'DrawerToggle',
|
||||
drawerPosition: 'left',
|
||||
drawerBackgroundColor: 'white',
|
||||
useNativeAnimations: true,
|
||||
@@ -82,6 +85,9 @@ const DrawerNavigator = (
|
||||
drawerPosition,
|
||||
useNativeAnimations,
|
||||
drawerBackgroundColor,
|
||||
drawerOpenRoute,
|
||||
drawerCloseRoute,
|
||||
drawerToggleRoute,
|
||||
...tabsConfig
|
||||
} = mergedConfig;
|
||||
|
||||
@@ -89,7 +95,7 @@ const DrawerNavigator = (
|
||||
|
||||
const drawerRouter = TabRouter(
|
||||
{
|
||||
DrawerClose: {
|
||||
[drawerCloseRoute]: {
|
||||
screen: createNavigator(
|
||||
contentRouter,
|
||||
routeConfigs,
|
||||
@@ -99,15 +105,15 @@ const DrawerNavigator = (
|
||||
<DrawerScreen {...props} />
|
||||
)),
|
||||
},
|
||||
DrawerOpen: {
|
||||
[drawerOpenRoute]: {
|
||||
screen: () => null,
|
||||
},
|
||||
DrawerToggle: {
|
||||
[drawerToggleRoute]: {
|
||||
screen: () => null,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'DrawerClose',
|
||||
initialRouteName: drawerCloseRoute,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -126,6 +132,9 @@ const DrawerNavigator = (
|
||||
contentComponent={contentComponent}
|
||||
contentOptions={contentOptions}
|
||||
drawerPosition={drawerPosition}
|
||||
drawerOpenRoute={drawerOpenRoute}
|
||||
drawerCloseRoute={drawerCloseRoute}
|
||||
drawerToggleRoute={drawerToggleRoute}
|
||||
/>
|
||||
));
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ const TabNavigator = (
|
||||
swipeEnabled,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
lazy,
|
||||
initialLayout,
|
||||
...tabsConfig
|
||||
} = mergedConfig;
|
||||
@@ -70,7 +69,6 @@ const TabNavigator = (
|
||||
swipeEnabled={swipeEnabled}
|
||||
animationEnabled={animationEnabled}
|
||||
configureTransition={configureTransition}
|
||||
lazy={lazy}
|
||||
initialLayout={initialLayout}
|
||||
/>
|
||||
));
|
||||
@@ -84,7 +82,6 @@ const Presets = {
|
||||
tabBarPosition: 'bottom',
|
||||
swipeEnabled: false,
|
||||
animationEnabled: false,
|
||||
lazy: false,
|
||||
initialLayout: undefined,
|
||||
},
|
||||
AndroidTopTabs: {
|
||||
@@ -92,7 +89,6 @@ const Presets = {
|
||||
tabBarPosition: 'top',
|
||||
swipeEnabled: true,
|
||||
animationEnabled: true,
|
||||
lazy: false,
|
||||
initialLayout: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -137,6 +137,7 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
@@ -182,6 +183,7 @@ exports[`DrawerNavigator renders successfully 1`] = `
|
||||
tvParallaxProperties={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
|
||||
@@ -89,6 +89,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
leftInterpolator={[Function]}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
@@ -107,6 +108,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
},
|
||||
}
|
||||
}
|
||||
rightInterpolator={[Function]}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
@@ -118,9 +120,11 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
titleInterpolator={[Function]}
|
||||
transitionConfig={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
@@ -316,6 +320,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
leftInterpolator={[Function]}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
@@ -334,6 +339,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
},
|
||||
}
|
||||
}
|
||||
rightInterpolator={[Function]}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
@@ -345,9 +351,11 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
titleInterpolator={[Function]}
|
||||
transitionConfig={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
|
||||
@@ -69,6 +69,7 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
style={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
|
||||
@@ -61,11 +61,7 @@ export default (
|
||||
const routes = order.map((routeName: string) => {
|
||||
const tabRouter = tabRouters[routeName];
|
||||
if (tabRouter) {
|
||||
const childAction =
|
||||
action.action ||
|
||||
NavigationActions.init({
|
||||
...(action.params ? { params: action.params } : {}),
|
||||
});
|
||||
const childAction = NavigationActions.init();
|
||||
return {
|
||||
...tabRouter.getStateForAction(childAction),
|
||||
key: routeName,
|
||||
@@ -106,7 +102,7 @@ export default (
|
||||
const activeTabRouter = tabRouters[order[state.index]];
|
||||
if (activeTabRouter) {
|
||||
const activeTabState = activeTabRouter.getStateForAction(
|
||||
action.action || action,
|
||||
action,
|
||||
activeTabLastState
|
||||
);
|
||||
if (!activeTabState && inputState) {
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
import React from 'react';
|
||||
import TabRouter from '../TabRouter';
|
||||
import StackRouter from '../StackRouter';
|
||||
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
|
||||
import type { NavigationRoute, NavigationState } from '../../TypeDefinition';
|
||||
|
||||
const INIT_ACTION = { type: NavigationActions.INIT };
|
||||
|
||||
const BareLeafRouteConfig = {
|
||||
@@ -212,7 +215,6 @@ describe('TabRouter', () => {
|
||||
const navAction = {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Baz',
|
||||
params: { foo: '42', bar: '43' },
|
||||
};
|
||||
let state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
@@ -224,8 +226,8 @@ describe('TabRouter', () => {
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo', params: { foo: '42', bar: '43' } },
|
||||
{ key: 'Bar', routeName: 'Bar', params: { foo: '42', bar: '43' } },
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -245,8 +247,8 @@ describe('TabRouter', () => {
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo', params: { foo: '42', bar: '43' } },
|
||||
{ key: 'Bar', routeName: 'Bar', params: { foo: '42', bar: '43' } },
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -604,4 +606,71 @@ describe('TabRouter', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('Inner actions are only unpacked if the current tab matches', () => {
|
||||
const PlainScreen = () => <div />;
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
ScreenB.router = StackRouter({
|
||||
Baz: { screen: PlainScreen },
|
||||
Zoo: { screen: PlainScreen },
|
||||
});
|
||||
ScreenA.router = StackRouter({
|
||||
Bar: { screen: PlainScreen },
|
||||
Boo: { screen: ScreenB },
|
||||
});
|
||||
const router = TabRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
});
|
||||
const screenApreState = {
|
||||
index: 0,
|
||||
key: 'Init',
|
||||
routeName: 'Foo',
|
||||
routes: [{ key: 'Init', routeName: 'Bar' }],
|
||||
};
|
||||
const preState = {
|
||||
index: 0,
|
||||
routes: [screenApreState],
|
||||
};
|
||||
|
||||
type ComparableRoute = {
|
||||
routeName?: string,
|
||||
routes?: Array<ComparableRoute>,
|
||||
};
|
||||
|
||||
type RouteOrState =
|
||||
| NavigationRoute
|
||||
| NavigationState
|
||||
| (NavigationRoute & NavigationState);
|
||||
|
||||
const comparable = (state: RouteOrState): ComparableRoute => {
|
||||
let result = {};
|
||||
if (typeof state.routeName === 'string') {
|
||||
result = { ...result, routeName: state.routeName };
|
||||
}
|
||||
if (state.routes instanceof Array) {
|
||||
result = {
|
||||
...result,
|
||||
routes: state.routes.map(comparable),
|
||||
};
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const action = NavigationActions.navigate({
|
||||
routeName: 'Boo',
|
||||
action: NavigationActions.navigate({ routeName: 'Zoo' }),
|
||||
});
|
||||
|
||||
const expectedState = ScreenA.router.getStateForAction(
|
||||
action,
|
||||
screenApreState
|
||||
);
|
||||
const state = router.getStateForAction(action, preState);
|
||||
const innerState = state ? state.routes[0] : state;
|
||||
|
||||
expect(expectedState && comparable(expectedState)).toEqual(
|
||||
innerState && comparable(innerState)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
12
src/utils/ReactNativeFeatures.js
Normal file
12
src/utils/ReactNativeFeatures.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/* @flow */
|
||||
|
||||
import { NativeModules } from 'react-native';
|
||||
const { PlatformConstants } = NativeModules;
|
||||
|
||||
export const supportsImprovedSpringAnimation = () => {
|
||||
if (PlatformConstants && PlatformConstants.reactNativeVersion) {
|
||||
const { major, minor } = PlatformConstants.reactNativeVersion;
|
||||
return minor >= 50 || (major === 0 && minor === 0); // `master` has major + minor set to 0
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -163,6 +163,11 @@ class CardStack extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const renderHeader = header || ((props: *) => <Header {...props} />);
|
||||
const {
|
||||
headerLeftInterpolator,
|
||||
headerTitleInterpolator,
|
||||
headerRightInterpolator,
|
||||
} = this._getTransitionConfig();
|
||||
|
||||
// We need to explicitly exclude `mode` since Flow doesn't see
|
||||
// mode: headerMode override below and reports prop mismatch
|
||||
@@ -173,6 +178,9 @@ class CardStack extends React.Component<Props> {
|
||||
scene,
|
||||
mode: headerMode,
|
||||
getScreenDetails: this._getScreenDetails,
|
||||
leftInterpolator: headerLeftInterpolator,
|
||||
titleInterpolator: headerTitleInterpolator,
|
||||
rightInterpolator: headerRightInterpolator,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -232,6 +240,8 @@ class CardStack extends React.Component<Props> {
|
||||
const { navigation, position, layout, scene, scenes, mode } = this.props;
|
||||
const { index } = navigation.state;
|
||||
const isVertical = mode === 'modal';
|
||||
const { options } = this._getScreenDetails(scene);
|
||||
const gestureDirectionInverted = options.gestureDirection === 'inverted';
|
||||
|
||||
const responder = PanResponder.create({
|
||||
onPanResponderTerminate: () => {
|
||||
@@ -262,7 +272,9 @@ class CardStack extends React.Component<Props> {
|
||||
const axisHasBeenMeasured = !!axisLength;
|
||||
|
||||
// Measure the distance from the touch to the edge of the screen
|
||||
const screenEdgeDistance = currentDragPosition - currentDragDistance;
|
||||
const screenEdgeDistance = gestureDirectionInverted
|
||||
? axisLength - (currentDragPosition - currentDragDistance)
|
||||
: currentDragPosition - currentDragDistance;
|
||||
// Compare to the gesture distance relavant to card or modal
|
||||
const {
|
||||
gestureResponseDistance: userGestureResponseDistance = {},
|
||||
@@ -294,7 +306,7 @@ class CardStack extends React.Component<Props> {
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const currentValue =
|
||||
I18nManager.isRTL && axis === 'dx'
|
||||
(I18nManager.isRTL && axis === 'dx') !== gestureDirectionInverted
|
||||
? startValue + gesture[axis] / axisDistance
|
||||
: startValue - gesture[axis] / axisDistance;
|
||||
const value = clamp(index - 1, currentValue, index);
|
||||
@@ -317,12 +329,19 @@ class CardStack extends React.Component<Props> {
|
||||
const axisDistance = isVertical
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const movedDistance = gesture[isVertical ? 'dy' : 'dx'];
|
||||
const gestureVelocity = gesture[isVertical ? 'vy' : 'vx'];
|
||||
const movementDirection = gestureDirectionInverted ? -1 : 1;
|
||||
const movedDistance =
|
||||
movementDirection * gesture[isVertical ? 'dy' : 'dx'];
|
||||
const gestureVelocity =
|
||||
movementDirection * gesture[isVertical ? 'vy' : 'vx'];
|
||||
const defaultVelocity = axisDistance / ANIMATION_DURATION;
|
||||
const velocity = Math.max(Math.abs(gestureVelocity), defaultVelocity);
|
||||
const resetDuration = movedDistance / velocity;
|
||||
const goBackDuration = (axisDistance - movedDistance) / velocity;
|
||||
const resetDuration = gestureDirectionInverted
|
||||
? (axisDistance - movedDistance) / velocity
|
||||
: movedDistance / velocity;
|
||||
const goBackDuration = gestureDirectionInverted
|
||||
? movedDistance / velocity
|
||||
: (axisDistance - movedDistance) / velocity;
|
||||
|
||||
// To asyncronously get the current animated value, we need to run stopAnimation:
|
||||
position.stopAnimation((value: number) => {
|
||||
@@ -348,7 +367,6 @@ class CardStack extends React.Component<Props> {
|
||||
},
|
||||
});
|
||||
|
||||
const { options } = this._getScreenDetails(scene);
|
||||
const gesturesEnabled =
|
||||
typeof options.gesturesEnabled === 'boolean'
|
||||
? options.gesturesEnabled
|
||||
|
||||
@@ -9,12 +9,25 @@ import type {
|
||||
} from '../../TypeDefinition';
|
||||
|
||||
import CardStackStyleInterpolator from './CardStackStyleInterpolator';
|
||||
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
||||
|
||||
const IOSTransitionSpec = ({
|
||||
duration: 500,
|
||||
easing: Easing.bezier(0.2833, 0.99, 0.31833, 0.99),
|
||||
timing: Animated.timing,
|
||||
}: NavigationTransitionSpec);
|
||||
let IOSTransitionSpec;
|
||||
if (ReactNativeFeatures.supportsImprovedSpringAnimation()) {
|
||||
// These are the exact values from UINavigationController's animation configuration
|
||||
IOSTransitionSpec = ({
|
||||
timing: Animated.spring,
|
||||
stiffness: 1000,
|
||||
damping: 500,
|
||||
mass: 3,
|
||||
}: NavigationTransitionSpec);
|
||||
} else {
|
||||
// This is an approximation of the IOS spring animation using a derived bezier curve
|
||||
IOSTransitionSpec = ({
|
||||
duration: 500,
|
||||
easing: Easing.bezier(0.2833, 0.99, 0.31833, 0.99),
|
||||
timing: Animated.timing,
|
||||
}: NavigationTransitionSpec);
|
||||
}
|
||||
|
||||
// Standard iOS navigation transition
|
||||
const SlideFromRightIOS = ({
|
||||
|
||||
@@ -33,6 +33,9 @@ export type DrawerViewConfig = {
|
||||
drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open',
|
||||
drawerWidth?: number | (() => number),
|
||||
drawerPosition?: 'left' | 'right',
|
||||
drawerOpenRoute?: string,
|
||||
drawerCloseRoute?: string,
|
||||
drawerToggleRoute?: string,
|
||||
contentComponent?: React.ComponentType<*>,
|
||||
contentOptions?: {},
|
||||
style?: ViewStyleProp,
|
||||
@@ -41,11 +44,21 @@ export type DrawerViewConfig = {
|
||||
screenProps?: {},
|
||||
};
|
||||
|
||||
export type DrawerViewPropsExceptRouter = DrawerViewConfig & {
|
||||
navigation: NavigationScreenProp<NavigationState>,
|
||||
};
|
||||
export type DrawerViewProps = {
|
||||
drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open',
|
||||
drawerWidth: number | (() => number),
|
||||
drawerPosition: 'left' | 'right',
|
||||
drawerOpenRoute: string,
|
||||
drawerCloseRoute: string,
|
||||
drawerToggleRoute: string,
|
||||
contentComponent: React.ComponentType<*>,
|
||||
contentOptions?: {},
|
||||
style?: ViewStyleProp,
|
||||
useNativeAnimations: boolean,
|
||||
drawerBackgroundColor: string,
|
||||
screenProps?: {},
|
||||
|
||||
export type DrawerViewProps = DrawerViewPropsExceptRouter & {
|
||||
navigation: NavigationScreenProp<NavigationState>,
|
||||
router: NavigationRouter<NavigationState, NavigationDrawerScreenOptions>,
|
||||
};
|
||||
|
||||
@@ -81,14 +94,19 @@ export default class DrawerView extends React.PureComponent<
|
||||
if (
|
||||
this.props.navigation.state.index !== nextProps.navigation.state.index
|
||||
) {
|
||||
const {
|
||||
drawerOpenRoute,
|
||||
drawerCloseRoute,
|
||||
drawerToggleRoute,
|
||||
} = this.props;
|
||||
const { routes, index } = nextProps.navigation.state;
|
||||
if (routes[index].routeName === 'DrawerOpen') {
|
||||
if (routes[index].routeName === drawerOpenRoute) {
|
||||
this._drawer.openDrawer();
|
||||
} else if (routes[index].routeName === 'DrawerToggle') {
|
||||
if (this._drawer.state.drawerShown) {
|
||||
this.props.navigation.navigate('DrawerClose');
|
||||
} else if (routes[index].routeName === drawerToggleRoute) {
|
||||
if (this.props.navigation.state.index === 0) {
|
||||
this.props.navigation.navigate(drawerOpenRoute);
|
||||
} else {
|
||||
this.props.navigation.navigate('DrawerOpen');
|
||||
this.props.navigation.navigate(drawerCloseRoute);
|
||||
}
|
||||
} else {
|
||||
this._drawer.closeDrawer();
|
||||
@@ -100,27 +118,28 @@ export default class DrawerView extends React.PureComponent<
|
||||
_screenNavigationProp: NavigationScreenProp<NavigationStateRoute>;
|
||||
|
||||
_handleDrawerOpen = () => {
|
||||
const { navigation } = this.props;
|
||||
const { navigation, drawerOpenRoute } = this.props;
|
||||
const { routes, index } = navigation.state;
|
||||
if (routes[index].routeName !== 'DrawerOpen') {
|
||||
this.props.navigation.navigate('DrawerOpen');
|
||||
if (routes[index].routeName !== drawerOpenRoute) {
|
||||
this.props.navigation.navigate(drawerOpenRoute);
|
||||
}
|
||||
};
|
||||
|
||||
_handleDrawerClose = () => {
|
||||
const { navigation } = this.props;
|
||||
const { navigation, drawerCloseRoute } = this.props;
|
||||
const { routes, index } = navigation.state;
|
||||
if (routes[index].routeName !== 'DrawerClose') {
|
||||
this.props.navigation.navigate('DrawerClose');
|
||||
if (routes[index].routeName !== drawerCloseRoute) {
|
||||
this.props.navigation.navigate(drawerCloseRoute);
|
||||
}
|
||||
};
|
||||
|
||||
_updateScreenNavigation = (
|
||||
navigation: NavigationScreenProp<NavigationState>
|
||||
) => {
|
||||
const { drawerCloseRoute } = this.props;
|
||||
// $FlowFixMe there's no way type the specific shape of the nav state
|
||||
const navigationState: NavigationStateRoute = navigation.state.routes.find(
|
||||
(route: *) => route.routeName === 'DrawerClose'
|
||||
(route: *) => route.routeName === drawerCloseRoute
|
||||
);
|
||||
if (
|
||||
this._screenNavigationProp &&
|
||||
@@ -146,8 +165,9 @@ export default class DrawerView extends React.PureComponent<
|
||||
};
|
||||
|
||||
_getNavigationState = (navigation: NavigationScreenProp<NavigationState>) => {
|
||||
const { drawerCloseRoute } = this.props;
|
||||
const navigationState = navigation.state.routes.find(
|
||||
(route: *) => route.routeName === 'DrawerClose'
|
||||
(route: *) => route.routeName === drawerCloseRoute
|
||||
);
|
||||
return navigationState;
|
||||
};
|
||||
@@ -168,7 +188,7 @@ export default class DrawerView extends React.PureComponent<
|
||||
|
||||
render() {
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName(
|
||||
'DrawerClose'
|
||||
this.props.drawerCloseRoute
|
||||
);
|
||||
|
||||
const screenNavigation = addNavigationHelpers({
|
||||
|
||||
@@ -49,6 +49,12 @@ const TITLE_OFFSET = Platform.OS === 'ios' ? 70 : 56;
|
||||
|
||||
type Props = HeaderProps & { isLandscape: boolean };
|
||||
class Header extends React.PureComponent<Props, State> {
|
||||
static defaultProps = {
|
||||
leftInterpolator: HeaderStyleInterpolator.forLeft,
|
||||
titleInterpolator: HeaderStyleInterpolator.forCenter,
|
||||
rightInterpolator: HeaderStyleInterpolator.forRight,
|
||||
};
|
||||
|
||||
static get HEIGHT() {
|
||||
console.warn(
|
||||
'Header.HEIGHT is deprecated and will be removed before react-navigation comes out of beta.'
|
||||
@@ -183,7 +189,7 @@ class Header extends React.PureComponent<Props, State> {
|
||||
props,
|
||||
'left',
|
||||
this._renderLeftComponent,
|
||||
HeaderStyleInterpolator.forLeft
|
||||
this.props.leftInterpolator
|
||||
);
|
||||
}
|
||||
|
||||
@@ -210,7 +216,7 @@ class Header extends React.PureComponent<Props, State> {
|
||||
{ ...props, style },
|
||||
'title',
|
||||
this._renderTitleComponent,
|
||||
HeaderStyleInterpolator.forCenter
|
||||
this.props.titleInterpolator
|
||||
);
|
||||
}
|
||||
|
||||
@@ -219,7 +225,7 @@ class Header extends React.PureComponent<Props, State> {
|
||||
props,
|
||||
'right',
|
||||
this._renderRightComponent,
|
||||
HeaderStyleInterpolator.forRight
|
||||
this.props.rightInterpolator
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Platform,
|
||||
SafeAreaView,
|
||||
StyleSheet,
|
||||
View,
|
||||
Animated,
|
||||
} from 'react-native';
|
||||
import withOrientation from './withOrientation';
|
||||
|
||||
@@ -101,19 +101,19 @@ class SafeView extends Component {
|
||||
const { forceInset = false, isLandscape, children, style } = this.props;
|
||||
|
||||
if (Platform.OS !== 'ios') {
|
||||
return <View style={style}>{this.props.children}</View>;
|
||||
return <Animated.View style={style}>{this.props.children}</Animated.View>;
|
||||
}
|
||||
|
||||
const safeAreaStyle = this._getSafeAreaStyle();
|
||||
|
||||
return (
|
||||
<View
|
||||
<Animated.View
|
||||
ref={c => (this.view = c)}
|
||||
onLayout={this._onLayout}
|
||||
style={safeAreaStyle}
|
||||
>
|
||||
{this.props.children}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class SafeView extends Component {
|
||||
const WIDTH = isLandscape ? X_HEIGHT : X_WIDTH;
|
||||
const HEIGHT = isLandscape ? X_WIDTH : X_HEIGHT;
|
||||
|
||||
this.view.measureInWindow((winX, winY, winWidth, winHeight) => {
|
||||
this.view._component.measureInWindow((winX, winY, winWidth, winHeight) => {
|
||||
let realY = winY;
|
||||
let realX = winX;
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ export type TabViewConfig = {
|
||||
currentTransitionProps: Object,
|
||||
nextTransitionProps: Object
|
||||
) => Object,
|
||||
lazy?: boolean,
|
||||
initialLayout?: Layout,
|
||||
};
|
||||
|
||||
@@ -47,7 +46,6 @@ type Props = {
|
||||
currentTransitionProps: Object,
|
||||
nextTransitionProps: Object
|
||||
) => Object,
|
||||
lazy?: boolean,
|
||||
initialLayout: Layout,
|
||||
|
||||
screenProps?: {},
|
||||
@@ -171,8 +169,6 @@ class TabView extends React.PureComponent<Props> {
|
||||
tabBarPosition,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
swipeEnabled,
|
||||
lazy,
|
||||
initialLayout,
|
||||
screenProps,
|
||||
} = this.props;
|
||||
@@ -190,6 +186,11 @@ class TabView extends React.PureComponent<Props> {
|
||||
const tabBarVisible =
|
||||
options.tabBarVisible == null ? true : options.tabBarVisible;
|
||||
|
||||
const swipeEnabled =
|
||||
options.swipeEnabled == null
|
||||
? this.props.swipeEnabled
|
||||
: options.swipeEnabled;
|
||||
|
||||
if (tabBarComponent !== undefined && tabBarVisible) {
|
||||
if (tabBarPosition === 'bottom') {
|
||||
renderFooter = this._renderTabBar;
|
||||
@@ -206,7 +207,6 @@ class TabView extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
const props = {
|
||||
lazy,
|
||||
initialLayout,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
@@ -221,7 +221,6 @@ class TabView extends React.PureComponent<Props> {
|
||||
style: styles.container,
|
||||
};
|
||||
|
||||
// $FlowFixMe: mismatch with react-native-tab-view type
|
||||
return <TabViewAnimated {...props} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,12 +172,17 @@ class Transitioner extends React.Component<Props, State> {
|
||||
|
||||
// update scenes and play the transition
|
||||
this._isTransitionRunning = true;
|
||||
this.setState(nextState, () => {
|
||||
nextProps.onTransitionStart &&
|
||||
nextProps.onTransitionStart(
|
||||
this.setState(nextState, async () => {
|
||||
if (nextProps.onTransitionStart) {
|
||||
const result = nextProps.onTransitionStart(
|
||||
this._transitionProps,
|
||||
this._prevTransitionProps
|
||||
);
|
||||
|
||||
if (result instanceof Promise) {
|
||||
await result;
|
||||
}
|
||||
}
|
||||
Animated.parallel(animations).start(this._onTransitionEnd);
|
||||
});
|
||||
}
|
||||
@@ -231,9 +236,18 @@ class Transitioner extends React.Component<Props, State> {
|
||||
|
||||
this._transitionProps = buildTransitionProps(this.props, nextState);
|
||||
|
||||
this.setState(nextState, () => {
|
||||
this.props.onTransitionEnd &&
|
||||
this.props.onTransitionEnd(this._transitionProps, prevTransitionProps);
|
||||
this.setState(nextState, async () => {
|
||||
if (this.props.onTransitionEnd) {
|
||||
const result = this.props.onTransitionEnd(
|
||||
this._transitionProps,
|
||||
prevTransitionProps
|
||||
);
|
||||
|
||||
if (result instanceof Promise) {
|
||||
await result;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._queuedTransition) {
|
||||
this._startTransition(
|
||||
this._queuedTransition.nextProps,
|
||||
|
||||
@@ -25,6 +25,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
style={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
@@ -133,7 +134,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
bounces={false}
|
||||
contentContainerStyle={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
contentOffset={
|
||||
@@ -163,9 +164,10 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
onTouchEnd={[Function]}
|
||||
onTouchMove={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
overScrollMode="never"
|
||||
pagingEnabled={true}
|
||||
scrollEnabled={undefined}
|
||||
scrollEventThrottle={16}
|
||||
scrollEventThrottle={1}
|
||||
scrollsToTop={false}
|
||||
sendMomentumEvents={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
@@ -178,7 +180,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
"overflow": "scroll",
|
||||
},
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
"flex": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -192,7 +194,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
"flexDirection": "row",
|
||||
},
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
"flex": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4488,11 +4488,11 @@ react-native-drawer-layout@1.3.2:
|
||||
dependencies:
|
||||
react-native-dismiss-keyboard "1.0.0"
|
||||
|
||||
react-native-tab-view@^0.0.70:
|
||||
version "0.0.70"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.70.tgz#1dd2ded32acd0cb6bfef38d26e53675db733b37b"
|
||||
react-native-tab-view@^0.0.74:
|
||||
version "0.0.74"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.74.tgz#62c0c882d9232b461ce181d440d683b4f99d1bd8"
|
||||
dependencies:
|
||||
prop-types "^15.5.10"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-native-vector-icons@^4.2.0:
|
||||
version "4.4.2"
|
||||
|
||||
Reference in New Issue
Block a user