mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-28 17:18:46 +08:00
Compare commits
51 Commits
v2.0.0-rc.
...
2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec1694f909 | ||
|
|
93db7d0c95 | ||
|
|
589b80b2fa | ||
|
|
364144d639 | ||
|
|
2fd7284fe2 | ||
|
|
6499f02157 | ||
|
|
39d5714ac0 | ||
|
|
1ceda5f04d | ||
|
|
4533883ba7 | ||
|
|
8ec2466fef | ||
|
|
1bd6593ede | ||
|
|
4e8d8ce12f | ||
|
|
9f95a7f10b | ||
|
|
f6bd3e4306 | ||
|
|
c1a94895f5 | ||
|
|
ad7cde9eb9 | ||
|
|
2643f690a9 | ||
|
|
8e52995ef3 | ||
|
|
8ed3817c90 | ||
|
|
eda9bfd567 | ||
|
|
723c5f2149 | ||
|
|
ab5481a290 | ||
|
|
df281cfed0 | ||
|
|
e0df3cf74a | ||
|
|
2440af66e4 | ||
|
|
47fe858d4e | ||
|
|
c641bee11b | ||
|
|
4b39e2db3c | ||
|
|
f7533a790f | ||
|
|
c56122466f | ||
|
|
7fc992dc58 | ||
|
|
32922cdd7d | ||
|
|
eda51b3b79 | ||
|
|
921ee09587 | ||
|
|
7ae4c60eb8 | ||
|
|
5fff7ef5c6 | ||
|
|
42bb1cc317 | ||
|
|
337fd89ad5 | ||
|
|
acf9b92ff7 | ||
|
|
5072130d6f | ||
|
|
20bbbd62ff | ||
|
|
0890896824 | ||
|
|
0cf14f8e1e | ||
|
|
e5e434c9e2 | ||
|
|
e5d8d2c216 | ||
|
|
abd5200739 | ||
|
|
202609d9cf | ||
|
|
7b4dd98255 | ||
|
|
70c644f522 | ||
|
|
5274d16e3b | ||
|
|
e5e2cbb86d |
@@ -42,9 +42,7 @@
|
||||
"react/forbid-prop-types": "warn",
|
||||
"react/prop-types": "off",
|
||||
"react/require-default-props": "off",
|
||||
"react/no-unused-prop-types": "off",
|
||||
},
|
||||
"settings": {
|
||||
"react/no-unused-prop-types": "off"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
|
||||
12
assetsTransformer.js
Normal file
12
assetsTransformer.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This file is needed to hijack asset imports so that test files don't attempt
|
||||
* to import them as JavaScript modules.
|
||||
* See https://github.com/facebook/jest/issues/2663#issuecomment-317109798
|
||||
*/
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
process(src, filename, config, options) {
|
||||
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
|
||||
},
|
||||
};
|
||||
@@ -27,6 +27,7 @@ import ModalStack from './ModalStack';
|
||||
import StacksInTabs from './StacksInTabs';
|
||||
import StacksOverTabs from './StacksOverTabs';
|
||||
import StacksWithKeys from './StacksWithKeys';
|
||||
import InactiveStack from './InactiveStack';
|
||||
import StackWithCustomHeaderBackImage from './StackWithCustomHeaderBackImage';
|
||||
import SimpleStack from './SimpleStack';
|
||||
import StackWithHeaderPreset from './StackWithHeaderPreset';
|
||||
@@ -34,6 +35,7 @@ import StackWithTranslucentHeader from './StackWithTranslucentHeader';
|
||||
import SimpleTabs from './SimpleTabs';
|
||||
import SwitchWithStacks from './SwitchWithStacks';
|
||||
import TabsWithNavigationFocus from './TabsWithNavigationFocus';
|
||||
import KeyboardHandlingExample from './KeyboardHandlingExample';
|
||||
|
||||
const ExampleInfo = {
|
||||
SimpleStack: {
|
||||
@@ -44,6 +46,11 @@ const ExampleInfo = {
|
||||
name: 'Switch between routes',
|
||||
description: 'Jump between routes',
|
||||
},
|
||||
InactiveStack: {
|
||||
name: 'Navigate idempotently to stacks in inactive routes',
|
||||
description:
|
||||
'An inactive route in a stack should be given the opportunity to handle actions',
|
||||
},
|
||||
StackWithCustomHeaderBackImage: {
|
||||
name: 'Custom header back image',
|
||||
description: 'Stack with custom header back image',
|
||||
@@ -114,6 +121,11 @@ const ExampleInfo = {
|
||||
name: 'withNavigationFocus',
|
||||
description: 'Receive the focus prop to know when a screen is focused',
|
||||
},
|
||||
KeyboardHandlingExample: {
|
||||
name: 'Keyboard Handling Example',
|
||||
description:
|
||||
'Demo automatic handling of keyboard showing/hiding inside StackNavigator',
|
||||
},
|
||||
};
|
||||
|
||||
const ExampleRoutes = {
|
||||
@@ -143,6 +155,9 @@ const ExampleRoutes = {
|
||||
path: 'settings',
|
||||
},
|
||||
TabsWithNavigationFocus,
|
||||
KeyboardHandlingExample,
|
||||
// This is commented out because it's rarely useful
|
||||
// InactiveStack,
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -307,7 +322,7 @@ const AppNavigator = createStackNavigator(
|
||||
}
|
||||
);
|
||||
|
||||
export default () => <AppNavigator persistenceKey="NavState" />;
|
||||
export default AppNavigator;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
item: {
|
||||
|
||||
@@ -32,14 +32,7 @@ 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 }}
|
||||
/>
|
||||
),
|
||||
headerTitle: 'Inbox',
|
||||
};
|
||||
|
||||
const EmailScreen = ({ navigation }) => (
|
||||
@@ -50,10 +43,7 @@ const DraftsScreen = ({ navigation }) => (
|
||||
<MyNavScreen banner={'Drafts Screen'} navigation={navigation} />
|
||||
);
|
||||
DraftsScreen.navigationOptions = {
|
||||
drawerLabel: 'Drafts',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
|
||||
),
|
||||
headerTitle: 'Drafts',
|
||||
};
|
||||
|
||||
const InboxStack = createStackNavigator({
|
||||
@@ -61,11 +51,29 @@ const InboxStack = createStackNavigator({
|
||||
Email: { screen: EmailScreen },
|
||||
});
|
||||
|
||||
InboxStack.navigationOptions = {
|
||||
drawerLabel: 'Inbox',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons
|
||||
name="move-to-inbox"
|
||||
size={24}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const DraftsStack = createStackNavigator({
|
||||
Drafts: { screen: DraftsScreen },
|
||||
Email: { screen: EmailScreen },
|
||||
});
|
||||
|
||||
DraftsStack.navigationOptions = {
|
||||
drawerLabel: 'Drafts',
|
||||
drawerIcon: ({ tintColor }) => (
|
||||
<MaterialIcons name="drafts" size={24} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
const DrawerExample = createDrawerNavigator(
|
||||
{
|
||||
Inbox: {
|
||||
|
||||
96
examples/NavigationPlayground/js/InactiveStack.js
Normal file
96
examples/NavigationPlayground/js/InactiveStack.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import React from 'react';
|
||||
import { Button, Text, StatusBar, View, StyleSheet } from 'react-native';
|
||||
import {
|
||||
SafeAreaView,
|
||||
createStackNavigator,
|
||||
createSwitchNavigator,
|
||||
NavigationActions,
|
||||
} from 'react-navigation';
|
||||
|
||||
const runSubRoutes = navigation => {
|
||||
navigation.dispatch(NavigationActions.navigate({ routeName: 'First2' }));
|
||||
navigation.dispatch(NavigationActions.navigate({ routeName: 'Second2' }));
|
||||
navigation.dispatch(NavigationActions.navigate({ routeName: 'First2' }));
|
||||
};
|
||||
|
||||
const runSubRoutesWithIntermediate = navigation => {
|
||||
navigation.dispatch(toFirst1);
|
||||
navigation.dispatch(toSecond2);
|
||||
navigation.dispatch(toFirst);
|
||||
navigation.dispatch(toFirst2);
|
||||
};
|
||||
|
||||
const runSubAction = navigation => {
|
||||
navigation.dispatch(toFirst2);
|
||||
navigation.dispatch(toSecond2);
|
||||
navigation.dispatch(toFirstChild1);
|
||||
};
|
||||
|
||||
const DummyScreen = ({ routeName, navigation, style }) => {
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={[
|
||||
StyleSheet.absoluteFill,
|
||||
{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
style,
|
||||
]}
|
||||
>
|
||||
<Text style={{ fontWeight: '800' }}>
|
||||
{routeName}({navigation.state.key})
|
||||
</Text>
|
||||
<View>
|
||||
<Button title="back" onPress={() => navigation.goBack()} />
|
||||
<Button title="dismiss" onPress={() => navigation.dismiss()} />
|
||||
<Button
|
||||
title="between sub-routes"
|
||||
onPress={() => runSubRoutes(navigation)}
|
||||
/>
|
||||
<Button
|
||||
title="between sub-routes (with intermediate)"
|
||||
onPress={() => runSubRoutesWithIntermediate(navigation)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="with sub-action"
|
||||
onPress={() => runSubAction(navigation)}
|
||||
/>
|
||||
</View>
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const createDummyScreen = routeName => {
|
||||
const BoundDummyScreen = props => DummyScreen({ ...props, routeName });
|
||||
return BoundDummyScreen;
|
||||
};
|
||||
|
||||
const toFirst = NavigationActions.navigate({ routeName: 'First' });
|
||||
const toFirst1 = NavigationActions.navigate({ routeName: 'First1' });
|
||||
const toFirst2 = NavigationActions.navigate({ routeName: 'First2' });
|
||||
const toSecond2 = NavigationActions.navigate({ routeName: 'Second2' });
|
||||
const toFirstChild1 = NavigationActions.navigate({
|
||||
routeName: 'First',
|
||||
action: NavigationActions.navigate({ routeName: 'First1' }),
|
||||
});
|
||||
|
||||
export default createStackNavigator(
|
||||
{
|
||||
Other: createDummyScreen('Leaf'),
|
||||
First: createStackNavigator({
|
||||
First1: createDummyScreen('First1'),
|
||||
First2: createDummyScreen('First2'),
|
||||
}),
|
||||
Second: createStackNavigator({
|
||||
Second1: createDummyScreen('Second1'),
|
||||
Second2: createDummyScreen('Second2'),
|
||||
}),
|
||||
},
|
||||
{
|
||||
headerMode: 'none',
|
||||
}
|
||||
);
|
||||
63
examples/NavigationPlayground/js/KeyboardHandlingExample.js
Normal file
63
examples/NavigationPlayground/js/KeyboardHandlingExample.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { StatusBar, View, TextInput, InteractionManager } from 'react-native';
|
||||
import { createStackNavigator, withNavigationFocus } from 'react-navigation';
|
||||
import { Button } from './commonComponents/ButtonWithMargin';
|
||||
|
||||
class ScreenOne extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Home',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<View style={{ paddingTop: 30 }}>
|
||||
<Button
|
||||
onPress={() => navigation.push('ScreenTwo')}
|
||||
title="Push screen with focused text input"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go Home" />
|
||||
<StatusBar barStyle="default" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenTwo extends React.Component {
|
||||
static navigationOptions = ({ navigation }) => ({
|
||||
title: navigation.getParam('inputValue', 'Screen w/ Input'),
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this._textInput.focus();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<View style={{ paddingTop: 30 }}>
|
||||
<View style={{ alignSelf: 'center', paddingVertical: 20 }}>
|
||||
<TextInput
|
||||
ref={c => (this._textInput = c)}
|
||||
onChangeText={inputValue => navigation.setParams({ inputValue })}
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
height: 24,
|
||||
width: 150,
|
||||
borderColor: '#555',
|
||||
borderWidth: 1,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<Button onPress={() => navigation.pop()} title="Pop" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default createStackNavigator({
|
||||
ScreenOne,
|
||||
ScreenTwo: withNavigationFocus(ScreenTwo),
|
||||
});
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
import type {
|
||||
NavigationScreenProp,
|
||||
NavigationState,
|
||||
NavigationStateRoute,
|
||||
NavigationEventSubscription,
|
||||
} from 'react-navigation';
|
||||
|
||||
@@ -19,11 +21,15 @@ import { Button } from './commonComponents/ButtonWithMargin';
|
||||
import { HeaderButtons } from './commonComponents/HeaderButtons';
|
||||
|
||||
type MyNavScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
navigation: NavigationScreenProp<NavigationState>,
|
||||
banner: React.Node,
|
||||
};
|
||||
|
||||
class MyBackButton extends React.Component<any, any> {
|
||||
type BackButtonProps = {
|
||||
navigation: NavigationScreenProp<NavigationStateRoute>,
|
||||
};
|
||||
|
||||
class MyBackButton extends React.Component<BackButtonProps, any> {
|
||||
render() {
|
||||
return (
|
||||
<HeaderButtons>
|
||||
@@ -68,7 +74,7 @@ class MyNavScreen extends React.Component<MyNavScreenProps> {
|
||||
}
|
||||
|
||||
type MyHomeScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
navigation: NavigationScreenProp<NavigationState>,
|
||||
};
|
||||
|
||||
class MyHomeScreen extends React.Component<MyHomeScreenProps> {
|
||||
@@ -112,7 +118,7 @@ class MyHomeScreen extends React.Component<MyHomeScreenProps> {
|
||||
}
|
||||
|
||||
type MyPhotosScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
navigation: NavigationScreenProp<NavigationState>,
|
||||
};
|
||||
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
static navigationOptions = {
|
||||
@@ -153,7 +159,7 @@ class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params.name}'s Photos`}
|
||||
banner={`${navigation.getParam('name')}'s Photos`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
@@ -162,9 +168,9 @@ class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
|
||||
const MyProfileScreen = ({ navigation }) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params.mode === 'edit' ? 'Now Editing ' : ''}${
|
||||
navigation.state.params.name
|
||||
}'s Profile`}
|
||||
banner={`${
|
||||
navigation.getParam('mode') === 'edit' ? 'Now Editing ' : ''
|
||||
}${navigation.getParam('name')}'s Profile`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import { SafeAreaView, StatusBar, Text, View } from 'react-native';
|
||||
import {
|
||||
createBottomTabNavigator,
|
||||
withNavigationFocus,
|
||||
} from 'react-navigation';
|
||||
import { withNavigationFocus } from 'react-navigation';
|
||||
import { createMaterialBottomTabNavigator } from 'react-navigation-material-bottom-tabs';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import { Button } from './commonComponents/ButtonWithMargin';
|
||||
|
||||
@@ -80,16 +78,26 @@ const createTabScreen = (name, icon, focusedIcon, tintColor = '#673ab7') => {
|
||||
return withNavigationFocus(TabScreen);
|
||||
};
|
||||
|
||||
const TabsWithNavigationFocus = createBottomTabNavigator({
|
||||
One: {
|
||||
screen: createTabScreen('One', 'numeric-1-box-outline', 'numeric-1-box'),
|
||||
const TabsWithNavigationFocus = createMaterialBottomTabNavigator(
|
||||
{
|
||||
One: {
|
||||
screen: createTabScreen('One', 'numeric-1-box-outline', 'numeric-1-box'),
|
||||
},
|
||||
Two: {
|
||||
screen: createTabScreen('Two', 'numeric-2-box-outline', 'numeric-2-box'),
|
||||
},
|
||||
Three: {
|
||||
screen: createTabScreen(
|
||||
'Three',
|
||||
'numeric-3-box-outline',
|
||||
'numeric-3-box'
|
||||
),
|
||||
},
|
||||
},
|
||||
Two: {
|
||||
screen: createTabScreen('Two', 'numeric-2-box-outline', 'numeric-2-box'),
|
||||
},
|
||||
Three: {
|
||||
screen: createTabScreen('Three', 'numeric-3-box-outline', 'numeric-3-box'),
|
||||
},
|
||||
});
|
||||
{
|
||||
shifting: false,
|
||||
activeTintColor: '#F44336',
|
||||
}
|
||||
);
|
||||
|
||||
export default TabsWithNavigationFocus;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"react-native": "^0.54.0",
|
||||
"react-native-iphone-x-helper": "^1.0.2",
|
||||
"react-navigation": "link:../..",
|
||||
"react-navigation-header-buttons": "^0.0.4"
|
||||
"react-navigation-header-buttons": "^0.0.4",
|
||||
"react-navigation-material-bottom-tabs": "0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "^22.4.1",
|
||||
|
||||
@@ -2110,6 +2110,13 @@ create-react-context@^0.2.1:
|
||||
fbjs "^0.8.0"
|
||||
gud "^1.0.0"
|
||||
|
||||
create-react-context@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca"
|
||||
dependencies:
|
||||
fbjs "^0.8.0"
|
||||
gud "^1.0.0"
|
||||
|
||||
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||
@@ -2220,7 +2227,7 @@ deepmerge@^1.3.0, deepmerge@^1.5.1:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
|
||||
|
||||
deepmerge@^2.0.1:
|
||||
deepmerge@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102"
|
||||
|
||||
@@ -5550,6 +5557,10 @@ react-lifecycles-compat@^1.0.2:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-1.1.4.tgz#fc005c72849b7ed364de20a0f64ff58ebdc2009a"
|
||||
|
||||
react-lifecycles-compat@^3, react-lifecycles-compat@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.2.tgz#7279047275bd727a912e25f734c0559527e84eff"
|
||||
|
||||
react-native-branch@2.0.0-beta.3:
|
||||
version "2.0.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-branch/-/react-native-branch-2.0.0-beta.3.tgz#2167af86bbc9f964bd45bd5f37684e5b54965e32"
|
||||
@@ -5589,14 +5600,15 @@ react-native-iphone-x-helper@^1.0.2:
|
||||
babel-plugin-module-resolver "^2.3.0"
|
||||
babel-preset-react-native "1.9.0"
|
||||
|
||||
react-native-paper@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-paper/-/react-native-paper-1.2.5.tgz#3a4480ca967aae4b3e65a987116973dd012d1fa8"
|
||||
react-native-paper@=1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-paper/-/react-native-paper-1.4.0.tgz#33bd477cde1468b63e60bdb0c9a500822d03cdca"
|
||||
dependencies:
|
||||
color "^2.0.1"
|
||||
create-react-context "^0.2.1"
|
||||
deepmerge "^2.0.1"
|
||||
create-react-context "^0.2.2"
|
||||
deepmerge "^2.1.0"
|
||||
hoist-non-react-statics "^2.5.0"
|
||||
react-lifecycles-compat "^3.0.2"
|
||||
|
||||
react-native-platform-touchable@^1.1.1:
|
||||
version "1.1.1"
|
||||
@@ -5642,9 +5654,9 @@ react-native-svg@6.2.2:
|
||||
lodash "^4.16.6"
|
||||
pegjs "^0.10.0"
|
||||
|
||||
"react-native-tab-view@github:react-navigation/react-native-tab-view":
|
||||
react-native-tab-view@^0.0.74:
|
||||
version "0.0.74"
|
||||
resolved "https://codeload.github.com/react-navigation/react-native-tab-view/tar.gz/36ebd834d78b841fc19778c966465d02fd1213bb"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.74.tgz#62c0c882d9232b461ce181d440d683b4f99d1bd8"
|
||||
dependencies:
|
||||
prop-types "^15.6.0"
|
||||
|
||||
@@ -5725,11 +5737,11 @@ react-native@^0.54.0:
|
||||
xmldoc "^0.4.0"
|
||||
yargs "^9.0.0"
|
||||
|
||||
react-navigation-deprecated-tab-navigator@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-deprecated-tab-navigator/-/react-navigation-deprecated-tab-navigator-1.0.1.tgz#a869d749d16116630411d6c7761c51484ba90175"
|
||||
react-navigation-deprecated-tab-navigator@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-deprecated-tab-navigator/-/react-navigation-deprecated-tab-navigator-1.1.0.tgz#58945c1c4c7c21b54954e814e8721e98423df75d"
|
||||
dependencies:
|
||||
react-native-tab-view react-navigation/react-native-tab-view
|
||||
react-native-tab-view "^0.0.74"
|
||||
|
||||
react-navigation-header-buttons@^0.0.4:
|
||||
version "0.0.4"
|
||||
@@ -5737,14 +5749,22 @@ react-navigation-header-buttons@^0.0.4:
|
||||
dependencies:
|
||||
react-native-platform-touchable "^1.1.1"
|
||||
|
||||
react-navigation-tabs@0.1.0-alpha.5:
|
||||
version "0.1.0-alpha.5"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-0.1.0-alpha.5.tgz#51cae3ea5f0f77bdf0deb50a855a5dcaa2f9bf1e"
|
||||
react-navigation-material-bottom-tabs@0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-material-bottom-tabs/-/react-navigation-material-bottom-tabs-0.1.3.tgz#8e11c3d57ebb72d19adf5e4764390729ed964072"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.5.0"
|
||||
prop-types "^15.6.0"
|
||||
react-native-paper "=1.4.0"
|
||||
react-navigation-tabs "0.2.0-rc.0"
|
||||
|
||||
react-navigation-tabs@0.2.0-rc.0:
|
||||
version "0.2.0-rc.0"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-0.2.0-rc.0.tgz#671e8c9914b915796879329752f7240147494038"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.5.0"
|
||||
prop-types "^15.6.0"
|
||||
react-lifecycles-compat "^1.0.2"
|
||||
react-native-paper "^1.2.5"
|
||||
react-native-safe-area-view "^0.7.0"
|
||||
react-native-tab-view "~0.0.77"
|
||||
|
||||
|
||||
42
flow/react-navigation.js
vendored
42
flow/react-navigation.js
vendored
@@ -488,17 +488,19 @@ declare module 'react-navigation' {
|
||||
goBack: (routeKey?: ?string) => boolean,
|
||||
dismiss: () => boolean,
|
||||
navigate: (
|
||||
routeName: | string
|
||||
| {
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
action?: NavigationNavigateAction,
|
||||
key?: string,
|
||||
},
|
||||
routeName:
|
||||
| string
|
||||
| {
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
action?: NavigationNavigateAction,
|
||||
key?: string,
|
||||
},
|
||||
params?: NavigationParams,
|
||||
action?: NavigationNavigateAction
|
||||
) => boolean,
|
||||
setParams: (newParams: NavigationParams) => boolean,
|
||||
getParam: (paramName: string, fallback?: any) => any,
|
||||
addListener: (
|
||||
eventName: string,
|
||||
callback: NavigationEventCallback
|
||||
@@ -807,10 +809,6 @@ declare module 'react-navigation' {
|
||||
routeConfigs: NavigationRouteConfigMap,
|
||||
config?: _TabNavigatorConfig
|
||||
): NavigationContainer<*, *, *>;
|
||||
declare export function createMaterialBottomTabNavigator(
|
||||
routeConfigs: NavigationRouteConfigMap,
|
||||
config?: _TabNavigatorConfig
|
||||
): NavigationContainer<*, *, *>;
|
||||
declare export function createMaterialTopTabNavigator(
|
||||
routeConfigs: NavigationRouteConfigMap,
|
||||
config?: _TabNavigatorConfig
|
||||
@@ -1086,13 +1084,17 @@ declare module 'react-navigation' {
|
||||
};
|
||||
declare export var TabBarBottom: React$ComponentType<_TabBarBottomProps>;
|
||||
|
||||
declare type _NavigationInjectedProps = {
|
||||
navigation: NavigationScreenProp<NavigationStateRoute>,
|
||||
};
|
||||
declare export function withNavigation<T: {}>(
|
||||
Component: React$ComponentType<T & _NavigationInjectedProps>
|
||||
): React$ComponentType<T>;
|
||||
declare export function withNavigationFocus<T: {}>(
|
||||
Component: React$ComponentType<T & _NavigationInjectedProps>
|
||||
): React$ComponentType<T>;
|
||||
declare export function withNavigation<Props: {}>(
|
||||
Component: React$ComponentType<Props>
|
||||
): React$ComponentType<
|
||||
$Diff<
|
||||
Props,
|
||||
{
|
||||
navigation: NavigationScreenProp<NavigationStateRoute> | void,
|
||||
}
|
||||
>
|
||||
>;
|
||||
declare export function withNavigationFocus<Props: {}>(
|
||||
Component: React$ComponentType<Props>
|
||||
): React$ComponentType<$Diff<Props, { isFocused: boolean | void }>>;
|
||||
}
|
||||
|
||||
23
package.json
23
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation",
|
||||
"version": "2.0.0-rc.1",
|
||||
"version": "2.0.0",
|
||||
"description": "Routing and navigation for your React Native apps",
|
||||
"main": "src/react-navigation.js",
|
||||
"repository": {
|
||||
@@ -34,11 +34,11 @@
|
||||
"hoist-non-react-statics": "^2.2.0",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-lifecycles-compat": "^1.0.2",
|
||||
"react-lifecycles-compat": "^3",
|
||||
"react-native-drawer-layout-polyfill": "^1.3.2",
|
||||
"react-native-safe-area-view": "^0.7.0",
|
||||
"react-navigation-deprecated-tab-navigator": "1.0.1",
|
||||
"react-navigation-tabs": "0.1.0-alpha.6"
|
||||
"react-navigation-deprecated-tab-navigator": "1.2.0",
|
||||
"react-navigation-tabs": "0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
@@ -48,17 +48,17 @@
|
||||
"babel-preset-react-native": "^2.1.0",
|
||||
"codecov": "^2.2.0",
|
||||
"eslint": "^4.2.0",
|
||||
"eslint-config-prettier": "^2.3.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.2",
|
||||
"eslint-plugin-prettier": "^2.1.2",
|
||||
"eslint-plugin-prettier": "^2.6.0",
|
||||
"eslint-plugin-react": "^7.1.0",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^22.1.3",
|
||||
"jest-expo": "^25.1.0",
|
||||
"lint-staged": "^4.2.1",
|
||||
"prettier": "^1.5.3",
|
||||
"prettier-eslint": "^6.4.2",
|
||||
"prettier": "^1.12.1",
|
||||
"prettier-eslint": "^8.8.1",
|
||||
"react": "16.2.0",
|
||||
"react-native": "^0.52.0",
|
||||
"react-native-vector-icons": "^4.2.0",
|
||||
@@ -67,7 +67,7 @@
|
||||
"jest": {
|
||||
"notify": true,
|
||||
"preset": "react-native",
|
||||
"testRegex": "./src/.*\\-test\\.js$",
|
||||
"testRegex": "/__tests__/[^/]+-test\\.js$",
|
||||
"setupFiles": [
|
||||
"<rootDir>/jest-setup.js"
|
||||
],
|
||||
@@ -82,8 +82,11 @@
|
||||
"coveragePathIgnorePatterns": [
|
||||
"jest-setup.js"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.png$": "<rootDir>/assetsTransformer.js"
|
||||
},
|
||||
"modulePathIgnorePatterns": [
|
||||
"examples"
|
||||
"<rootDir>/examples/"
|
||||
],
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|react-navigation-deprecated-tab-navigator)"
|
||||
|
||||
@@ -21,7 +21,7 @@ const StateUtils = {
|
||||
* routes of the navigation state, or -1 if it is not present.
|
||||
*/
|
||||
indexOf(state, key) {
|
||||
return state.routes.map(route => route.key).indexOf(key);
|
||||
return state.routes.findIndex(route => route.key === key);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -116,8 +116,23 @@ const StateUtils = {
|
||||
|
||||
/**
|
||||
* Replace a route by a key.
|
||||
* Note that this moves the index to the positon to where the new route in the
|
||||
* stack is at.
|
||||
* Note that this moves the index to the position to where the new route in the
|
||||
* stack is at and updates the routes array accordingly.
|
||||
*/
|
||||
replaceAndPrune(state, key, route) {
|
||||
const index = StateUtils.indexOf(state, key);
|
||||
const replaced = StateUtils.replaceAtIndex(state, index, route);
|
||||
|
||||
return {
|
||||
...replaced,
|
||||
routes: replaced.routes.slice(0, index + 1),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace a route by a key.
|
||||
* Note that this moves the index to the position to where the new route in the
|
||||
* stack is at. Does not prune the routes.
|
||||
*/
|
||||
replaceAt(state, key, route) {
|
||||
const index = StateUtils.indexOf(state, key);
|
||||
@@ -137,7 +152,7 @@ const StateUtils = {
|
||||
route.key
|
||||
);
|
||||
|
||||
if (state.routes[index] === route) {
|
||||
if (state.routes[index] === route && index === state.index) {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -153,7 +168,7 @@ const StateUtils = {
|
||||
|
||||
/**
|
||||
* Resets all routes.
|
||||
* Note that this moves the index to the positon to where the last route in the
|
||||
* Note that this moves the index to the position to where the last route in the
|
||||
* stack is at if the param `index` isn't provided.
|
||||
*/
|
||||
reset(state, routes, index) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import 'react-native';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
@@ -202,7 +202,7 @@ describe('StateUtils', () => {
|
||||
).toEqual(newState);
|
||||
});
|
||||
|
||||
it('Returns the state if index matches the route', () => {
|
||||
it('Returns the state with updated index if route is unchanged but index changes', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
@@ -210,7 +210,7 @@ describe('StateUtils', () => {
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAtIndex(state, 1, state.routes[1])
|
||||
).toEqual(state);
|
||||
).toEqual({ ...state, index: 1 });
|
||||
});
|
||||
|
||||
// Reset
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Linking, AsyncStorage } from 'react-native';
|
||||
import withLifecyclePolyfill from 'react-lifecycles-compat';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
|
||||
import { BackHandler } from './PlatformHelpers';
|
||||
import NavigationActions from './NavigationActions';
|
||||
import invariant from './utils/invariant';
|
||||
import getNavigationActionCreators from './routers/getNavigationActionCreators';
|
||||
import docsUrl from './utils/docsUrl';
|
||||
|
||||
function isStateful(props) {
|
||||
@@ -208,10 +209,25 @@ export default function createNavigationContainer(Component) {
|
||||
_statefulContainerCount++;
|
||||
Linking.addEventListener('url', this._handleOpenURL);
|
||||
|
||||
// Pull out anything that can impact state
|
||||
const { persistenceKey } = this.props;
|
||||
const startupStateJSON =
|
||||
persistenceKey && (await AsyncStorage.getItem(persistenceKey));
|
||||
const url = await Linking.getInitialURL();
|
||||
const parsedUrl = url && this._urlToPathAndParams(url);
|
||||
|
||||
// Initialize state. This must be done *after* any async code
|
||||
// so we don't end up with a different value for this.state.nav
|
||||
// due to changes while async function was resolving
|
||||
let action = this._initialAction;
|
||||
let startupState = this.state.nav;
|
||||
if (!startupState) {
|
||||
!!process.env.REACT_NAV_LOGGING &&
|
||||
console.log('Init new Navigation State');
|
||||
startupState = Component.router.getStateForAction(action);
|
||||
}
|
||||
|
||||
// Pull persisted state from AsyncStorage
|
||||
if (startupStateJSON) {
|
||||
try {
|
||||
startupState = JSON.parse(startupStateJSON);
|
||||
@@ -219,15 +235,7 @@ export default function createNavigationContainer(Component) {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
let action = this._initialAction;
|
||||
if (!startupState) {
|
||||
!!process.env.REACT_NAV_LOGGING &&
|
||||
console.log('Init new Navigation State');
|
||||
startupState = Component.router.getStateForAction(action);
|
||||
}
|
||||
|
||||
const url = await Linking.getInitialURL();
|
||||
const parsedUrl = url && this._urlToPathAndParams(url);
|
||||
// Pull state out of URL
|
||||
if (parsedUrl) {
|
||||
const { path, params } = parsedUrl;
|
||||
const urlAction = Component.router.getActionForPathAndParams(
|
||||
@@ -244,11 +252,8 @@ export default function createNavigationContainer(Component) {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (startupState === this.state.nav) {
|
||||
return;
|
||||
}
|
||||
this.setState({ nav: startupState }, () => {
|
||||
_reactNavigationIsHydratingState = false;
|
||||
|
||||
const dispatchActions = () =>
|
||||
this._actionEventSubscribers.forEach(subscriber =>
|
||||
subscriber({
|
||||
type: 'action',
|
||||
@@ -257,6 +262,15 @@ export default function createNavigationContainer(Component) {
|
||||
lastState: null,
|
||||
})
|
||||
);
|
||||
|
||||
if (startupState === this.state.nav) {
|
||||
dispatchActions();
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ nav: startupState }, () => {
|
||||
_reactNavigationIsHydratingState = false;
|
||||
dispatchActions();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -346,6 +360,11 @@ export default function createNavigationContainer(Component) {
|
||||
};
|
||||
},
|
||||
};
|
||||
const actionCreators = getNavigationActionCreators(nav);
|
||||
Object.keys(actionCreators).forEach(actionName => {
|
||||
this._navigation[actionName] = (...args) =>
|
||||
this.dispatch(actionCreators[actionName](...args));
|
||||
});
|
||||
}
|
||||
navigation = this._navigation;
|
||||
}
|
||||
@@ -354,5 +373,5 @@ export default function createNavigationContainer(Component) {
|
||||
}
|
||||
}
|
||||
|
||||
return withLifecyclePolyfill(NavigationContainer);
|
||||
return polyfill(NavigationContainer);
|
||||
}
|
||||
|
||||
@@ -138,11 +138,14 @@ export default function getChildEventSubscriber(addListener, key) {
|
||||
emit((lastEmittedEvent = 'didBlur'), childPayload);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastEmittedEvent === 'didBlur' && !newRoute) {
|
||||
removeAll();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
removeAll,
|
||||
addListener(eventName, eventHandler) {
|
||||
const subscribers = getChildSubscribers(eventName);
|
||||
if (!subscribers) {
|
||||
|
||||
@@ -77,7 +77,6 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
</View>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"transform": Array [
|
||||
@@ -278,7 +277,6 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
</View>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"transform": Array [
|
||||
|
||||
@@ -143,7 +143,7 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
},
|
||||
false,
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
"flex": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -156,6 +156,7 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
"alignSelf": "center",
|
||||
"height": "100%",
|
||||
"justifyContent": "center",
|
||||
"minWidth": 30,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
@@ -170,6 +171,7 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
"alignSelf": "center",
|
||||
"height": "100%",
|
||||
"justifyContent": "center",
|
||||
"minWidth": 30,
|
||||
"opacity": 0,
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
|
||||
55
src/navigators/createKeyboardAwareNavigator.js
Normal file
55
src/navigators/createKeyboardAwareNavigator.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { TextInput } from 'react-native';
|
||||
|
||||
export default Navigator =>
|
||||
class KeyboardAwareNavigator extends React.Component {
|
||||
static router = Navigator.router;
|
||||
_previouslyFocusedTextInput = null;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Navigator
|
||||
{...this.props}
|
||||
onGestureBegin={this._handleGestureBegin}
|
||||
onGestureCanceled={this._handleGestureCanceled}
|
||||
onGestureFinish={this._handleGestureFinish}
|
||||
onTransitionStart={this._handleTransitionStart}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_handleGestureBegin = () => {
|
||||
this._previouslyFocusedTextInput = TextInput.State.currentlyFocusedField();
|
||||
if (this._previouslyFocusedTextInput) {
|
||||
TextInput.State.blurTextInput(this._previouslyFocusedTextInput);
|
||||
}
|
||||
this.props.onGestureBegin && this.props.onGestureBegin();
|
||||
};
|
||||
|
||||
_handleGestureCanceled = () => {
|
||||
if (this._previouslyFocusedTextInput) {
|
||||
TextInput.State.focusTextInput(this._previouslyFocusedTextInput);
|
||||
}
|
||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||
};
|
||||
|
||||
_handleGestureFinish = () => {
|
||||
this._previouslyFocusedTextInput = null;
|
||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||
};
|
||||
|
||||
_handleTransitionStart = (transitionProps, prevTransitionProps) => {
|
||||
// TODO: We should not even have received the transition start event
|
||||
// in the case where the index did not change, I believe. We
|
||||
// should revisit this after 2.0 release.
|
||||
if (transitionProps.index !== prevTransitionProps.index) {
|
||||
const currentField = TextInput.State.currentlyFocusedField();
|
||||
if (currentField) {
|
||||
TextInput.State.blurTextInput(currentField);
|
||||
}
|
||||
}
|
||||
|
||||
this.props.onTransitionStart &&
|
||||
this.props.onTransitionStart(transitionProps, prevTransitionProps);
|
||||
};
|
||||
};
|
||||
@@ -14,18 +14,17 @@ function createNavigator(NavigatorView, router, navigationConfig) {
|
||||
const activeKeys = this.props.navigation.state.routes.map(r => r.key);
|
||||
Object.keys(this.childEventSubscribers).forEach(key => {
|
||||
if (!activeKeys.includes(key)) {
|
||||
this.childEventSubscribers[key].removeAll();
|
||||
delete this.childEventSubscribers[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove all subscriptions
|
||||
// Remove all subscription references
|
||||
componentWillUnmount() {
|
||||
Object.values(this.childEventSubscribers).map(s => s.removeAll());
|
||||
this.childEventSubscribers = {};
|
||||
}
|
||||
|
||||
_isRouteFocused = route => () => {
|
||||
_isRouteFocused = route => {
|
||||
const { state } = this.props.navigation;
|
||||
const focusedRoute = state.routes[state.index];
|
||||
return route === focusedRoute;
|
||||
@@ -95,6 +94,7 @@ function createNavigator(NavigatorView, router, navigationConfig) {
|
||||
|
||||
return (
|
||||
<NavigatorView
|
||||
{...this.props}
|
||||
screenProps={screenProps}
|
||||
navigation={navigation}
|
||||
navigationConfig={navigationConfig}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import createNavigationContainer from '../createNavigationContainer';
|
||||
import createKeyboardAwareNavigator from './createKeyboardAwareNavigator';
|
||||
import createNavigator from './createNavigator';
|
||||
import StackView from '../views/StackView/StackView';
|
||||
import StackRouter from '../routers/StackRouter';
|
||||
@@ -11,6 +12,7 @@ function createStackNavigator(routeConfigMap, stackConfig = {}) {
|
||||
initialRouteParams,
|
||||
paths,
|
||||
navigationOptions,
|
||||
disableKeyboardHandling,
|
||||
} = stackConfig;
|
||||
|
||||
const stackRouterConfig = {
|
||||
@@ -24,7 +26,10 @@ function createStackNavigator(routeConfigMap, stackConfig = {}) {
|
||||
const router = StackRouter(routeConfigMap, stackRouterConfig);
|
||||
|
||||
// Create a navigator with StackView as the view
|
||||
const Navigator = createNavigator(StackView, router, stackConfig);
|
||||
let Navigator = createNavigator(StackView, router, stackConfig);
|
||||
if (!disableKeyboardHandling) {
|
||||
Navigator = createKeyboardAwareNavigator(Navigator);
|
||||
}
|
||||
|
||||
// HOC to provide the navigation prop for the top-level navigator (when the prop is missing)
|
||||
return createNavigationContainer(Navigator);
|
||||
|
||||
10
src/react-navigation.js
vendored
10
src/react-navigation.js
vendored
@@ -42,23 +42,21 @@ module.exports = {
|
||||
},
|
||||
get createTabNavigator() {
|
||||
console.warn(
|
||||
'TabNavigator is deprecated. Please use the createBottomTabNavigator or createMaterialTopNavigator instead.'
|
||||
'createTabNavigator is deprecated. Please use the createBottomTabNavigator or createMaterialTopTabNavigator instead.'
|
||||
);
|
||||
return require('react-navigation-deprecated-tab-navigator')
|
||||
.createTabNavigator;
|
||||
},
|
||||
get TabNavigator() {
|
||||
console.warn(
|
||||
'TabNavigator is deprecated. Please use the createBottomTabNavigator or createMaterialTopNavigator instead.'
|
||||
'TabNavigator is deprecated. Please use the createBottomTabNavigator or createMaterialTopTabNavigator instead.'
|
||||
);
|
||||
return require('react-navigation-deprecated-tab-navigator').default;
|
||||
return require('react-navigation-deprecated-tab-navigator')
|
||||
.createTabNavigator;
|
||||
},
|
||||
get createBottomTabNavigator() {
|
||||
return require('react-navigation-tabs').createBottomTabNavigator;
|
||||
},
|
||||
get createMaterialBottomTabNavigator() {
|
||||
return require('react-navigation-tabs').createMaterialBottomTabNavigator;
|
||||
},
|
||||
get createMaterialTopTabNavigator() {
|
||||
return require('react-navigation-tabs').createMaterialTopTabNavigator;
|
||||
},
|
||||
|
||||
@@ -33,44 +33,52 @@ export default (routeConfigs, config = {}) => {
|
||||
|
||||
const isRouterTargeted = action.key == null || action.key === state.key;
|
||||
|
||||
if (
|
||||
isRouterTargeted &&
|
||||
action.type === DrawerActions.CLOSE_DRAWER &&
|
||||
state.isDrawerOpen
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
if (isRouterTargeted) {
|
||||
// Only handle actions that are meant for this drawer, as specified by action.key.
|
||||
|
||||
if (action.type === DrawerActions.CLOSE_DRAWER && state.isDrawerOpen) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.OPEN_DRAWER && !state.isDrawerOpen) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === DrawerActions.TOGGLE_DRAWER) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: !state.isDrawerOpen,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isRouterTargeted &&
|
||||
action.type === DrawerActions.OPEN_DRAWER &&
|
||||
!state.isDrawerOpen
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: true,
|
||||
};
|
||||
// Fall back on switch router for screen switching logic, and handling of child routers
|
||||
const switchedState = switchRouter.getStateForAction(action, state);
|
||||
|
||||
if (switchedState === null) {
|
||||
// The switch router or a child router is attempting to swallow this action. We return null to allow this.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isRouterTargeted && action.type === DrawerActions.TOGGLE_DRAWER) {
|
||||
return {
|
||||
...state,
|
||||
isDrawerOpen: !state.isDrawerOpen,
|
||||
};
|
||||
if (switchedState !== state) {
|
||||
if (switchedState.index !== state.index) {
|
||||
// If the tabs have changed, make sure to close the drawer
|
||||
return {
|
||||
...switchedState,
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
}
|
||||
// Return the state new state, as returned by the switch router.
|
||||
// The index hasn't changed, so this most likely means that a child router has returned a new state
|
||||
return switchedState;
|
||||
}
|
||||
|
||||
// Fall back on tab router for screen switching logic
|
||||
const childState = switchRouter.getStateForAction(action, state);
|
||||
if (childState !== null && childState !== state) {
|
||||
// If the tabs have changed, make sure to close the drawer
|
||||
return {
|
||||
...childState,
|
||||
isDrawerOpen: false,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,6 +27,10 @@ function behavesLikePushAction(action) {
|
||||
|
||||
const defaultActionCreators = (route, navStateKey) => ({});
|
||||
|
||||
function isResetToRootStack(action) {
|
||||
return action.type === StackActions.RESET && action.key === null;
|
||||
}
|
||||
|
||||
export default (routeConfigs, stackConfig = {}) => {
|
||||
// Fail fast on invalid route definitions
|
||||
validateRouteConfigMap(routeConfigs);
|
||||
@@ -160,7 +164,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
|
||||
getActionCreators(route, navStateKey) {
|
||||
return {
|
||||
...getNavigationActionCreators(route, navStateKey),
|
||||
...getNavigationActionCreators(route),
|
||||
...getCustomActionCreators(route, navStateKey),
|
||||
pop: (n, params) =>
|
||||
StackActions.pop({
|
||||
@@ -223,7 +227,10 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
|
||||
// Check if the focused child scene wants to handle the action, as long as
|
||||
// it is not a reset to the root stack
|
||||
if (action.type !== StackActions.RESET || action.key !== null) {
|
||||
if (
|
||||
!isResetToRootStack(action) &&
|
||||
action.type !== NavigationActions.NAVIGATE
|
||||
) {
|
||||
const keyIndex = action.key
|
||||
? StateUtils.indexOf(state, action.key)
|
||||
: -1;
|
||||
@@ -243,6 +250,31 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
return StateUtils.replaceAt(state, childRoute.key, route);
|
||||
}
|
||||
}
|
||||
} else if (action.type === NavigationActions.NAVIGATE) {
|
||||
// Traverse routes from the top of the stack to the bottom, so the
|
||||
// active route has the first opportunity, then the one before it, etc.
|
||||
for (let childRoute of state.routes.slice().reverse()) {
|
||||
let childRouter = childRouters[childRoute.routeName];
|
||||
let childAction =
|
||||
action.routeName === childRoute.routeName && action.action
|
||||
? action.action
|
||||
: action;
|
||||
|
||||
if (childRouter) {
|
||||
const nextRouteState = childRouter.getStateForAction(
|
||||
childAction,
|
||||
childRoute
|
||||
);
|
||||
|
||||
if (nextRouteState === null || nextRouteState !== childRoute) {
|
||||
return StateUtils.replaceAndPrune(
|
||||
state,
|
||||
nextRouteState ? nextRouteState.key : childRoute.key,
|
||||
nextRouteState ? nextRouteState : childRoute
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle explicit push navigation action. This must happen after the
|
||||
@@ -295,7 +327,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
isTransitioning:
|
||||
state.index !== lastRouteIndex
|
||||
? action.immediate !== true
|
||||
: undefined,
|
||||
: state.isTransitioning,
|
||||
index: lastRouteIndex,
|
||||
routes,
|
||||
};
|
||||
|
||||
@@ -108,7 +108,7 @@ export default (routeConfigs, config = {}) => {
|
||||
|
||||
getActionCreators(route, stateKey) {
|
||||
return {
|
||||
...getNavigationActionCreators(route, stateKey),
|
||||
...getNavigationActionCreators(route),
|
||||
...getCustomActionCreators(route, stateKey),
|
||||
};
|
||||
},
|
||||
|
||||
@@ -91,3 +91,49 @@ describe('DrawerRouter', () => {
|
||||
expect(state3.isDrawerOpen).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Nested routers bubble up blocked actions', () => {
|
||||
const ScreenA = () => <div />;
|
||||
ScreenA.router = {
|
||||
getStateForAction(action, lastState) {
|
||||
if (action.type === 'CHILD_ACTION') return null;
|
||||
return lastState;
|
||||
},
|
||||
};
|
||||
const ScreenB = () => <div />;
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
Bar: { screen: ScreenB },
|
||||
});
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
|
||||
const state2 = router.getStateForAction({ type: 'CHILD_ACTION' }, state);
|
||||
expect(state2).toEqual(null);
|
||||
});
|
||||
|
||||
test('Drawer stays open when child routers return new state', () => {
|
||||
const ScreenA = () => <div />;
|
||||
ScreenA.router = {
|
||||
getStateForAction(action, lastState = { changed: false }) {
|
||||
if (action.type === 'CHILD_ACTION')
|
||||
return { ...lastState, changed: true };
|
||||
return lastState;
|
||||
},
|
||||
};
|
||||
const router = DrawerRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
});
|
||||
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
expect(state.isDrawerOpen).toEqual(false);
|
||||
|
||||
const state2 = router.getStateForAction(
|
||||
{ type: DrawerActions.OPEN_DRAWER, key: state.key },
|
||||
state
|
||||
);
|
||||
expect(state2.isDrawerOpen).toEqual(true);
|
||||
|
||||
const state3 = router.getStateForAction({ type: 'CHILD_ACTION' }, state2);
|
||||
expect(state3.isDrawerOpen).toEqual(true);
|
||||
expect(state3.routes[0].changed).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/* eslint react/no-multi-comp:0 */
|
||||
/* eslint react/no-multi-comp:0, react/display-name:0 */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import StackRouter from '../StackRouter';
|
||||
import TabRouter from '../TabRouter';
|
||||
import SwitchRouter from '../SwitchRouter';
|
||||
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
|
||||
@@ -89,6 +90,75 @@ Object.keys(ROUTERS).forEach(routerName => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Nested navigate behavior test', () => {
|
||||
const Leaf = () => <div />;
|
||||
|
||||
const First = () => <div />;
|
||||
First.router = StackRouter({
|
||||
First1: Leaf,
|
||||
First2: Leaf,
|
||||
});
|
||||
|
||||
const Second = () => <div />;
|
||||
Second.router = StackRouter({
|
||||
Second1: Leaf,
|
||||
Second2: Leaf,
|
||||
});
|
||||
|
||||
const Main = () => <div />;
|
||||
Main.router = StackRouter({
|
||||
First,
|
||||
Second,
|
||||
});
|
||||
const TestRouter = SwitchRouter({
|
||||
Login: Leaf,
|
||||
Main,
|
||||
});
|
||||
|
||||
const state1 = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
|
||||
const state2 = TestRouter.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'First' },
|
||||
state1
|
||||
);
|
||||
expect(state2.index).toEqual(1);
|
||||
expect(state2.routes[1].index).toEqual(0);
|
||||
expect(state2.routes[1].routes[0].index).toEqual(0);
|
||||
|
||||
const state3 = TestRouter.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Second2' },
|
||||
state2
|
||||
);
|
||||
expect(state3.index).toEqual(1);
|
||||
expect(state3.routes[1].index).toEqual(1); // second
|
||||
expect(state3.routes[1].routes[1].index).toEqual(1); //second.second2
|
||||
|
||||
const state4 = TestRouter.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'First',
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'First2' },
|
||||
},
|
||||
state3,
|
||||
true
|
||||
);
|
||||
expect(state4.index).toEqual(1); // main
|
||||
expect(state4.routes[1].index).toEqual(0); // first
|
||||
expect(state4.routes[1].routes[0].index).toEqual(1); // first2
|
||||
|
||||
const state5 = TestRouter.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'First',
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'First1' },
|
||||
},
|
||||
state3 // second.second2 is active on state3
|
||||
);
|
||||
expect(state5.index).toEqual(1); // main
|
||||
expect(state5.routes[1].index).toEqual(0); // first
|
||||
expect(state5.routes[1].routes[0].index).toEqual(0); // first.first1
|
||||
});
|
||||
|
||||
test('Handles no-op actions with tabs within stack router', () => {
|
||||
const BarView = () => <div />;
|
||||
const FooTabNavigator = () => <div />;
|
||||
@@ -157,6 +227,70 @@ test('Handles deep action', () => {
|
||||
expect(state2 && state2.routes[1].index).toEqual(1);
|
||||
});
|
||||
|
||||
test('Handles the navigate action with params', () => {
|
||||
const FooTabNavigator = () => <div />;
|
||||
FooTabNavigator.router = TabRouter({
|
||||
Baz: { screen: () => <div /> },
|
||||
Boo: { screen: () => <div /> },
|
||||
});
|
||||
|
||||
const TestRouter = StackRouter({
|
||||
Foo: { screen: () => <div /> },
|
||||
Bar: { screen: FooTabNavigator },
|
||||
});
|
||||
const state = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = TestRouter.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
immediate: true,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.routes[1].params).toEqual({ foo: '42' });
|
||||
expect(state2 && state2.routes[1].routes).toEqual([
|
||||
{
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
{
|
||||
key: 'Boo',
|
||||
routeName: 'Boo',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Handles the setParams action', () => {
|
||||
const FooTabNavigator = () => <div />;
|
||||
FooTabNavigator.router = TabRouter({
|
||||
Baz: { screen: () => <div /> },
|
||||
});
|
||||
const TestRouter = StackRouter({
|
||||
Foo: { screen: FooTabNavigator },
|
||||
Bar: { screen: () => <div /> },
|
||||
});
|
||||
const state = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = TestRouter.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'foobar' },
|
||||
key: 'Baz',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].routes).toEqual([
|
||||
{
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
params: { name: 'foobar' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Supports lazily-evaluated getScreen', () => {
|
||||
const BarView = () => <div />;
|
||||
const FooTabNavigator = () => <div />;
|
||||
@@ -249,3 +383,62 @@ test('Does not switch tab index when TabRouter child handles COMPLETE_NAVIGATION
|
||||
expect(stateAfterCompleteTransition.index).toEqual(1);
|
||||
expect(stateAfterSetParams.index).toEqual(1);
|
||||
});
|
||||
|
||||
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 TestRouter = TabRouter({
|
||||
Foo: { screen: ScreenA },
|
||||
});
|
||||
const screenApreState = {
|
||||
index: 0,
|
||||
key: 'Init',
|
||||
isTransitioning: false,
|
||||
routeName: 'Foo',
|
||||
routes: [{ key: 'Init', routeName: 'Bar' }],
|
||||
};
|
||||
const preState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [screenApreState],
|
||||
};
|
||||
|
||||
const comparable = state => {
|
||||
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 = TestRouter.getStateForAction(action, preState);
|
||||
const innerState = state ? state.routes[0] : state;
|
||||
|
||||
expect(expectedState && comparable(expectedState)).toEqual(
|
||||
innerState && comparable(innerState)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ import React from 'react';
|
||||
import StackRouter from '../StackRouter';
|
||||
import StackActions from '../StackActions';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import TabRouter from '../TabRouter';
|
||||
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -484,6 +483,44 @@ describe('StackRouter', () => {
|
||||
expect(state3 && state3.index).toEqual(0);
|
||||
});
|
||||
|
||||
test('pop action works as expected', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
bar: { screen: () => <div /> },
|
||||
});
|
||||
|
||||
const state = {
|
||||
index: 3,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'A', routeName: 'foo' },
|
||||
{ key: 'B', routeName: 'bar', params: { bazId: '321' } },
|
||||
{ key: 'C', routeName: 'foo' },
|
||||
{ key: 'D', routeName: 'bar' },
|
||||
],
|
||||
};
|
||||
const poppedState = TestRouter.getStateForAction(StackActions.pop(), state);
|
||||
expect(poppedState.routes.length).toBe(3);
|
||||
expect(poppedState.index).toBe(2);
|
||||
expect(poppedState.isTransitioning).toBe(true);
|
||||
|
||||
const poppedState2 = TestRouter.getStateForAction(
|
||||
StackActions.pop({ n: 2, immediate: true }),
|
||||
state
|
||||
);
|
||||
expect(poppedState2.routes.length).toBe(2);
|
||||
expect(poppedState2.index).toBe(1);
|
||||
expect(poppedState2.isTransitioning).toBe(false);
|
||||
|
||||
const poppedState3 = TestRouter.getStateForAction(
|
||||
StackActions.pop({ n: 5 }),
|
||||
state
|
||||
);
|
||||
expect(poppedState3.routes.length).toBe(1);
|
||||
expect(poppedState3.index).toBe(0);
|
||||
expect(poppedState3.isTransitioning).toBe(true);
|
||||
});
|
||||
|
||||
test('popToTop works as expected', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
@@ -642,23 +679,74 @@ describe('StackRouter', () => {
|
||||
expect(pushedState).toEqual(null);
|
||||
});
|
||||
|
||||
test('Navigate with key is idempotent', () => {
|
||||
test('Navigate with key and without it is idempotent', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
bar: { screen: () => <div /> },
|
||||
});
|
||||
const initState = TestRouter.getStateForAction(NavigationActions.init());
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', key: 'a' }),
|
||||
initState
|
||||
for (key of ['a', null]) {
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', key: 'a' }),
|
||||
initState
|
||||
);
|
||||
expect(pushedState.index).toEqual(1);
|
||||
expect(pushedState.routes[1].routeName).toEqual('bar');
|
||||
const pushedTwiceState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', key: 'a' }),
|
||||
pushedState
|
||||
);
|
||||
expect(pushedTwiceState).toEqual(null);
|
||||
}
|
||||
});
|
||||
|
||||
// https://github.com/react-navigation/react-navigation/issues/4063
|
||||
test('Navigate on inactive stackrouter is idempotent', () => {
|
||||
const FirstChildNavigator = () => <div />;
|
||||
FirstChildNavigator.router = StackRouter({
|
||||
First1: () => <div />,
|
||||
First2: () => <div />,
|
||||
});
|
||||
|
||||
const SecondChildNavigator = () => <div />;
|
||||
SecondChildNavigator.router = StackRouter({
|
||||
Second1: () => <div />,
|
||||
Second2: () => <div />,
|
||||
});
|
||||
|
||||
const router = StackRouter({
|
||||
Leaf: () => <div />,
|
||||
First: FirstChildNavigator,
|
||||
Second: SecondChildNavigator,
|
||||
});
|
||||
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
|
||||
const first = router.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'First2' }),
|
||||
state
|
||||
);
|
||||
expect(pushedState.index).toEqual(1);
|
||||
expect(pushedState.routes[1].routeName).toEqual('bar');
|
||||
const pushedTwiceState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', key: 'a' }),
|
||||
pushedState
|
||||
|
||||
const second = router.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'Second2' }),
|
||||
first
|
||||
);
|
||||
expect(pushedTwiceState).toEqual(null);
|
||||
|
||||
const firstAgain = router.getStateForAction(
|
||||
NavigationActions.navigate({
|
||||
routeName: 'First2',
|
||||
params: { debug: true },
|
||||
}),
|
||||
second
|
||||
);
|
||||
|
||||
expect(first.routes.length).toEqual(2);
|
||||
expect(first.index).toEqual(1);
|
||||
expect(second.routes.length).toEqual(3);
|
||||
expect(second.index).toEqual(2);
|
||||
|
||||
expect(firstAgain.index).toEqual(1);
|
||||
expect(firstAgain.routes.length).toEqual(2);
|
||||
});
|
||||
|
||||
test('Navigate to current routeName returns null to indicate handled action', () => {
|
||||
@@ -1079,15 +1167,51 @@ describe('StackRouter', () => {
|
||||
expect(state2 && state2.routes[0].params).toEqual({ name: 'Qux' });
|
||||
});
|
||||
|
||||
test('Handles the SetParams action for inactive routes', () => {
|
||||
const router = StackRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Bar',
|
||||
initialRouteParams: { name: 'Zoo' },
|
||||
}
|
||||
);
|
||||
const initialState = {
|
||||
index: 1,
|
||||
routes: [
|
||||
{
|
||||
key: 'RouteA',
|
||||
routeName: 'Foo',
|
||||
params: { name: 'InitialParam', other: 'Unchanged' },
|
||||
},
|
||||
{ key: 'RouteB', routeName: 'Bar', params: {} },
|
||||
],
|
||||
};
|
||||
const state = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'NewParam' },
|
||||
key: 'RouteA',
|
||||
},
|
||||
initialState
|
||||
);
|
||||
expect(state.index).toEqual(1);
|
||||
expect(state.routes[0].params).toEqual({
|
||||
name: 'NewParam',
|
||||
other: 'Unchanged',
|
||||
});
|
||||
});
|
||||
|
||||
test('Handles the setParams action with nested routers', () => {
|
||||
const ChildNavigator = () => <div />;
|
||||
const GrandChildNavigator = () => <div />;
|
||||
GrandChildNavigator.router = StackRouter({
|
||||
Quux: { screen: () => <div /> },
|
||||
Corge: { screen: () => <div /> },
|
||||
});
|
||||
ChildNavigator.router = TabRouter({
|
||||
Baz: { screen: GrandChildNavigator },
|
||||
ChildNavigator.router = StackRouter({
|
||||
Baz: { screen: () => <div /> },
|
||||
Qux: { screen: () => <div /> },
|
||||
});
|
||||
const router = StackRouter({
|
||||
@@ -1104,10 +1228,10 @@ describe('StackRouter', () => {
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].routes[0].routes).toEqual([
|
||||
expect(state2 && state2.routes[0].routes).toEqual([
|
||||
{
|
||||
key: 'id-0',
|
||||
routeName: 'Quux',
|
||||
routeName: 'Baz',
|
||||
params: { name: 'foobar' },
|
||||
},
|
||||
]);
|
||||
@@ -1193,7 +1317,7 @@ describe('StackRouter', () => {
|
||||
});
|
||||
|
||||
test('Handles the reset action with nested Router', () => {
|
||||
const ChildRouter = TabRouter({
|
||||
const ChildRouter = StackRouter({
|
||||
baz: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
@@ -1214,6 +1338,7 @@ describe('StackRouter', () => {
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: StackActions.RESET,
|
||||
key: null,
|
||||
actions: [
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
@@ -1445,42 +1570,6 @@ describe('StackRouter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Handles the navigate action with params and nested TabRouter', () => {
|
||||
const ChildNavigator = () => <div />;
|
||||
ChildNavigator.router = TabRouter({
|
||||
Baz: { screen: () => <div /> },
|
||||
Boo: { screen: () => <div /> },
|
||||
});
|
||||
|
||||
const router = StackRouter({
|
||||
Foo: { screen: () => <div /> },
|
||||
Bar: { screen: ChildNavigator },
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
immediate: true,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.routes[1].params).toEqual({ foo: '42' });
|
||||
expect(state2 && state2.routes[1].routes).toEqual([
|
||||
{
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
{
|
||||
key: 'Boo',
|
||||
routeName: 'Boo',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Handles empty URIs', () => {
|
||||
const router = StackRouter(
|
||||
{
|
||||
@@ -1761,3 +1850,120 @@ test('Handles deep navigate completion action', () => {
|
||||
expect(state3 && state3.routes[0].index).toEqual(1);
|
||||
expect(state3 && state3.routes[0].isTransitioning).toEqual(false);
|
||||
});
|
||||
|
||||
test('order of handling navigate action is correct for nested stackrouters', () => {
|
||||
const Screen = () => <div />;
|
||||
const NestedStack = () => <div />;
|
||||
let nestedRouter = StackRouter({
|
||||
Foo: Screen,
|
||||
Bar: Screen,
|
||||
});
|
||||
|
||||
NestedStack.router = nestedRouter;
|
||||
|
||||
let router = StackRouter(
|
||||
{
|
||||
NestedStack,
|
||||
Bar: Screen,
|
||||
Baz: Screen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Baz',
|
||||
}
|
||||
);
|
||||
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state.routes[state.index].routeName).toEqual('Baz');
|
||||
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2.routes[state2.index].routeName).toEqual('Bar');
|
||||
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Baz',
|
||||
},
|
||||
state2
|
||||
);
|
||||
expect(state3.routes[state3.index].routeName).toEqual('Baz');
|
||||
|
||||
const state4 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
},
|
||||
state3
|
||||
);
|
||||
let activeState4 = state4.routes[state4.index];
|
||||
expect(activeState4.routeName).toEqual('NestedStack');
|
||||
expect(activeState4.routes[activeState4.index].routeName).toEqual('Foo');
|
||||
|
||||
const state5 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state4
|
||||
);
|
||||
let activeState5 = state5.routes[state5.index];
|
||||
expect(activeState5.routeName).toEqual('NestedStack');
|
||||
expect(activeState5.routes[activeState5.index].routeName).toEqual('Bar');
|
||||
});
|
||||
|
||||
test('order of handling navigate action is correct for nested stackrouters', () => {
|
||||
const Screen = () => <div />;
|
||||
const NestedStack = () => <div />;
|
||||
const OtherNestedStack = () => <div />;
|
||||
|
||||
let nestedRouter = StackRouter({ Foo: Screen, Bar: Screen });
|
||||
let otherNestedRouter = StackRouter({ Foo: Screen });
|
||||
NestedStack.router = nestedRouter;
|
||||
OtherNestedStack.router = otherNestedRouter;
|
||||
|
||||
let router = StackRouter(
|
||||
{
|
||||
NestedStack,
|
||||
OtherNestedStack,
|
||||
Bar: Screen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'OtherNestedStack',
|
||||
}
|
||||
);
|
||||
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state.routes[state.index].routeName).toEqual('OtherNestedStack');
|
||||
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2.routes[state2.index].routeName).toEqual('Bar');
|
||||
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'NestedStack',
|
||||
},
|
||||
state2
|
||||
);
|
||||
const state4 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state3
|
||||
);
|
||||
let activeState4 = state4.routes[state4.index];
|
||||
expect(activeState4.routeName).toEqual('NestedStack');
|
||||
expect(activeState4.routes[activeState4.index].routeName).toEqual('Bar');
|
||||
});
|
||||
|
||||
@@ -127,6 +127,93 @@ describe('SwitchRouter', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('order of handling navigate action is correct for nested switchrouters', () => {
|
||||
// router = switch({ Nested: switch({ Foo, Bar }), Other: switch({ Foo }), Bar })
|
||||
// if we are focused on Other and navigate to Bar, what should happen?
|
||||
|
||||
const Screen = () => <div />;
|
||||
const NestedSwitch = () => <div />;
|
||||
const OtherNestedSwitch = () => <div />;
|
||||
|
||||
let nestedRouter = SwitchRouter({ Foo: Screen, Bar: Screen });
|
||||
let otherNestedRouter = SwitchRouter({ Foo: Screen });
|
||||
NestedSwitch.router = nestedRouter;
|
||||
OtherNestedSwitch.router = otherNestedRouter;
|
||||
|
||||
let router = SwitchRouter(
|
||||
{
|
||||
NestedSwitch,
|
||||
OtherNestedSwitch,
|
||||
Bar: Screen,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'OtherNestedSwitch',
|
||||
}
|
||||
);
|
||||
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state.routes[state.index].routeName).toEqual('OtherNestedSwitch');
|
||||
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2.routes[state2.index].routeName).toEqual('Bar');
|
||||
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'NestedSwitch',
|
||||
},
|
||||
state2
|
||||
);
|
||||
const state4 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state3
|
||||
);
|
||||
let activeState4 = state4.routes[state4.index];
|
||||
expect(activeState4.routeName).toEqual('NestedSwitch');
|
||||
expect(activeState4.routes[activeState4.index].routeName).toEqual('Bar');
|
||||
});
|
||||
|
||||
// https://github.com/react-navigation/react-navigation.github.io/issues/117#issuecomment-385597628
|
||||
test('order of handling navigate action is correct for nested stackrouters', () => {
|
||||
const Screen = () => <div />;
|
||||
const MainStack = () => <div />;
|
||||
const LoginStack = () => <div />;
|
||||
MainStack.router = StackRouter({ Home: Screen, Profile: Screen });
|
||||
LoginStack.router = StackRouter({ Form: Screen, ForgotPassword: Screen });
|
||||
|
||||
let router = SwitchRouter(
|
||||
{
|
||||
Home: Screen,
|
||||
Login: LoginStack,
|
||||
Main: MainStack,
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Login',
|
||||
}
|
||||
);
|
||||
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state.routes[state.index].routeName).toEqual('Login');
|
||||
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Home',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2.routes[state2.index].routeName).toEqual('Home');
|
||||
});
|
||||
});
|
||||
|
||||
const getExampleRouter = (config = {}) => {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import React from 'react';
|
||||
import TabRouter from '../TabRouter';
|
||||
import StackRouter from '../StackRouter';
|
||||
|
||||
import StackActions from '../../routers/StackActions';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
@@ -141,6 +140,46 @@ describe('TabRouter', () => {
|
||||
expect(state2 && state2.routes[0].params).toEqual({ name: 'Qux' });
|
||||
});
|
||||
|
||||
test('Handles the SetParams action for inactive routes', () => {
|
||||
const router = TabRouter(
|
||||
{
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: 'Bar',
|
||||
}
|
||||
);
|
||||
const initialState = {
|
||||
index: 1,
|
||||
routes: [
|
||||
{
|
||||
key: 'RouteA',
|
||||
routeName: 'Foo',
|
||||
params: { name: 'InitialParam', other: 'Unchanged' },
|
||||
},
|
||||
{ key: 'RouteB', routeName: 'Bar', params: {} },
|
||||
],
|
||||
};
|
||||
const state = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'NewParam' },
|
||||
key: 'RouteA',
|
||||
},
|
||||
initialState
|
||||
);
|
||||
expect(state.index).toEqual(1);
|
||||
expect(state.routes[0].params).toEqual({
|
||||
name: 'NewParam',
|
||||
other: 'Unchanged',
|
||||
});
|
||||
});
|
||||
|
||||
test('getStateForAction returns null when navigating to same tab', () => {
|
||||
const router = TabRouter(
|
||||
{ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig },
|
||||
@@ -695,53 +734,15 @@ describe('TabRouter', () => {
|
||||
expect(state2).toEqual(state0);
|
||||
});
|
||||
|
||||
test('pop action works as expected', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
bar: { screen: () => <div /> },
|
||||
});
|
||||
|
||||
const state = {
|
||||
index: 3,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'A', routeName: 'foo' },
|
||||
{ key: 'B', routeName: 'bar', params: { bazId: '321' } },
|
||||
{ key: 'C', routeName: 'foo' },
|
||||
{ key: 'D', routeName: 'bar' },
|
||||
],
|
||||
};
|
||||
const poppedState = TestRouter.getStateForAction(StackActions.pop(), state);
|
||||
expect(poppedState.routes.length).toBe(3);
|
||||
expect(poppedState.index).toBe(2);
|
||||
expect(poppedState.isTransitioning).toBe(true);
|
||||
|
||||
const poppedState2 = TestRouter.getStateForAction(
|
||||
StackActions.pop({ n: 2, immediate: true }),
|
||||
state
|
||||
);
|
||||
expect(poppedState2.routes.length).toBe(2);
|
||||
expect(poppedState2.index).toBe(1);
|
||||
expect(poppedState2.isTransitioning).toBe(false);
|
||||
|
||||
const poppedState3 = TestRouter.getStateForAction(
|
||||
StackActions.pop({ n: 5 }),
|
||||
state
|
||||
);
|
||||
expect(poppedState3.routes.length).toBe(1);
|
||||
expect(poppedState3.index).toBe(0);
|
||||
expect(poppedState3.isTransitioning).toBe(true);
|
||||
});
|
||||
|
||||
test('Inner actions are only unpacked if the current tab matches', () => {
|
||||
const PlainScreen = () => <div />;
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
ScreenB.router = StackRouter({
|
||||
ScreenB.router = TabRouter({
|
||||
Baz: { screen: PlainScreen },
|
||||
Zoo: { screen: PlainScreen },
|
||||
});
|
||||
ScreenA.router = StackRouter({
|
||||
ScreenA.router = TabRouter({
|
||||
Bar: { screen: PlainScreen },
|
||||
Boo: { screen: ScreenB },
|
||||
});
|
||||
@@ -750,10 +751,10 @@ describe('TabRouter', () => {
|
||||
});
|
||||
const screenApreState = {
|
||||
index: 0,
|
||||
key: 'Init',
|
||||
key: 'Foo',
|
||||
isTransitioning: false,
|
||||
routeName: 'Foo',
|
||||
routes: [{ key: 'Init', routeName: 'Bar' }],
|
||||
routes: [{ key: 'Bar', routeName: 'Bar' }],
|
||||
};
|
||||
const preState = {
|
||||
index: 0,
|
||||
@@ -779,7 +780,6 @@ describe('TabRouter', () => {
|
||||
routeName: 'Boo',
|
||||
action: NavigationActions.navigate({ routeName: 'Zoo' }),
|
||||
});
|
||||
|
||||
const expectedState = ScreenA.router.getStateForAction(
|
||||
action,
|
||||
screenApreState
|
||||
@@ -787,8 +787,25 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction(action, preState);
|
||||
const innerState = state ? state.routes[0] : state;
|
||||
|
||||
expect(innerState.routes[1].index).toEqual(1);
|
||||
expect(expectedState && comparable(expectedState)).toEqual(
|
||||
innerState && comparable(innerState)
|
||||
);
|
||||
|
||||
const noMatchAction = NavigationActions.navigate({
|
||||
routeName: 'Qux',
|
||||
action: NavigationActions.navigate({ routeName: 'Zoo' }),
|
||||
});
|
||||
const expectedState2 = ScreenA.router.getStateForAction(
|
||||
noMatchAction,
|
||||
screenApreState
|
||||
);
|
||||
const state2 = router.getStateForAction(noMatchAction, preState);
|
||||
const innerState2 = state2 ? state2.routes[0] : state2;
|
||||
|
||||
expect(innerState2.routes[1].index).toEqual(0);
|
||||
expect(expectedState2 && comparable(expectedState2)).toEqual(
|
||||
innerState2 && comparable(innerState2)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import StackActions from '../../routers/StackActions';
|
||||
import invariant from '../../utils/invariant';
|
||||
|
||||
/**
|
||||
@@ -46,9 +47,10 @@ class DrawerSidebar extends React.PureComponent {
|
||||
_onItemPress = ({ route, focused }) => {
|
||||
if (!focused) {
|
||||
let subAction;
|
||||
// TODO (v3): Revisit and repeal this behavior:
|
||||
// if the child screen is a StackRouter then always navigate to its first screen (see #1914)
|
||||
if (route.index !== undefined && route.index !== 0) {
|
||||
subAction = NavigationActions.reset({
|
||||
if (route.index != null && route.index !== 0) {
|
||||
subAction = StackActions.reset({
|
||||
index: 0,
|
||||
actions: [
|
||||
NavigationActions.navigate({
|
||||
@@ -57,7 +59,12 @@ class DrawerSidebar extends React.PureComponent {
|
||||
],
|
||||
});
|
||||
}
|
||||
this.props.navigation.navigate(route.routeName, undefined, subAction);
|
||||
this.props.navigation.dispatch(
|
||||
NavigationActions.navigate({
|
||||
routeName: route.routeName,
|
||||
action: subAction,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
I18nManager,
|
||||
ViewPropTypes,
|
||||
} from 'react-native';
|
||||
import { MaskedViewIOS } from '../../PlatformHelpers';
|
||||
@@ -24,7 +25,9 @@ const TITLE_OFFSET = Platform.OS === 'ios' ? 70 : 56;
|
||||
|
||||
const getAppBarHeight = isLandscape => {
|
||||
return Platform.OS === 'ios'
|
||||
? isLandscape && !Platform.isPad ? 32 : 44
|
||||
? isLandscape && !Platform.isPad
|
||||
? 32
|
||||
: 44
|
||||
: 56;
|
||||
};
|
||||
|
||||
@@ -47,12 +50,6 @@ class Header extends React.PureComponent {
|
||||
widths: {},
|
||||
};
|
||||
|
||||
_handleOnLayout = e => {
|
||||
if (typeof this.props.onLayout === 'function') {
|
||||
this.props.onLayout(e.nativeEvent.layout);
|
||||
}
|
||||
};
|
||||
|
||||
_getHeaderTitleString(scene) {
|
||||
const options = scene.descriptor.options;
|
||||
if (typeof options.headerTitle === 'string') {
|
||||
@@ -172,7 +169,7 @@ class Header extends React.PureComponent {
|
||||
ButtonContainerComponent,
|
||||
LabelContainerComponent
|
||||
) => {
|
||||
const { options } = props.scene.descriptor;
|
||||
const { options, navigation } = props.scene.descriptor;
|
||||
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||
props.scene
|
||||
@@ -184,7 +181,7 @@ class Header extends React.PureComponent {
|
||||
const goBack = () => {
|
||||
// Go back on next tick because button ripple effect needs to happen on Android
|
||||
requestAnimationFrame(() => {
|
||||
this.props.navigation.goBack(props.scene.descriptor.key);
|
||||
navigation.goBack(props.scene.descriptor.key);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -494,10 +491,7 @@ class Header extends React.PureComponent {
|
||||
const forceInset = headerForceInset || { top: 'always', bottom: 'never' };
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={this.props.layoutInterpolator(this.props)}
|
||||
onLayout={this._handleOnLayout}
|
||||
>
|
||||
<Animated.View style={this.props.layoutInterpolator(this.props)}>
|
||||
<SafeAreaView forceInset={forceInset} style={containerStyles}>
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
{options.headerBackground}
|
||||
@@ -572,6 +566,7 @@ const styles = StyleSheet.create({
|
||||
marginTop: -0.5, // resizes down to 20.5
|
||||
alignSelf: 'center',
|
||||
resizeMode: 'contain',
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
},
|
||||
title: {
|
||||
bottom: 0,
|
||||
|
||||
@@ -47,7 +47,10 @@ function isGoingBack(scenes) {
|
||||
}
|
||||
|
||||
function forLayout(props) {
|
||||
const { layout, position, scene, scenes } = props;
|
||||
const { layout, position, scene, scenes, mode } = props;
|
||||
if (mode !== 'float') {
|
||||
return {};
|
||||
}
|
||||
const isBack = isGoingBack(scenes);
|
||||
|
||||
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||
@@ -55,8 +58,19 @@ function forLayout(props) {
|
||||
|
||||
const { first, last } = interpolate;
|
||||
const index = scene.index;
|
||||
|
||||
const width = layout.initWidth;
|
||||
|
||||
// Make sure the header stays hidden when transitioning between 2 screens
|
||||
// with no header.
|
||||
if (
|
||||
(isBack && !hasHeader(scenes[index]) && !hasHeader(scenes[last])) ||
|
||||
(!isBack && !hasHeader(scenes[first]) && !hasHeader(scenes[index]))
|
||||
) {
|
||||
return {
|
||||
transform: [{ translateX: width }],
|
||||
};
|
||||
}
|
||||
|
||||
const rtlMult = I18nManager.isRTL ? -1 : 1;
|
||||
const translateX = position.interpolate({
|
||||
inputRange: [first, index, last],
|
||||
@@ -163,11 +177,11 @@ function forLeftButton(props) {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* NOTE: this offset calculation is an approximation that gives us
|
||||
* decent results in many cases, but it is ultimately a poor substitute
|
||||
* for text measurement. See the comment on title for more information.
|
||||
*
|
||||
*
|
||||
* - 70 is the width of the left button area.
|
||||
* - 25 is the width of the left button icon (to account for label offset)
|
||||
*/
|
||||
@@ -234,14 +248,14 @@ function forLeftLabel(props) {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* NOTE: this offset calculation is a an approximation that gives us
|
||||
* decent results in many cases, but it is ultimately a poor substitute
|
||||
* for text measurement. We want the back button label to transition
|
||||
* smoothly into the title text and to do this we need to understand
|
||||
* where the title is positioned within the title container (since it is
|
||||
* centered).
|
||||
*
|
||||
*
|
||||
* - 70 is the width of the left button area.
|
||||
* - 25 is the width of the left button icon (to account for label offset)
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import withLifecyclePolyfill from 'react-lifecycles-compat';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
|
||||
import SceneView from './SceneView';
|
||||
|
||||
@@ -77,4 +77,4 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default withLifecyclePolyfill(ResourceSavingSceneView);
|
||||
export default polyfill(ResourceSavingSceneView);
|
||||
|
||||
@@ -27,10 +27,7 @@ class StackView extends React.Component {
|
||||
onTransitionStart={this.props.onTransitionStart}
|
||||
onTransitionEnd={(transition, lastTransition) => {
|
||||
const { onTransitionEnd, navigation } = this.props;
|
||||
if (
|
||||
transition.navigation.state.isTransitioning &&
|
||||
!lastTransition.navigation.state.isTransitioning
|
||||
) {
|
||||
if (transition.navigation.state.isTransitioning) {
|
||||
navigation.dispatch(
|
||||
StackActions.completeTransition({
|
||||
key: navigation.state.key,
|
||||
@@ -60,6 +57,9 @@ class StackView extends React.Component {
|
||||
return (
|
||||
<StackViewLayout
|
||||
{...navigationConfig}
|
||||
onGestureBegin={this.props.onGestureBegin}
|
||||
onGestureCanceled={this.props.onGestureCanceled}
|
||||
onGestureEnd={this.props.onGestureEnd}
|
||||
screenProps={screenProps}
|
||||
descriptors={this.props.descriptors}
|
||||
transitionProps={transitionProps}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
View,
|
||||
I18nManager,
|
||||
Easing,
|
||||
Dimensions,
|
||||
} from 'react-native';
|
||||
|
||||
import Card from './StackViewCard';
|
||||
@@ -16,6 +17,7 @@ import Header from '../Header/Header';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import StackActions from '../../routers/StackActions';
|
||||
import SceneView from '../SceneView';
|
||||
import withOrientation from '../withOrientation';
|
||||
import { NavigationProvider } from '../NavigationContext';
|
||||
|
||||
import TransitionConfigs from './StackViewTransitionConfigs';
|
||||
@@ -23,6 +25,13 @@ import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
||||
|
||||
const emptyFunction = () => {};
|
||||
|
||||
const { width: WINDOW_WIDTH, height: WINDOW_HEIGHT } = Dimensions.get('window');
|
||||
const IS_IPHONE_X =
|
||||
Platform.OS === 'ios' &&
|
||||
!Platform.isPad &&
|
||||
!Platform.isTVOS &&
|
||||
(WINDOW_HEIGHT === 812 || WINDOW_WIDTH === 812);
|
||||
|
||||
const EaseInOut = Easing.inOut(Easing.ease);
|
||||
|
||||
/**
|
||||
@@ -94,11 +103,7 @@ class StackViewLayout extends React.Component {
|
||||
}
|
||||
|
||||
// Handle the case where the header option is a function, and provide the default
|
||||
const renderHeader =
|
||||
header ||
|
||||
(props => (
|
||||
<Header onLayout={layout => (this._headerLayout = layout)} {...props} />
|
||||
));
|
||||
const renderHeader = header || (props => <Header {...props} />);
|
||||
|
||||
const {
|
||||
headerLeftInterpolator,
|
||||
@@ -222,8 +227,12 @@ class StackViewLayout extends React.Component {
|
||||
const { index } = navigation.state;
|
||||
const isVertical = mode === 'modal';
|
||||
const { options } = scene.descriptor;
|
||||
const gestureDirection = options.gestureDirection;
|
||||
|
||||
const gestureDirectionInverted = options.gestureDirection === 'inverted';
|
||||
const gestureDirectionInverted =
|
||||
typeof gestureDirection === 'string'
|
||||
? gestureDirection === 'inverted'
|
||||
: I18nManager.isRTL;
|
||||
|
||||
const gesturesEnabled =
|
||||
typeof options.gesturesEnabled === 'boolean'
|
||||
@@ -236,12 +245,14 @@ class StackViewLayout extends React.Component {
|
||||
onPanResponderTerminate: () => {
|
||||
this._isResponding = false;
|
||||
this._reset(index, 0);
|
||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||
},
|
||||
onPanResponderGrant: () => {
|
||||
position.stopAnimation((value: number) => {
|
||||
this._isResponding = true;
|
||||
this._gestureStartValue = value;
|
||||
});
|
||||
this.props.onGestureBegin && this.props.onGestureBegin();
|
||||
},
|
||||
onMoveShouldSetPanResponder: (event, gesture) => {
|
||||
if (index !== scene.index) {
|
||||
@@ -295,7 +306,7 @@ class StackViewLayout extends React.Component {
|
||||
? layout.height.__getValue()
|
||||
: layout.width.__getValue();
|
||||
const currentValue =
|
||||
(I18nManager.isRTL && axis === 'dx') !== gestureDirectionInverted
|
||||
axis === 'dx' && gestureDirectionInverted
|
||||
? startValue + gesture[axis] / axisDistance
|
||||
: startValue - gesture[axis] / axisDistance;
|
||||
const value = clamp(index - 1, currentValue, index);
|
||||
@@ -340,10 +351,12 @@ class StackViewLayout extends React.Component {
|
||||
// If the speed of the gesture release is significant, use that as the indication
|
||||
// of intent
|
||||
if (gestureVelocity < -0.5) {
|
||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||
this._reset(immediateIndex, resetDuration);
|
||||
return;
|
||||
}
|
||||
if (gestureVelocity > 0.5) {
|
||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||
this._goBack(immediateIndex, goBackDuration);
|
||||
return;
|
||||
}
|
||||
@@ -351,8 +364,10 @@ class StackViewLayout extends React.Component {
|
||||
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
||||
// and the back will happen.
|
||||
if (value <= index - POSITION_THRESHOLD) {
|
||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||
this._goBack(immediateIndex, goBackDuration);
|
||||
} else {
|
||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||
this._reset(immediateIndex, resetDuration);
|
||||
}
|
||||
});
|
||||
@@ -453,8 +468,20 @@ class StackViewLayout extends React.Component {
|
||||
const hasHeader = options.header !== null;
|
||||
const headerMode = this._getHeaderMode();
|
||||
let marginTop = 0;
|
||||
if (!hasHeader && headerMode === 'float' && this._headerLayout) {
|
||||
marginTop = -this._headerLayout.height;
|
||||
if (!hasHeader && headerMode === 'float') {
|
||||
const { isLandscape } = this.props;
|
||||
let headerHeight;
|
||||
if (Platform.OS === 'android') {
|
||||
// TODO: Need to handle translucent status bar.
|
||||
headerHeight = 56;
|
||||
} else if (isLandscape && !Platform.isPad) {
|
||||
headerHeight = 52;
|
||||
} else if (IS_IPHONE_X) {
|
||||
headerHeight = 88;
|
||||
} else {
|
||||
headerHeight = 64;
|
||||
}
|
||||
marginTop = -headerHeight;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -484,4 +511,4 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default StackViewLayout;
|
||||
export default withOrientation(StackViewLayout);
|
||||
|
||||
49
src/views/__tests__/Transitioner-test.js
Normal file
49
src/views/__tests__/Transitioner-test.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* eslint react/display-name:0 */
|
||||
import * as React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import Transitioner from '../Transitioner';
|
||||
|
||||
describe('Transitioner', () => {
|
||||
it('should not trigger onTransitionStart and onTransitionEnd when route params are changed', () => {
|
||||
const onTransitionStartCallback = jest.fn();
|
||||
const onTransitionEndCallback = jest.fn();
|
||||
|
||||
const transitionerProps = {
|
||||
configureTransition: (transitionProps, prevTransitionProps) => ({}),
|
||||
navigation: {
|
||||
state: {
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: '1', routeName: 'Foo' },
|
||||
{ key: '2', routeName: 'Bar' },
|
||||
],
|
||||
},
|
||||
goBack: () => false,
|
||||
dispatch: () => false,
|
||||
setParams: () => false,
|
||||
navigate: () => false,
|
||||
},
|
||||
render: () => <div />,
|
||||
onTransitionStart: onTransitionStartCallback,
|
||||
onTransitionEnd: onTransitionEndCallback,
|
||||
};
|
||||
|
||||
const nextTransitionerProps = {
|
||||
...transitionerProps,
|
||||
navigation: {
|
||||
...transitionerProps.navigation,
|
||||
state: {
|
||||
index: 0,
|
||||
routes: [
|
||||
{ key: '1', routeName: 'Foo', params: { name: 'Zoom' } },
|
||||
{ key: '2', routeName: 'Bar' },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const component = renderer.create(<Transitioner {...transitionerProps} />);
|
||||
component.update(<Transitioner {...nextTransitionerProps} />);
|
||||
expect(onTransitionStartCallback).not.toBeCalled();
|
||||
expect(onTransitionEndCallback).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
177
yarn.lock
177
yarn.lock
@@ -108,7 +108,7 @@ ansi-gray@^0.1.1:
|
||||
dependencies:
|
||||
ansi-wrap "0.1.0"
|
||||
|
||||
ansi-regex@^2.0.0, ansi-regex@^2.1.1:
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||
|
||||
@@ -120,7 +120,7 @@ ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||
|
||||
ansi-styles@^3.0.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1:
|
||||
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
dependencies:
|
||||
@@ -1372,34 +1372,20 @@ collection-visit@^1.0.0:
|
||||
map-visit "^1.0.0"
|
||||
object-visit "^1.0.0"
|
||||
|
||||
color-convert@^1.9.0, color-convert@^1.9.1:
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
|
||||
dependencies:
|
||||
color-name "^1.1.1"
|
||||
|
||||
color-name@^1.0.0, color-name@^1.1.1:
|
||||
color-name@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
|
||||
color-string@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.2.tgz#26e45814bc3c9a7cbd6751648a41434514a773a9"
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
simple-swizzle "^0.2.2"
|
||||
|
||||
color-support@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
|
||||
|
||||
color@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color/-/color-2.0.1.tgz#e4ed78a3c4603d0891eba5430b04b86314f4c839"
|
||||
dependencies:
|
||||
color-convert "^1.9.1"
|
||||
color-string "^1.5.2"
|
||||
|
||||
combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
|
||||
@@ -1693,10 +1679,6 @@ deep-is@~0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
|
||||
deepmerge@^2.0.1:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102"
|
||||
|
||||
default-require-extensions@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
|
||||
@@ -1908,7 +1890,7 @@ escodegen@^1.9.0:
|
||||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-prettier@^2.3.0:
|
||||
eslint-config-prettier@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz#5ecd65174d486c22dff389fe036febf502d468a3"
|
||||
dependencies:
|
||||
@@ -1955,7 +1937,7 @@ eslint-plugin-jsx-a11y@^6.0.2:
|
||||
emoji-regex "^6.1.0"
|
||||
jsx-ast-utils "^2.0.0"
|
||||
|
||||
eslint-plugin-prettier@^2.1.2:
|
||||
eslint-plugin-prettier@^2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz#33e4e228bdb06142d03c560ce04ec23f6c767dd7"
|
||||
dependencies:
|
||||
@@ -1982,7 +1964,50 @@ eslint-visitor-keys@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
||||
|
||||
eslint@^4.2.0, eslint@^4.5.0:
|
||||
eslint@^4.0.0:
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300"
|
||||
dependencies:
|
||||
ajv "^5.3.0"
|
||||
babel-code-frame "^6.22.0"
|
||||
chalk "^2.1.0"
|
||||
concat-stream "^1.6.0"
|
||||
cross-spawn "^5.1.0"
|
||||
debug "^3.1.0"
|
||||
doctrine "^2.1.0"
|
||||
eslint-scope "^3.7.1"
|
||||
eslint-visitor-keys "^1.0.0"
|
||||
espree "^3.5.4"
|
||||
esquery "^1.0.0"
|
||||
esutils "^2.0.2"
|
||||
file-entry-cache "^2.0.0"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
glob "^7.1.2"
|
||||
globals "^11.0.1"
|
||||
ignore "^3.3.3"
|
||||
imurmurhash "^0.1.4"
|
||||
inquirer "^3.0.6"
|
||||
is-resolvable "^1.0.0"
|
||||
js-yaml "^3.9.1"
|
||||
json-stable-stringify-without-jsonify "^1.0.1"
|
||||
levn "^0.3.0"
|
||||
lodash "^4.17.4"
|
||||
minimatch "^3.0.2"
|
||||
mkdirp "^0.5.1"
|
||||
natural-compare "^1.4.0"
|
||||
optionator "^0.8.2"
|
||||
path-is-inside "^1.0.2"
|
||||
pluralize "^7.0.0"
|
||||
progress "^2.0.0"
|
||||
regexpp "^1.0.1"
|
||||
require-uncached "^1.0.3"
|
||||
semver "^5.3.0"
|
||||
strip-ansi "^4.0.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
table "4.0.2"
|
||||
text-table "~0.2.0"
|
||||
|
||||
eslint@^4.2.0:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.2.tgz#0f81267ad1012e7d2051e186a9004cc2267b8d45"
|
||||
dependencies:
|
||||
@@ -2024,7 +2049,7 @@ eslint@^4.2.0, eslint@^4.5.0:
|
||||
table "4.0.2"
|
||||
text-table "~0.2.0"
|
||||
|
||||
espree@^3.5.2:
|
||||
espree@^3.5.2, espree@^3.5.4:
|
||||
version "3.5.4"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
|
||||
dependencies:
|
||||
@@ -2883,10 +2908,6 @@ is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
|
||||
is-arrayish@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.1.tgz#c2dfc386abaa0c3e33c48db3fe87059e69065efd"
|
||||
|
||||
is-binary-path@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
|
||||
@@ -3896,6 +3917,10 @@ lodash.throttle@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
|
||||
|
||||
lodash.unescape@4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
|
||||
|
||||
lodash@^3.5.0:
|
||||
version "3.10.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
|
||||
@@ -4681,30 +4706,26 @@ preserve@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prettier-eslint@^6.4.2:
|
||||
version "6.4.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-6.4.3.tgz#8335b7a8bd50d01879a9d505bc0b9208caa9ecfc"
|
||||
prettier-eslint@^8.8.1:
|
||||
version "8.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-8.8.1.tgz#38505163274742f2a0b31653c39e40f37ebd07da"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
common-tags "^1.4.0"
|
||||
dlv "^1.1.0"
|
||||
eslint "^4.5.0"
|
||||
eslint "^4.0.0"
|
||||
indent-string "^3.2.0"
|
||||
lodash.merge "^4.6.0"
|
||||
loglevel-colored-level-prefix "^1.0.0"
|
||||
prettier "^1.6.0"
|
||||
pretty-format "^20.0.3"
|
||||
prettier "^1.7.0"
|
||||
pretty-format "^22.0.3"
|
||||
require-relative "^0.8.7"
|
||||
typescript "^2.5.1"
|
||||
typescript-eslint-parser "^11.0.0"
|
||||
|
||||
prettier@^1.5.3, prettier@^1.6.0:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
|
||||
|
||||
pretty-format@^20.0.3:
|
||||
version "20.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14"
|
||||
dependencies:
|
||||
ansi-regex "^2.1.1"
|
||||
ansi-styles "^3.0.0"
|
||||
prettier@^1.12.1, prettier@^1.7.0:
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
|
||||
|
||||
pretty-format@^21.2.1:
|
||||
version "21.2.1"
|
||||
@@ -4713,6 +4734,13 @@ pretty-format@^21.2.1:
|
||||
ansi-regex "^3.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
|
||||
pretty-format@^22.0.3:
|
||||
version "22.4.3"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f"
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
|
||||
pretty-format@^22.4.0:
|
||||
version "22.4.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.0.tgz#237b1f7e1c50ed03bc65c03ccc29d7c8bb7beb94"
|
||||
@@ -4833,6 +4861,10 @@ react-lifecycles-compat@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-1.0.2.tgz#551d8b1d156346e5fcf30ffac9b32ce3f78b8850"
|
||||
|
||||
react-lifecycles-compat@^3:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.2.tgz#7279047275bd727a912e25f734c0559527e84eff"
|
||||
|
||||
react-native-dismiss-keyboard@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz#32886242b3f2317e121f3aeb9b0a585e2b879b49"
|
||||
@@ -4849,24 +4881,15 @@ react-native-drawer-layout@1.3.2:
|
||||
dependencies:
|
||||
react-native-dismiss-keyboard "1.0.0"
|
||||
|
||||
react-native-paper@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-paper/-/react-native-paper-1.2.5.tgz#3a4480ca967aae4b3e65a987116973dd012d1fa8"
|
||||
dependencies:
|
||||
color "^2.0.1"
|
||||
create-react-context "^0.2.1"
|
||||
deepmerge "^2.0.1"
|
||||
hoist-non-react-statics "^2.5.0"
|
||||
|
||||
react-native-safe-area-view@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.7.0.tgz#38f5ab9368d6ef9e5d18ab64212938af3ec39421"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.3.1"
|
||||
|
||||
react-native-tab-view@react-navigation/react-native-tab-view:
|
||||
react-native-tab-view@^0.0.74:
|
||||
version "0.0.74"
|
||||
resolved "https://codeload.github.com/react-navigation/react-native-tab-view/tar.gz/36ebd834d78b841fc19778c966465d02fd1213bb"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.74.tgz#62c0c882d9232b461ce181d440d683b4f99d1bd8"
|
||||
dependencies:
|
||||
prop-types "^15.6.0"
|
||||
|
||||
@@ -4943,20 +4966,19 @@ react-native@^0.52.0:
|
||||
xmldoc "^0.4.0"
|
||||
yargs "^9.0.0"
|
||||
|
||||
react-navigation-deprecated-tab-navigator@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-deprecated-tab-navigator/-/react-navigation-deprecated-tab-navigator-1.0.1.tgz#a869d749d16116630411d6c7761c51484ba90175"
|
||||
react-navigation-deprecated-tab-navigator@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-deprecated-tab-navigator/-/react-navigation-deprecated-tab-navigator-1.2.0.tgz#e0d969c196dcd3a4a440770a7bd97fa058eb4aaf"
|
||||
dependencies:
|
||||
react-native-tab-view react-navigation/react-native-tab-view
|
||||
react-native-tab-view "^0.0.74"
|
||||
|
||||
react-navigation-tabs@0.1.0-alpha.6:
|
||||
version "0.1.0-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-0.1.0-alpha.6.tgz#2d10b41f8406e4be7b48f53df60c8f78fdd0c8fb"
|
||||
react-navigation-tabs@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-0.2.0.tgz#d25be64e01a728c893a93cfeee6b176ab74d67de"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.5.0"
|
||||
prop-types "^15.6.0"
|
||||
react-lifecycles-compat "^1.0.2"
|
||||
react-native-paper "^1.2.5"
|
||||
react-native-safe-area-view "^0.7.0"
|
||||
react-native-tab-view "~0.0.77"
|
||||
|
||||
@@ -5094,6 +5116,10 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||
extend-shallow "^3.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
regexpp@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab"
|
||||
|
||||
regexpu-core@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
|
||||
@@ -5385,6 +5411,10 @@ sax@~1.1.1:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
|
||||
semver@5.4.1:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
||||
|
||||
send@0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.13.2.tgz#765e7607c8055452bba6f0b052595350986036de"
|
||||
@@ -5500,12 +5530,6 @@ simple-plist@^0.2.1:
|
||||
bplist-parser "0.1.1"
|
||||
plist "2.0.1"
|
||||
|
||||
simple-swizzle@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
|
||||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
slash@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||
@@ -5963,6 +5987,17 @@ typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
|
||||
typescript-eslint-parser@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-11.0.0.tgz#37dba6a0130dd307504aa4b4b21b0d3dc7d4e9f2"
|
||||
dependencies:
|
||||
lodash.unescape "4.0.1"
|
||||
semver "5.4.1"
|
||||
|
||||
typescript@^2.5.1:
|
||||
version "2.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.3.tgz#5d817f9b6f31bb871835f4edf0089f21abe6c170"
|
||||
|
||||
ua-parser-js@^0.7.9:
|
||||
version "0.7.17"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
||||
|
||||
Reference in New Issue
Block a user