Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
371a714b57 | ||
|
|
1d33b95c5f | ||
|
|
593bc8a648 | ||
|
|
174a6e4175 | ||
|
|
af991e5512 | ||
|
|
0b0e9e9df5 | ||
|
|
42b0ccca79 | ||
|
|
da6a960bb1 | ||
|
|
3ca47ec778 | ||
|
|
14ee56a20d | ||
|
|
9d36daf48e | ||
|
|
055fdb2ffc | ||
|
|
b3bf80634d | ||
|
|
fafd0e8702 | ||
|
|
d94045817c | ||
|
|
f0acaddf05 | ||
|
|
9beb32ecac | ||
|
|
29a29936bf | ||
|
|
8961fef00b | ||
|
|
021c7e54be | ||
|
|
27538cad94 | ||
|
|
9a8a50ef1d | ||
|
|
2c6dc35723 | ||
|
|
3600ecbd9b | ||
|
|
c74f001b1c | ||
|
|
7f4706e4cc | ||
|
|
d0ef33b12f | ||
|
|
3c3668c952 | ||
|
|
5febb81a1c | ||
|
|
50dcb37cd7 | ||
|
|
69bca191a7 | ||
|
|
4df9198979 | ||
|
|
0fc7f87173 | ||
|
|
5b4d11ab5d | ||
|
|
4d1cc285b9 | ||
|
|
41725afa8a | ||
|
|
58b77d44ae | ||
|
|
1d49b6e3fe | ||
|
|
58b8a08af6 | ||
|
|
b11ea8a1d5 | ||
|
|
3c2b977eca | ||
|
|
95565487ec | ||
|
|
a26d2acfca | ||
|
|
a1b3d2213d | ||
|
|
eb39df507e | ||
|
|
cca06bb530 | ||
|
|
2187d8fe66 | ||
|
|
67f98b69eb |
@@ -1,11 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import { Constants, ScreenOrientation } from 'expo';
|
||||
import { Asset, Constants, ScreenOrientation } from 'expo';
|
||||
|
||||
ScreenOrientation.allow(ScreenOrientation.Orientation.ALL);
|
||||
|
||||
import {
|
||||
Animated,
|
||||
Image,
|
||||
Platform,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
@@ -16,7 +18,6 @@ import {
|
||||
} from 'react-native';
|
||||
import { SafeAreaView, StackNavigator } from 'react-navigation';
|
||||
|
||||
import Banner from './Banner';
|
||||
import CustomTabs from './CustomTabs';
|
||||
import CustomTransitioner from './CustomTransitioner';
|
||||
import Drawer from './Drawer';
|
||||
@@ -25,9 +26,12 @@ import TabsInDrawer from './TabsInDrawer';
|
||||
import ModalStack from './ModalStack';
|
||||
import StacksInTabs from './StacksInTabs';
|
||||
import StacksOverTabs from './StacksOverTabs';
|
||||
import StacksWithKeys from './StacksWithKeys';
|
||||
import SimpleStack from './SimpleStack';
|
||||
import StackWithHeaderPreset from './StackWithHeaderPreset';
|
||||
import SimpleTabs from './SimpleTabs';
|
||||
import TabAnimations from './TabAnimations';
|
||||
import TabsWithNavigationFocus from './TabsWithNavigationFocus';
|
||||
|
||||
const ExampleInfo = {
|
||||
SimpleStack: {
|
||||
@@ -42,6 +46,10 @@ const ExampleInfo = {
|
||||
name: 'Drawer Example',
|
||||
description: 'Android-style drawer navigation',
|
||||
},
|
||||
StackWithHeaderPreset: {
|
||||
name: 'UIKit-style Header Transitions',
|
||||
description: 'Masked back button and sliding header items. iOS only.',
|
||||
},
|
||||
// MultipleDrawer: {
|
||||
// name: 'Multiple Drawer Example',
|
||||
// description: 'Add any drawer you need',
|
||||
@@ -76,6 +84,10 @@ const ExampleInfo = {
|
||||
name: 'Stacks over Tabs',
|
||||
description: 'Nested stack navigation that pushes on top of tabs',
|
||||
},
|
||||
StacksWithKeys: {
|
||||
name: 'Link in Stack with keys',
|
||||
description: 'Use keys to link between screens',
|
||||
},
|
||||
LinkStack: {
|
||||
name: 'Link in Stack',
|
||||
description: 'Deep linking into a route in stack',
|
||||
@@ -88,38 +100,27 @@ const ExampleInfo = {
|
||||
name: 'Animated Tabs Example',
|
||||
description: 'Tab transitions have custom animations',
|
||||
},
|
||||
// TabsWithNavigationFocus: {
|
||||
// name: 'withNavigationFocus',
|
||||
// description: 'Receive the focus prop to know when a screen is focused',
|
||||
// },
|
||||
};
|
||||
|
||||
const ExampleRoutes = {
|
||||
SimpleStack: {
|
||||
screen: SimpleStack,
|
||||
},
|
||||
SimpleTabs: {
|
||||
screen: SimpleTabs,
|
||||
},
|
||||
Drawer: {
|
||||
screen: Drawer,
|
||||
},
|
||||
SimpleStack: SimpleStack,
|
||||
SimpleTabs: SimpleTabs,
|
||||
Drawer: Drawer,
|
||||
// MultipleDrawer: {
|
||||
// screen: MultipleDrawer,
|
||||
// },
|
||||
TabsInDrawer: {
|
||||
screen: TabsInDrawer,
|
||||
},
|
||||
CustomTabs: {
|
||||
screen: CustomTabs,
|
||||
},
|
||||
CustomTransitioner: {
|
||||
screen: CustomTransitioner,
|
||||
},
|
||||
ModalStack: {
|
||||
screen: ModalStack,
|
||||
},
|
||||
StacksInTabs: {
|
||||
screen: StacksInTabs,
|
||||
},
|
||||
StacksOverTabs: {
|
||||
screen: StacksOverTabs,
|
||||
},
|
||||
StackWithHeaderPreset: StackWithHeaderPreset,
|
||||
TabsInDrawer: TabsInDrawer,
|
||||
CustomTabs: CustomTabs,
|
||||
CustomTransitioner: CustomTransitioner,
|
||||
ModalStack: ModalStack,
|
||||
StacksWithKeys: StacksWithKeys,
|
||||
StacksInTabs: StacksInTabs,
|
||||
StacksOverTabs: StacksOverTabs,
|
||||
LinkStack: {
|
||||
screen: SimpleStack,
|
||||
path: 'people/Jordan',
|
||||
@@ -128,48 +129,148 @@ const ExampleRoutes = {
|
||||
screen: SimpleTabs,
|
||||
path: 'settings',
|
||||
},
|
||||
TabAnimations: {
|
||||
screen: TabAnimations,
|
||||
},
|
||||
TabAnimations: TabAnimations,
|
||||
// TabsWithNavigationFocus: TabsWithNavigationFocus,
|
||||
};
|
||||
|
||||
class MainScreen extends React.Component<*> {
|
||||
type State = {
|
||||
scrollY: Animated.Value,
|
||||
};
|
||||
class MainScreen extends React.Component<any, State> {
|
||||
state = {
|
||||
scrollY: new Animated.Value(0),
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
Asset.fromModule(
|
||||
require('react-navigation/src/views/assets/back-icon-mask.png')
|
||||
).downloadAsync();
|
||||
Asset.fromModule(
|
||||
require('react-navigation/src/views/assets/back-icon.png')
|
||||
).downloadAsync();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
const scale = this.state.scrollY.interpolate({
|
||||
inputRange: [-450, 0, 100],
|
||||
outputRange: [2, 1, 0.8],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
const translateY = this.state.scrollY.interpolate({
|
||||
inputRange: [-450, 0, 100],
|
||||
outputRange: [-150, 0, 40],
|
||||
});
|
||||
|
||||
const opacity = this.state.scrollY.interpolate({
|
||||
inputRange: [0, 50],
|
||||
outputRange: [1, 0],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
const underlayOpacity = this.state.scrollY.interpolate({
|
||||
inputRange: [0, 50],
|
||||
outputRange: [0, 1],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
const backgroundScale = this.state.scrollY.interpolate({
|
||||
inputRange: [-450, 0],
|
||||
outputRange: [3, 1],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
const backgroundTranslateY = this.state.scrollY.interpolate({
|
||||
inputRange: [-450, 0],
|
||||
outputRange: [0, 0],
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
<Banner />
|
||||
{Object.keys(ExampleRoutes).map((routeName: string) => (
|
||||
<TouchableOpacity
|
||||
key={routeName}
|
||||
onPress={() => {
|
||||
const { path, params, screen } = ExampleRoutes[routeName];
|
||||
const { router } = screen;
|
||||
const action =
|
||||
path && router.getActionForPathAndParams(path, params);
|
||||
navigation.navigate(routeName, {}, action);
|
||||
}}
|
||||
<Animated.ScrollView
|
||||
style={{ flex: 1 }}
|
||||
scrollEventThrottle={1}
|
||||
onScroll={Animated.event(
|
||||
[
|
||||
{
|
||||
nativeEvent: { contentOffset: { y: this.state.scrollY } },
|
||||
},
|
||||
],
|
||||
{ useNativeDriver: true }
|
||||
)}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.backgroundUnderlay,
|
||||
{
|
||||
transform: [
|
||||
{ scale: backgroundScale },
|
||||
{ translateY: backgroundTranslateY },
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Animated.View
|
||||
style={{ opacity, transform: [{ scale }, { translateY }] }}
|
||||
>
|
||||
<SafeAreaView
|
||||
style={styles.bannerContainer}
|
||||
forceInset={{ top: 'always', bottom: 'never' }}
|
||||
>
|
||||
<SafeAreaView
|
||||
style={styles.itemContainer}
|
||||
forceInset={{ vertical: 'never' }}
|
||||
>
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.title}>
|
||||
{ExampleInfo[routeName].name}
|
||||
</Text>
|
||||
<Text style={styles.description}>
|
||||
{ExampleInfo[routeName].description}
|
||||
</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
<View style={styles.banner}>
|
||||
<Image
|
||||
source={require('./assets/NavLogo.png')}
|
||||
style={styles.bannerImage}
|
||||
/>
|
||||
<Text style={styles.bannerTitle}>
|
||||
React Navigation Examples
|
||||
</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</Animated.View>
|
||||
|
||||
<SafeAreaView forceInset={{ bottom: 'always', horizontal: 'never' }}>
|
||||
<View style={{ backgroundColor: '#fff' }}>
|
||||
{Object.keys(ExampleRoutes).map((routeName: string) => (
|
||||
<TouchableOpacity
|
||||
key={routeName}
|
||||
onPress={() => {
|
||||
let route = ExampleRoutes[routeName];
|
||||
if (route.screen || route.path || route.params) {
|
||||
const { path, params, screen } = route;
|
||||
const { router } = screen;
|
||||
const action =
|
||||
path && router.getActionForPathAndParams(path, params);
|
||||
navigation.navigate(routeName, {}, action);
|
||||
} else {
|
||||
navigation.navigate(routeName);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SafeAreaView
|
||||
style={styles.itemContainer}
|
||||
forceInset={{ veritcal: 'never', bottom: 'never' }}
|
||||
>
|
||||
<View style={styles.item}>
|
||||
<Text style={styles.title}>
|
||||
{ExampleInfo[routeName].name}
|
||||
</Text>
|
||||
<Text style={styles.description}>
|
||||
{ExampleInfo[routeName].description}
|
||||
</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</Animated.ScrollView>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<View style={styles.statusBarUnderlay} />
|
||||
<Animated.View
|
||||
style={[styles.statusBarUnderlay, { opacity: underlayOpacity }]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -230,4 +331,35 @@ const styles = StyleSheet.create({
|
||||
fontSize: 13,
|
||||
color: '#999',
|
||||
},
|
||||
backgroundUnderlay: {
|
||||
backgroundColor: '#673ab7',
|
||||
position: 'absolute',
|
||||
top: -100,
|
||||
height: 300,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
bannerContainer: {
|
||||
// backgroundColor: '#673ab7',
|
||||
alignItems: 'center',
|
||||
},
|
||||
banner: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
},
|
||||
bannerImage: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
resizeMode: 'contain',
|
||||
tintColor: '#fff',
|
||||
margin: 8,
|
||||
},
|
||||
bannerTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '200',
|
||||
color: '#fff',
|
||||
marginVertical: 8,
|
||||
marginRight: 5,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import { SafeAreaView, StackNavigator } from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
const MyNavScreen = ({ navigation, banner }) => (
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<ScrollView>
|
||||
<SafeAreaView
|
||||
forceInset={{
|
||||
top: navigation.state.routeName === 'HeaderTest' ? 'always' : 'never',
|
||||
|
||||
@@ -19,14 +19,12 @@ type MyNavScreenProps = {
|
||||
|
||||
class MyBackButton extends React.Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<Button onPress={this._navigateBack} title="Custom Back" />
|
||||
);
|
||||
return <Button onPress={this._navigateBack} title="Custom Back" />;
|
||||
}
|
||||
|
||||
_navigateBack = () => {
|
||||
this.props.navigation.goBack(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const MyBackButtonWithNavigation = withNavigation(MyBackButton);
|
||||
@@ -108,7 +106,7 @@ type MyPhotosScreenProps = {
|
||||
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Photos',
|
||||
headerLeft: <MyBackButtonWithNavigation />
|
||||
headerLeft: <MyBackButtonWithNavigation />,
|
||||
};
|
||||
_s0: NavigationEventSubscription;
|
||||
_s1: NavigationEventSubscription;
|
||||
@@ -180,18 +178,20 @@ MyProfileScreen.navigationOptions = props => {
|
||||
};
|
||||
};
|
||||
|
||||
const SimpleStack = StackNavigator({
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Profile: {
|
||||
path: 'people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
Photos: {
|
||||
path: 'photos/:name',
|
||||
screen: MyPhotosScreen,
|
||||
},
|
||||
});
|
||||
const SimpleStack = StackNavigator(
|
||||
{
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Profile: {
|
||||
path: 'people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
Photos: {
|
||||
path: 'photos/:name',
|
||||
screen: MyPhotosScreen,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default SimpleStack;
|
||||
|
||||
@@ -163,6 +163,8 @@ const SimpleTabs = TabNavigator(
|
||||
},
|
||||
},
|
||||
{
|
||||
lazy: true,
|
||||
removeClippedSubviews: true,
|
||||
tabBarOptions: {
|
||||
activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
|
||||
},
|
||||
|
||||
68
examples/NavigationPlayground/js/StackWithHeaderPreset.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
import type { NavigationScreenProp } from 'react-navigation';
|
||||
|
||||
import * as React from 'react';
|
||||
import { Button, ScrollView, StatusBar } from 'react-native';
|
||||
import { StackNavigator, SafeAreaView } from 'react-navigation';
|
||||
|
||||
type NavScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
};
|
||||
|
||||
class HomeScreen extends React.Component<NavScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button
|
||||
onPress={() => navigation.push('Other')}
|
||||
title="Push another screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OtherScreen extends React.Component<NavScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Your title here',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ paddingTop: 30 }}>
|
||||
<Button
|
||||
onPress={() => navigation.push('Other')}
|
||||
title="Push another screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const StackWithHeaderPreset = StackNavigator(
|
||||
{
|
||||
Home: HomeScreen,
|
||||
Other: OtherScreen,
|
||||
},
|
||||
{
|
||||
headerTransitionPreset: 'uikit',
|
||||
}
|
||||
);
|
||||
|
||||
export default StackWithHeaderPreset;
|
||||
101
examples/NavigationPlayground/js/StacksWithKeys.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import { Button, StatusBar, Text, View } from 'react-native';
|
||||
import { StackNavigator } from 'react-navigation';
|
||||
|
||||
class HomeScreen extends React.Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Home</Text>
|
||||
<Button
|
||||
title="Navigate to 'Profile' with key 'A'"
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({
|
||||
routeName: 'Profile',
|
||||
key: 'A',
|
||||
params: { homeKey: this.props.navigation.state.key },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title="Go back to other examples"
|
||||
onPress={() => this.props.navigation.goBack(null)}
|
||||
/>
|
||||
<StatusBar barStyle="default" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileScreen extends React.Component<any, any> {
|
||||
render() {
|
||||
const { homeKey } = this.props.navigation.state.params;
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Profile</Text>
|
||||
<Button
|
||||
title="Navigate to 'Settings' with key 'B'"
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({
|
||||
routeName: 'Settings',
|
||||
key: 'B',
|
||||
params: { homeKey },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title={`Navigate back to 'Home' with key ${homeKey}`}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({ routeName: 'Home', key: homeKey })
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsScreen extends React.Component<any, any> {
|
||||
render() {
|
||||
const { homeKey } = this.props.navigation.state.params;
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Text>Settings</Text>
|
||||
<Button
|
||||
title={`Navigate back to 'Home' with key ${homeKey}`}
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({ routeName: 'Home', key: homeKey })
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
title="Navigate back to 'Profile' with key 'A'"
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate({
|
||||
routeName: 'Profile',
|
||||
key: 'A',
|
||||
})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Stack = StackNavigator(
|
||||
{
|
||||
Home: {
|
||||
screen: HomeScreen,
|
||||
},
|
||||
Profile: {
|
||||
screen: ProfileScreen,
|
||||
},
|
||||
Settings: {
|
||||
screen: SettingsScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
headerMode: 'none',
|
||||
}
|
||||
);
|
||||
|
||||
export default Stack;
|
||||
66
examples/NavigationPlayground/js/TabsWithNavigationFocus.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { SafeAreaView, Text } from 'react-native';
|
||||
import { TabNavigator, withNavigationFocus } from 'react-navigation';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
|
||||
import SampleText from './SampleText';
|
||||
|
||||
const createTabScreen = (name, icon, focusedIcon, tintColor = '#673ab7') => {
|
||||
const TabScreen = ({ isFocused }) => (
|
||||
<SafeAreaView
|
||||
forceInset={{ horizontal: 'always', top: 'always' }}
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontWeight: '700', fontSize: 16, marginBottom: 5 }}>
|
||||
{'Tab ' + name.toLowerCase()}
|
||||
</Text>
|
||||
<Text>{'props.isFocused: ' + (isFocused ? ' true' : 'false')}</Text>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
TabScreen.navigationOptions = {
|
||||
tabBarLabel: name,
|
||||
tabBarIcon: ({ tintColor, focused }) => (
|
||||
<MaterialCommunityIcons
|
||||
name={focused ? focusedIcon : icon}
|
||||
size={26}
|
||||
style={{ color: focused ? tintColor : '#ccc' }}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return withNavigationFocus(TabScreen);
|
||||
};
|
||||
|
||||
const TabsWithNavigationFocus = TabNavigator(
|
||||
{
|
||||
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'
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
tabBarPosition: 'bottom',
|
||||
animationEnabled: true,
|
||||
swipeEnabled: true,
|
||||
}
|
||||
);
|
||||
|
||||
export default TabsWithNavigationFocus;
|
||||
@@ -568,9 +568,9 @@ babel-jest@^21.0.0, babel-jest@^21.2.0:
|
||||
babel-plugin-istanbul "^4.0.0"
|
||||
babel-preset-jest "^21.2.0"
|
||||
|
||||
babel-jest@^22.1.0, babel-jest@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.2.0.tgz#2d04087f5d149585e14f641d529551963fc9b4f8"
|
||||
babel-jest@^22.1.0, babel-jest@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.2.2.tgz#eda38dca284e32cc5257f96a9b51351975de4e04"
|
||||
dependencies:
|
||||
babel-plugin-istanbul "^4.1.5"
|
||||
babel-preset-jest "^22.2.0"
|
||||
@@ -1217,8 +1217,8 @@ bplist-parser@0.1.1:
|
||||
big-integer "^1.6.7"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
@@ -2093,9 +2093,9 @@ expect@^21.2.1:
|
||||
jest-message-util "^21.2.1"
|
||||
jest-regex-util "^21.2.0"
|
||||
|
||||
expect@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-22.2.0.tgz#dddcaab2e22ccc9f51e7c1732e0aa723f2f1f2b8"
|
||||
expect@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-22.2.2.tgz#6cb6ae2eeb651a4187b9096de70333a018fab63f"
|
||||
dependencies:
|
||||
ansi-styles "^3.2.0"
|
||||
jest-diff "^22.1.0"
|
||||
@@ -3212,9 +3212,9 @@ jest-cli@^21.2.1:
|
||||
worker-farm "^1.3.1"
|
||||
yargs "^9.0.0"
|
||||
|
||||
jest-cli@^22.2.1:
|
||||
version "22.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.2.1.tgz#f1df6675cd719e0773be77d6314c5f4b1d8af47f"
|
||||
jest-cli@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.2.2.tgz#4431a93a29549da5dcb6d4a41dd03503c9198cd6"
|
||||
dependencies:
|
||||
ansi-escapes "^3.0.0"
|
||||
chalk "^2.0.1"
|
||||
@@ -3228,18 +3228,18 @@ jest-cli@^22.2.1:
|
||||
istanbul-lib-instrument "^1.8.0"
|
||||
istanbul-lib-source-maps "^1.2.1"
|
||||
jest-changed-files "^22.2.0"
|
||||
jest-config "^22.2.1"
|
||||
jest-environment-jsdom "^22.2.0"
|
||||
jest-config "^22.2.2"
|
||||
jest-environment-jsdom "^22.2.2"
|
||||
jest-get-type "^22.1.0"
|
||||
jest-haste-map "^22.2.0"
|
||||
jest-haste-map "^22.2.2"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-regex-util "^22.1.0"
|
||||
jest-resolve-dependencies "^22.1.0"
|
||||
jest-runner "^22.2.1"
|
||||
jest-runtime "^22.2.1"
|
||||
jest-runner "^22.2.2"
|
||||
jest-runtime "^22.2.2"
|
||||
jest-snapshot "^22.2.0"
|
||||
jest-util "^22.2.0"
|
||||
jest-worker "^22.2.0"
|
||||
jest-util "^22.2.2"
|
||||
jest-worker "^22.2.2"
|
||||
micromatch "^2.3.11"
|
||||
node-notifier "^5.2.1"
|
||||
realpath-native "^1.0.0"
|
||||
@@ -3266,20 +3266,20 @@ jest-config@^21.2.1:
|
||||
jest-validate "^21.2.1"
|
||||
pretty-format "^21.2.1"
|
||||
|
||||
jest-config@^22.2.1:
|
||||
version "22.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.2.1.tgz#8617e99cff0544f0a5f254a5dde37f43b5383934"
|
||||
jest-config@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.2.2.tgz#6b8ed615bc51239847d15460086f174dad4a7015"
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
glob "^7.1.1"
|
||||
jest-environment-jsdom "^22.2.0"
|
||||
jest-environment-node "^22.2.0"
|
||||
jest-environment-jsdom "^22.2.2"
|
||||
jest-environment-node "^22.2.2"
|
||||
jest-get-type "^22.1.0"
|
||||
jest-jasmine2 "^22.2.1"
|
||||
jest-jasmine2 "^22.2.2"
|
||||
jest-regex-util "^22.1.0"
|
||||
jest-resolve "^22.2.0"
|
||||
jest-util "^22.2.0"
|
||||
jest-validate "^22.2.0"
|
||||
jest-resolve "^22.2.2"
|
||||
jest-util "^22.2.2"
|
||||
jest-validate "^22.2.2"
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-diff@^21.2.1:
|
||||
@@ -3310,9 +3310,9 @@ jest-docblock@^21.2.0:
|
||||
version "21.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
|
||||
|
||||
jest-docblock@^22.1.0, jest-docblock@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.2.0.tgz#4d054eac354751e94a43a0ea2e2fe5c04cc61bbb"
|
||||
jest-docblock@^22.1.0, jest-docblock@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.2.2.tgz#617f13edb16ec64202002b3c336cd14ae36c0631"
|
||||
dependencies:
|
||||
detect-newline "^2.1.0"
|
||||
|
||||
@@ -3324,12 +3324,12 @@ jest-environment-jsdom@^21.2.1:
|
||||
jest-util "^21.2.1"
|
||||
jsdom "^9.12.0"
|
||||
|
||||
jest-environment-jsdom@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.2.0.tgz#e9537400cbdef2d1e61d7196f8afa40e826fe9d8"
|
||||
jest-environment-jsdom@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.2.2.tgz#3513ccdccc2bc41daf9cdee199b7069b0d9feebc"
|
||||
dependencies:
|
||||
jest-mock "^22.2.0"
|
||||
jest-util "^22.2.0"
|
||||
jest-util "^22.2.2"
|
||||
jsdom "^11.5.1"
|
||||
|
||||
jest-environment-node@^21.2.1:
|
||||
@@ -3339,12 +3339,12 @@ jest-environment-node@^21.2.1:
|
||||
jest-mock "^21.2.0"
|
||||
jest-util "^21.2.1"
|
||||
|
||||
jest-environment-node@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.2.0.tgz#ba7d0183fac076d34867367a4ac53ced69e3d3a9"
|
||||
jest-environment-node@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.2.2.tgz#570896eef2dd0f939c71bd5712ef4321958c1270"
|
||||
dependencies:
|
||||
jest-mock "^22.2.0"
|
||||
jest-util "^22.2.0"
|
||||
jest-util "^22.2.2"
|
||||
|
||||
jest-expo@^25.1.0:
|
||||
version "25.1.0"
|
||||
@@ -3385,14 +3385,14 @@ jest-haste-map@^21.2.0:
|
||||
sane "^2.0.0"
|
||||
worker-farm "^1.3.1"
|
||||
|
||||
jest-haste-map@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.2.0.tgz#c9f508b8f63322490339ba02343dd688474d9ad5"
|
||||
jest-haste-map@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.2.2.tgz#9d3d5a14bd5e05ab9176979f2a5fbb4ddc80eb20"
|
||||
dependencies:
|
||||
fb-watchman "^2.0.0"
|
||||
graceful-fs "^4.1.11"
|
||||
jest-docblock "^22.2.0"
|
||||
jest-worker "^22.2.0"
|
||||
jest-docblock "^22.2.2"
|
||||
jest-worker "^22.2.2"
|
||||
micromatch "^2.3.11"
|
||||
sane "^2.0.0"
|
||||
|
||||
@@ -3409,14 +3409,14 @@ jest-jasmine2@^21.2.1:
|
||||
jest-snapshot "^21.2.1"
|
||||
p-cancelable "^0.3.0"
|
||||
|
||||
jest-jasmine2@^22.2.1:
|
||||
version "22.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.2.1.tgz#58d115f3f4a0a186b5e90c5db8ac68aecfc41051"
|
||||
jest-jasmine2@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.2.2.tgz#9065255c8f635ae9dfa33fc66068f59adf53c9aa"
|
||||
dependencies:
|
||||
callsites "^2.0.0"
|
||||
chalk "^2.0.1"
|
||||
co "^4.6.0"
|
||||
expect "^22.2.0"
|
||||
expect "^22.2.2"
|
||||
graceful-fs "^4.1.11"
|
||||
is-generator-fn "^1.0.0"
|
||||
jest-diff "^22.1.0"
|
||||
@@ -3501,9 +3501,9 @@ jest-resolve@^21.2.0:
|
||||
chalk "^2.0.1"
|
||||
is-builtin-module "^1.0.0"
|
||||
|
||||
jest-resolve@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.2.0.tgz#25aa8b887b31ab8c79763503e209d7c136f74ab1"
|
||||
jest-resolve@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.2.2.tgz#6f49d91e3680c86a4d5e5f72ccdab3996d1cbc19"
|
||||
dependencies:
|
||||
browser-resolve "^1.11.2"
|
||||
chalk "^2.0.1"
|
||||
@@ -3523,20 +3523,20 @@ jest-runner@^21.2.1:
|
||||
throat "^4.0.0"
|
||||
worker-farm "^1.3.1"
|
||||
|
||||
jest-runner@^22.2.1:
|
||||
version "22.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.2.1.tgz#539b2b7eb0ceb34e63a1ca78a1eda46ace70b940"
|
||||
jest-runner@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.2.2.tgz#17fff27a61b63b58cf104c9cdcc0fdfccd3878ce"
|
||||
dependencies:
|
||||
exit "^0.1.2"
|
||||
jest-config "^22.2.1"
|
||||
jest-docblock "^22.2.0"
|
||||
jest-haste-map "^22.2.0"
|
||||
jest-jasmine2 "^22.2.1"
|
||||
jest-config "^22.2.2"
|
||||
jest-docblock "^22.2.2"
|
||||
jest-haste-map "^22.2.2"
|
||||
jest-jasmine2 "^22.2.2"
|
||||
jest-leak-detector "^22.1.0"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-runtime "^22.2.1"
|
||||
jest-util "^22.2.0"
|
||||
jest-worker "^22.2.0"
|
||||
jest-runtime "^22.2.2"
|
||||
jest-util "^22.2.2"
|
||||
jest-worker "^22.2.2"
|
||||
throat "^4.0.0"
|
||||
|
||||
jest-runtime@^21.2.1:
|
||||
@@ -3561,22 +3561,22 @@ jest-runtime@^21.2.1:
|
||||
write-file-atomic "^2.1.0"
|
||||
yargs "^9.0.0"
|
||||
|
||||
jest-runtime@^22.2.1:
|
||||
version "22.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.2.1.tgz#c5b0173a7f5274b28da30019cf7bb7b8cda68040"
|
||||
jest-runtime@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.2.2.tgz#256d0efb65deae1c23b819d88cec5ab43d7a4ed6"
|
||||
dependencies:
|
||||
babel-core "^6.0.0"
|
||||
babel-jest "^22.2.0"
|
||||
babel-jest "^22.2.2"
|
||||
babel-plugin-istanbul "^4.1.5"
|
||||
chalk "^2.0.1"
|
||||
convert-source-map "^1.4.0"
|
||||
exit "^0.1.2"
|
||||
graceful-fs "^4.1.11"
|
||||
jest-config "^22.2.1"
|
||||
jest-haste-map "^22.2.0"
|
||||
jest-config "^22.2.2"
|
||||
jest-haste-map "^22.2.2"
|
||||
jest-regex-util "^22.1.0"
|
||||
jest-resolve "^22.2.0"
|
||||
jest-util "^22.2.0"
|
||||
jest-resolve "^22.2.2"
|
||||
jest-util "^22.2.2"
|
||||
json-stable-stringify "^1.0.1"
|
||||
micromatch "^2.3.11"
|
||||
realpath-native "^1.0.0"
|
||||
@@ -3619,16 +3619,16 @@ jest-util@^21.2.1:
|
||||
jest-validate "^21.2.1"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
jest-util@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.2.0.tgz#afad693641447858bec7b37f32952516bf887262"
|
||||
jest-util@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.2.2.tgz#335484b6aeae0c5a1ae498401630324977fe3465"
|
||||
dependencies:
|
||||
callsites "^2.0.0"
|
||||
chalk "^2.0.1"
|
||||
graceful-fs "^4.1.11"
|
||||
is-ci "^1.0.10"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-validate "^22.2.0"
|
||||
jest-validate "^22.2.2"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
jest-validate@^21.2.1:
|
||||
@@ -3640,9 +3640,9 @@ jest-validate@^21.2.1:
|
||||
leven "^2.1.0"
|
||||
pretty-format "^21.2.1"
|
||||
|
||||
jest-validate@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.2.0.tgz#f7ce459105a8210825e5e57279f868ab080063fa"
|
||||
jest-validate@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.2.2.tgz#9cdce422c93cc28395e907ac6bbc929158d9a6ba"
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
jest-get-type "^22.1.0"
|
||||
@@ -3655,9 +3655,9 @@ jest-worker@22.1.0:
|
||||
dependencies:
|
||||
merge-stream "^1.0.1"
|
||||
|
||||
jest-worker@^22.1.0, jest-worker@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.2.0.tgz#d88d6ee176d6409f206cbbf7b485250793264262"
|
||||
jest-worker@^22.1.0, jest-worker@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.2.2.tgz#c1f5dc39976884b81f68ec50cb8532b2cbab3390"
|
||||
dependencies:
|
||||
merge-stream "^1.0.1"
|
||||
|
||||
@@ -3668,11 +3668,11 @@ jest@^21.0.1:
|
||||
jest-cli "^21.2.1"
|
||||
|
||||
jest@^22.1.1:
|
||||
version "22.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-22.2.1.tgz#fb6524d35bd02968afe3b17f330d6f7207846147"
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-22.2.2.tgz#26aca0f5e4eaa76d52f2792b14033a3d1e7be2bd"
|
||||
dependencies:
|
||||
import-local "^1.0.0"
|
||||
jest-cli "^22.2.1"
|
||||
jest-cli "^22.2.2"
|
||||
|
||||
joi@^10.0.2:
|
||||
version "10.6.0"
|
||||
@@ -4156,10 +4156,6 @@ lru-memoizer@^1.11.1:
|
||||
lru-cache "~4.0.0"
|
||||
very-fast-args "^1.1.0"
|
||||
|
||||
lsmod@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lsmod/-/lsmod-1.0.0.tgz#9a00f76dca36eb23fa05350afe1b585d4299e64b"
|
||||
|
||||
macos-release@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-1.1.0.tgz#831945e29365b470aa8724b0ab36c8f8959d10fb"
|
||||
@@ -4986,9 +4982,9 @@ probe-image-size@^3.1.0:
|
||||
next-tick "^1.0.0"
|
||||
stream-parser "~0.3.1"
|
||||
|
||||
process-nextick-args@~1.0.6:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
|
||||
process@~0.5.1:
|
||||
version "0.5.2"
|
||||
@@ -5099,11 +5095,10 @@ raven-js@^3.17.0:
|
||||
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.2.tgz#85785928ebd664049e54efd0db8ff28da0cbb374"
|
||||
|
||||
raven@^2.1.1:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/raven/-/raven-2.4.0.tgz#49b7d5f838e5893f31dd72f82d05a35e42203f60"
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raven/-/raven-2.4.1.tgz#7a6a6ff1c42d0a3892308f44c94273e7f88677fd"
|
||||
dependencies:
|
||||
cookie "0.3.1"
|
||||
lsmod "1.0.0"
|
||||
md5 "^2.2.1"
|
||||
stack-trace "0.0.9"
|
||||
timed-out "4.0.1"
|
||||
@@ -5398,13 +5393,13 @@ readable-stream@1.1.x, readable-stream@^1.1.8, readable-stream@~1.1.8, readable-
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@2, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~1.0.6"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.0.3"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
## Usage
|
||||
|
||||
Please see the [Contributors Guide](https://reactnavigation.org/docs/guides/contributors#Run-the-Example-App) for instructions on running these example apps.
|
||||
Please see the [Contributors Guide](https://reactnavigation.org/docs/contributing.html#run-the-example-app) for instructions on running these example apps.
|
||||
|
||||
17
flow/react-navigation.js
vendored
@@ -332,12 +332,15 @@ declare module 'react-navigation' {
|
||||
navigationOptions?: ?NavigationScreenConfig<Options>,
|
||||
};
|
||||
|
||||
declare export type NavigationRouteConfig = {
|
||||
navigationOptions?: NavigationScreenConfig<*>,
|
||||
path?: string,
|
||||
} & NavigationScreenRouteConfig;
|
||||
declare export type NavigationRouteConfig =
|
||||
| NavigationComponent
|
||||
| ({
|
||||
navigationOptions?: NavigationScreenConfig<*>,
|
||||
path?: string,
|
||||
} & NavigationScreenRouteConfig);
|
||||
|
||||
declare export type NavigationScreenRouteConfig =
|
||||
| NavigationComponent
|
||||
| {
|
||||
screen: NavigationComponent,
|
||||
}
|
||||
@@ -405,6 +408,7 @@ declare module 'react-navigation' {
|
||||
declare export type NavigationStackViewConfig = {|
|
||||
mode?: 'card' | 'modal',
|
||||
headerMode?: HeaderMode,
|
||||
headerTransitionPreset?: 'fade-in-place' | 'uikit',
|
||||
cardStyle?: ViewStyleProp,
|
||||
transitionConfig?: () => TransitionConfig,
|
||||
onTransitionStart?: () => void,
|
||||
@@ -829,6 +833,8 @@ declare module 'react-navigation' {
|
||||
declare type _TabNavigatorConfig = {|
|
||||
...NavigationTabRouterConfig,
|
||||
..._TabViewConfig,
|
||||
lazy?: boolean,
|
||||
removeClippedSubviews?: boolean,
|
||||
containerOptions?: void,
|
||||
|};
|
||||
declare export function TabNavigator(
|
||||
@@ -1099,4 +1105,7 @@ declare module 'react-navigation' {
|
||||
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>;
|
||||
}
|
||||
|
||||
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.2",
|
||||
"description": "Routing and navigation for your React Native apps",
|
||||
"main": "src/react-navigation.js",
|
||||
"repository": {
|
||||
@@ -33,7 +33,7 @@
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react-native-drawer-layout-polyfill": "^1.3.2",
|
||||
"react-native-safe-area-view": "^0.6.0",
|
||||
"react-native-safe-area-view": "^0.7.0",
|
||||
"react-native-tab-view": "^0.0.74"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -72,6 +72,10 @@
|
||||
"modulePathIgnorePatterns": ["examples"]
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": ["eslint --fix", "git add"]
|
||||
"*.js": [
|
||||
"eslint --fix",
|
||||
"prettier --write flow/react-navigation.js",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
BackAndroid as DeprecatedBackAndroid,
|
||||
BackHandler as ModernBackHandler,
|
||||
Linking,
|
||||
MaskedViewIOS,
|
||||
} from 'react-native';
|
||||
|
||||
const BackHandler = ModernBackHandler || DeprecatedBackAndroid;
|
||||
|
||||
export { BackHandler, Linking };
|
||||
export { BackHandler, MaskedViewIOS };
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
export const Linking = {
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {},
|
||||
getInitialURL: () => Promise.reject('Unsupported platform'),
|
||||
};
|
||||
import React from 'react';
|
||||
import { BackHandler, View } from 'react-native';
|
||||
|
||||
export const BackHandler = {
|
||||
addEventListener: () => {},
|
||||
};
|
||||
const MaskedViewIOS = () => <View>{this.props.children}</View>;
|
||||
|
||||
export { BackHandler, MaskedViewIOS };
|
||||
|
||||
@@ -76,4 +76,43 @@ describe('addNavigationHelpers', () => {
|
||||
});
|
||||
expect(mockedDispatch.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('handles GetParams action', () => {
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { key: 'B', routeName: 'Settings', params: { name: 'Peter' } },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).getParam('name', 'Brent')
|
||||
).toEqual('Peter');
|
||||
});
|
||||
|
||||
it('handles GetParams action with default param', () => {
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { key: 'B', routeName: 'Settings' },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).getParam('name', 'Brent')
|
||||
).toEqual('Brent');
|
||||
});
|
||||
|
||||
it('handles GetParams action with param value as null', () => {
|
||||
const mockedDispatch = jest
|
||||
.fn(() => false)
|
||||
.mockImplementationOnce(() => true);
|
||||
expect(
|
||||
addNavigationHelpers({
|
||||
state: { key: 'B', routeName: 'Settings', params: { name: null } },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).getParam('name')
|
||||
).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,16 @@ export default function(navigation) {
|
||||
return navigation.dispatch(NavigationActions.setParams({ params, key }));
|
||||
},
|
||||
|
||||
getParam: (paramName, defaultValue) => {
|
||||
const params = navigation.state.params;
|
||||
|
||||
if (params && paramName in params) {
|
||||
return params[paramName];
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
push: (routeName, params, action) =>
|
||||
navigation.dispatch(
|
||||
NavigationActions.push({ routeName, params, action })
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { BackHandler, Linking } from './PlatformHelpers';
|
||||
import { Linking } from 'react-native';
|
||||
import { BackHandler } from './PlatformHelpers';
|
||||
import NavigationActions from './NavigationActions';
|
||||
import addNavigationHelpers from './addNavigationHelpers';
|
||||
import invariant from './utils/invariant';
|
||||
@@ -25,6 +26,20 @@ export default function createNavigationContainer(Component) {
|
||||
this._validateProps(props);
|
||||
|
||||
this._initialAction = NavigationActions.init();
|
||||
|
||||
if (this._isStateful()) {
|
||||
this.subs = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (!this._isMounted) {
|
||||
this.subs && this.subs.remove();
|
||||
} else {
|
||||
// dispatch returns true if the action results in a state change,
|
||||
// and false otherwise. This maps well to what BackHandler expects
|
||||
// from a callback -- true if handled, false if not handled
|
||||
return this.dispatch(NavigationActions.back());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.state = {
|
||||
nav: this._isStateful()
|
||||
? Component.router.getStateForAction(this._initialAction)
|
||||
@@ -124,14 +139,11 @@ export default function createNavigationContainer(Component) {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
if (!this._isStateful()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.subs = BackHandler.addEventListener('hardwareBackPress', () =>
|
||||
this.dispatch(NavigationActions.back())
|
||||
);
|
||||
|
||||
Linking.addEventListener('url', this._handleOpenURL);
|
||||
|
||||
Linking.getInitialURL().then(url => url && this._handleOpenURL({ url }));
|
||||
@@ -147,6 +159,7 @@ export default function createNavigationContainer(Component) {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
Linking.removeEventListener('url', this._handleOpenURL);
|
||||
this.subs && this.subs.remove();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export default (routeConfigMap, stackConfig = {}) => {
|
||||
initialRouteParams,
|
||||
paths,
|
||||
headerMode,
|
||||
headerTransitionPreset,
|
||||
mode,
|
||||
cardStyle,
|
||||
transitionConfig,
|
||||
@@ -38,6 +39,7 @@ export default (routeConfigMap, stackConfig = {}) => {
|
||||
<CardStackTransitioner
|
||||
{...props}
|
||||
headerMode={headerMode}
|
||||
headerTransitionPreset={headerTransitionPreset}
|
||||
mode={mode}
|
||||
cardStyle={cardStyle}
|
||||
transitionConfig={transitionConfig}
|
||||
|
||||
@@ -19,6 +19,8 @@ const TabNavigator = (routeConfigs, config = {}) => {
|
||||
tabBarComponent,
|
||||
tabBarPosition,
|
||||
tabBarOptions,
|
||||
lazy,
|
||||
removeClippedSubviews,
|
||||
swipeEnabled,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
@@ -31,6 +33,8 @@ const TabNavigator = (routeConfigs, config = {}) => {
|
||||
const navigator = createNavigator(router, routeConfigs, config)(props => (
|
||||
<TabView
|
||||
{...props}
|
||||
lazy={lazy}
|
||||
removeClippedSubviews={removeClippedSubviews}
|
||||
tabBarComponent={tabBarComponent}
|
||||
tabBarPosition={tabBarPosition}
|
||||
tabBarOptions={tabBarOptions}
|
||||
|
||||
@@ -79,6 +79,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
headerTransitionPreset={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
Object {
|
||||
@@ -89,12 +90,15 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
leftButtonInterpolator={[Function]}
|
||||
leftInterpolator={[Function]}
|
||||
leftLabelInterpolator={[Function]}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": [Function],
|
||||
"getParam": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
@@ -108,7 +112,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"key": "StackRouterRoot",
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-1",
|
||||
"key": "id-0-1",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
@@ -127,8 +131,10 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
titleFromLeftInterpolator={[Function]}
|
||||
titleInterpolator={[Function]}
|
||||
transitionConfig={undefined}
|
||||
transitionPreset="fade-in-place"
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
@@ -184,11 +190,6 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
"position": "absolute",
|
||||
"right": 70,
|
||||
"top": 0,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -204,7 +205,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, .9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "600",
|
||||
"fontWeight": "700",
|
||||
"marginHorizontal": 16,
|
||||
"textAlign": "center",
|
||||
}
|
||||
@@ -318,6 +319,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
headerTransitionPreset={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
Object {
|
||||
@@ -328,12 +330,15 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"width": 0,
|
||||
}
|
||||
}
|
||||
leftButtonInterpolator={[Function]}
|
||||
leftInterpolator={[Function]}
|
||||
leftLabelInterpolator={[Function]}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": [Function],
|
||||
"getParam": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
@@ -347,7 +352,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"key": "StackRouterRoot",
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-0",
|
||||
"key": "id-0-0",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
@@ -366,8 +371,10 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
titleFromLeftInterpolator={[Function]}
|
||||
titleInterpolator={[Function]}
|
||||
transitionConfig={undefined}
|
||||
transitionPreset="fade-in-place"
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
@@ -423,11 +430,6 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"translateX": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
>
|
||||
@@ -443,7 +445,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, .9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "600",
|
||||
"fontWeight": "700",
|
||||
"marginHorizontal": 16,
|
||||
"textAlign": "center",
|
||||
}
|
||||
|
||||
@@ -55,13 +55,23 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
@@ -168,6 +178,7 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
|
||||
3
src/react-navigation.js
vendored
@@ -88,4 +88,7 @@ module.exports = {
|
||||
get withNavigation() {
|
||||
return require('./views/withNavigation').default;
|
||||
},
|
||||
get withNavigationFocus() {
|
||||
return require('./views/withNavigationFocus').default;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -32,4 +32,7 @@ module.exports = {
|
||||
get withNavigation() {
|
||||
return require('./views/withNavigation').default;
|
||||
},
|
||||
get withNavigationFocus() {
|
||||
return require('./views/withNavigationFocus').default;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -51,6 +51,61 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
const pathsByRouteNames = { ...stackConfig.paths } || {};
|
||||
let paths = [];
|
||||
|
||||
function getInitialState(action) {
|
||||
let route = {};
|
||||
const childRouter = childRouters[action.routeName];
|
||||
|
||||
// This is a push-like action, and childRouter will be a router or null if we are responsible for this routeName
|
||||
if (behavesLikePushAction(action) && childRouter !== undefined) {
|
||||
let childState = {};
|
||||
// The router is null for normal leaf routes
|
||||
if (childRouter !== null) {
|
||||
const childAction =
|
||||
action.action || NavigationActions.init({ params: action.params });
|
||||
childState = childRouter.getStateForAction(childAction);
|
||||
}
|
||||
return {
|
||||
key: 'StackRouterRoot',
|
||||
isTransitioning: false,
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
params: action.params,
|
||||
...childState,
|
||||
key: action.key || generateKey(),
|
||||
routeName: action.routeName,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (initialChildRouter) {
|
||||
route = initialChildRouter.getStateForAction(
|
||||
NavigationActions.navigate({
|
||||
routeName: initialRouteName,
|
||||
params: initialRouteParams,
|
||||
})
|
||||
);
|
||||
}
|
||||
const params = (route.params || action.params || initialRouteParams) && {
|
||||
...(route.params || {}),
|
||||
...(action.params || {}),
|
||||
...(initialRouteParams || {}),
|
||||
};
|
||||
route = {
|
||||
...route,
|
||||
...(params ? { params } : {}),
|
||||
routeName: initialRouteName,
|
||||
key: action.key || generateKey(),
|
||||
};
|
||||
return {
|
||||
key: 'StackRouterRoot',
|
||||
isTransitioning: false,
|
||||
index: 0,
|
||||
routes: [route],
|
||||
};
|
||||
}
|
||||
|
||||
// Build paths for each route
|
||||
routeNames.forEach(routeName => {
|
||||
let pathPattern =
|
||||
@@ -100,55 +155,11 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
getStateForAction(action, state) {
|
||||
// Set up the initial state if needed
|
||||
if (!state) {
|
||||
let route = {};
|
||||
if (
|
||||
behavesLikePushAction(action) &&
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
return {
|
||||
key: 'StackRouterRoot',
|
||||
isTransitioning: false,
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
routeName: action.routeName,
|
||||
params: action.params,
|
||||
key: `Init-${generateKey()}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (initialChildRouter) {
|
||||
route = initialChildRouter.getStateForAction(
|
||||
NavigationActions.navigate({
|
||||
routeName: initialRouteName,
|
||||
params: initialRouteParams,
|
||||
})
|
||||
);
|
||||
}
|
||||
const params = (route.params ||
|
||||
action.params ||
|
||||
initialRouteParams) && {
|
||||
...(route.params || {}),
|
||||
...(action.params || {}),
|
||||
...(initialRouteParams || {}),
|
||||
};
|
||||
route = {
|
||||
...route,
|
||||
routeName: initialRouteName,
|
||||
key: `Init-${generateKey()}`,
|
||||
...(params ? { params } : {}),
|
||||
};
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
state = {
|
||||
key: 'StackRouterRoot',
|
||||
isTransitioning: false,
|
||||
index: 0,
|
||||
routes: [route],
|
||||
};
|
||||
return getInitialState(action);
|
||||
}
|
||||
|
||||
// Check if a child scene wants to handle the action as long as it is not a reset to the root stack
|
||||
// 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 !== NavigationActions.RESET || action.key !== null) {
|
||||
const keyIndex = action.key
|
||||
? StateUtils.indexOf(state, action.key)
|
||||
@@ -171,8 +182,131 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle explicit push navigation action. This must happen after the
|
||||
// focused child router has had a chance to handle the action.
|
||||
if (
|
||||
behavesLikePushAction(action) &&
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
const childRouter = childRouters[action.routeName];
|
||||
let route;
|
||||
|
||||
invariant(
|
||||
action.type !== NavigationActions.PUSH || action.key == null,
|
||||
'StackRouter does not support key on the push action'
|
||||
);
|
||||
|
||||
// With the navigate action, the key may be provided for pushing, or to navigate back to the key
|
||||
if (action.key) {
|
||||
const lastRouteIndex = state.routes.findIndex(
|
||||
r => r.key === action.key
|
||||
);
|
||||
if (lastRouteIndex !== -1) {
|
||||
// If index is unchanged and params are not being set, leave state identity intact
|
||||
if (state.index === lastRouteIndex && !action.params) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Remove the now unused routes at the tail of the routes array
|
||||
const routes = state.routes.slice(0, lastRouteIndex + 1);
|
||||
|
||||
// Apply params if provided, otherwise leave route identity intact
|
||||
if (action.params) {
|
||||
const route = state.routes.find(r => r.key === action.key);
|
||||
routes[lastRouteIndex] = {
|
||||
...route,
|
||||
params: {
|
||||
...route.params,
|
||||
...action.params,
|
||||
},
|
||||
};
|
||||
}
|
||||
// Return state with new index. Change isTransitioning only if index has changed
|
||||
return {
|
||||
...state,
|
||||
isTransitioning:
|
||||
state.index !== lastRouteIndex
|
||||
? action.immediate !== true
|
||||
: undefined,
|
||||
index: lastRouteIndex,
|
||||
routes,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (childRouter) {
|
||||
const childAction =
|
||||
action.action || NavigationActions.init({ params: action.params });
|
||||
route = {
|
||||
params: action.params,
|
||||
// merge the child state in this order to allow params override
|
||||
...childRouter.getStateForAction(childAction),
|
||||
routeName: action.routeName,
|
||||
key: action.key || generateKey(),
|
||||
};
|
||||
} else {
|
||||
route = {
|
||||
params: action.params,
|
||||
routeName: action.routeName,
|
||||
key: action.key || generateKey(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...StateUtils.push(state, route),
|
||||
isTransitioning: action.immediate !== true,
|
||||
};
|
||||
} else if (
|
||||
action.type === NavigationActions.PUSH &&
|
||||
childRouters[action.routeName] === undefined
|
||||
) {
|
||||
// If we've made it this far with a push action, we return the
|
||||
// state with a new identity to prevent the action from bubbling
|
||||
// back up.
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle navigation to other child routers that are not yet pushed
|
||||
if (behavesLikePushAction(action)) {
|
||||
const childRouterNames = Object.keys(childRouters);
|
||||
for (let i = 0; i < childRouterNames.length; i++) {
|
||||
const childRouterName = childRouterNames[i];
|
||||
const childRouter = childRouters[childRouterName];
|
||||
if (childRouter) {
|
||||
// For each child router, start with a blank state
|
||||
const initChildRoute = childRouter.getStateForAction(
|
||||
NavigationActions.init()
|
||||
);
|
||||
// Then check to see if the router handles our navigate action
|
||||
const navigatedChildRoute = childRouter.getStateForAction(
|
||||
action,
|
||||
initChildRoute
|
||||
);
|
||||
let routeToPush = null;
|
||||
if (navigatedChildRoute === null) {
|
||||
// Push the route if the router has 'handled' the action and returned null
|
||||
routeToPush = initChildRoute;
|
||||
} else if (navigatedChildRoute !== initChildRoute) {
|
||||
// Push the route if the state has changed in response to this navigation
|
||||
routeToPush = navigatedChildRoute;
|
||||
}
|
||||
if (routeToPush) {
|
||||
const route = {
|
||||
...routeToPush,
|
||||
routeName: childRouterName,
|
||||
key: action.key || generateKey(),
|
||||
};
|
||||
return StateUtils.push(state, route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle pop-to-top behavior. Make sure this happens after children have had a chance to handle the action, so that the inner stack pops to top first.
|
||||
if (action.type === NavigationActions.POP_TO_TOP) {
|
||||
// If we're already at the top, then we return the state with a new
|
||||
// identity so that the action is handled by this router.
|
||||
if (state.index === 0) {
|
||||
return {
|
||||
...state,
|
||||
@@ -206,90 +340,14 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
params: action.params,
|
||||
// merge the child state in this order to allow params override
|
||||
...childState,
|
||||
key: action.newKey || generateKey(),
|
||||
routeName: action.routeName,
|
||||
key: action.newKey || generateKey(),
|
||||
};
|
||||
return { ...state, routes };
|
||||
}
|
||||
}
|
||||
|
||||
// Handle explicit push navigation action. Make sure this happens after children have had a chance to handle the action
|
||||
if (
|
||||
behavesLikePushAction(action) &&
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
const childRouter = childRouters[action.routeName];
|
||||
let route;
|
||||
|
||||
invariant(
|
||||
action.type !== NavigationActions.PUSH || action.key == null,
|
||||
'StackRouter does not support key on the push action'
|
||||
);
|
||||
// With the navigate action, the key may be provided for pushing, or to navigate back to the key
|
||||
if (action.key) {
|
||||
const lastRouteIndex = state.routes.findIndex(
|
||||
r => r.key === action.key
|
||||
);
|
||||
if (lastRouteIndex !== -1) {
|
||||
// If index is unchanged and params are not being set, leave state identity intact
|
||||
if (state.index === lastRouteIndex && !action.params) {
|
||||
return state;
|
||||
}
|
||||
const routes = [...state.routes];
|
||||
// Apply params if provided, otherwise leave route identity intact
|
||||
if (action.params) {
|
||||
const route = state.routes.find(r => r.key === action.key);
|
||||
routes[lastRouteIndex] = {
|
||||
...route,
|
||||
params: {
|
||||
...route.params,
|
||||
...action.params,
|
||||
},
|
||||
};
|
||||
}
|
||||
// Return state with new index. Change isTransitioning only if index has changed
|
||||
return {
|
||||
...state,
|
||||
isTransitioning:
|
||||
state.index !== lastRouteIndex
|
||||
? action.immediate !== true
|
||||
: undefined,
|
||||
index: lastRouteIndex,
|
||||
routes,
|
||||
};
|
||||
}
|
||||
}
|
||||
const key = action.key || generateKey();
|
||||
if (childRouter) {
|
||||
const childAction =
|
||||
action.action || NavigationActions.init({ params: action.params });
|
||||
route = {
|
||||
params: action.params,
|
||||
// merge the child state in this order to allow params override
|
||||
...childRouter.getStateForAction(childAction),
|
||||
key,
|
||||
routeName: action.routeName,
|
||||
};
|
||||
} else {
|
||||
route = {
|
||||
params: action.params,
|
||||
key,
|
||||
routeName: action.routeName,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...StateUtils.push(state, route),
|
||||
isTransitioning: action.immediate !== true,
|
||||
};
|
||||
} else if (
|
||||
action.type === NavigationActions.PUSH &&
|
||||
childRouters[action.routeName] === undefined
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
}
|
||||
|
||||
// Update transitioning state
|
||||
if (
|
||||
action.type === NavigationActions.COMPLETE_TRANSITION &&
|
||||
(action.key == null || action.key === state.key) &&
|
||||
@@ -301,41 +359,6 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
};
|
||||
}
|
||||
|
||||
// Handle navigation to other child routers that are not yet pushed
|
||||
if (behavesLikePushAction(action)) {
|
||||
const childRouterNames = Object.keys(childRouters);
|
||||
for (let i = 0; i < childRouterNames.length; i++) {
|
||||
const childRouterName = childRouterNames[i];
|
||||
const childRouter = childRouters[childRouterName];
|
||||
if (childRouter) {
|
||||
// For each child router, start with a blank state
|
||||
const initChildRoute = childRouter.getStateForAction(
|
||||
NavigationActions.init()
|
||||
);
|
||||
// Then check to see if the router handles our navigate action
|
||||
const navigatedChildRoute = childRouter.getStateForAction(
|
||||
action,
|
||||
initChildRoute
|
||||
);
|
||||
let routeToPush = null;
|
||||
if (navigatedChildRoute === null) {
|
||||
// Push the route if the router has 'handled' the action and returned null
|
||||
routeToPush = initChildRoute;
|
||||
} else if (navigatedChildRoute !== initChildRoute) {
|
||||
// Push the route if the state has changed in response to this navigation
|
||||
routeToPush = navigatedChildRoute;
|
||||
}
|
||||
if (routeToPush) {
|
||||
return StateUtils.push(state, {
|
||||
...routeToPush,
|
||||
key: generateKey(),
|
||||
routeName: childRouterName,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action.type === NavigationActions.SET_PARAMS) {
|
||||
const key = action.key;
|
||||
const lastRoute = state.routes.find(route => route.key === key);
|
||||
@@ -369,20 +392,16 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
...state,
|
||||
routes: resetAction.actions.map(childAction => {
|
||||
const router = childRouters[childAction.routeName];
|
||||
let childState = {};
|
||||
if (router) {
|
||||
return {
|
||||
...childAction,
|
||||
...router.getStateForAction(childAction),
|
||||
routeName: childAction.routeName,
|
||||
key: generateKey(),
|
||||
};
|
||||
childState = router.getStateForAction(childAction);
|
||||
}
|
||||
const route = {
|
||||
...childAction,
|
||||
key: generateKey(),
|
||||
return {
|
||||
params: childAction.params,
|
||||
...childState,
|
||||
routeName: childAction.routeName,
|
||||
key: childAction.key || generateKey(),
|
||||
};
|
||||
delete route.type;
|
||||
return route;
|
||||
}),
|
||||
index: action.index,
|
||||
};
|
||||
@@ -521,7 +540,15 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
}
|
||||
const nextResult = result || {};
|
||||
const paramName = key.name;
|
||||
nextResult[paramName] = matchResult;
|
||||
|
||||
let decodedMatchResult;
|
||||
try {
|
||||
decodedMatchResult = decodeURIComponent(matchResult);
|
||||
} catch (e) {
|
||||
// ignore `URIError: malformed URI`
|
||||
}
|
||||
|
||||
nextResult[paramName] = decodedMatchResult || matchResult;
|
||||
return nextResult;
|
||||
}, queryParams);
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@ export default (routeConfigs, config = {}) => {
|
||||
paths[routeName] =
|
||||
typeof routeConfig.path === 'string' ? routeConfig.path : routeName;
|
||||
tabRouters[routeName] = null;
|
||||
if (routeConfig.screen && routeConfig.screen.router) {
|
||||
tabRouters[routeName] = routeConfig.screen.router;
|
||||
const screen = getScreenForRouteName(routeConfigs, routeName);
|
||||
if (screen.router) {
|
||||
tabRouters[routeName] = screen.router;
|
||||
}
|
||||
});
|
||||
if (initialRouteIndex === -1) {
|
||||
@@ -239,7 +240,7 @@ export default (routeConfigs, config = {}) => {
|
||||
},
|
||||
|
||||
getComponentForState(state) {
|
||||
const routeName = order[state.index];
|
||||
const routeName = state.routes[state.index].routeName;
|
||||
invariant(
|
||||
routeName,
|
||||
`There is no route defined for index ${state.index}. Check that
|
||||
|
||||
@@ -7,7 +7,6 @@ import TabRouter from '../TabRouter';
|
||||
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import addNavigationHelpers from '../../addNavigationHelpers';
|
||||
|
||||
import { _TESTING_ONLY_normalize_keys } from '../KeyGenerator';
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -19,7 +18,7 @@ const ROUTERS = {
|
||||
StackRouter,
|
||||
};
|
||||
|
||||
const dummyEventSubscriber = (name: string, handler: (*) => void) => ({
|
||||
const dummyEventSubscriber = (name, handler) => ({
|
||||
remove: () => {},
|
||||
});
|
||||
|
||||
@@ -111,8 +110,8 @@ test('Handles no-op actions with tabs within stack router', () => {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Qux',
|
||||
});
|
||||
expect(state1.routes[0].key).toEqual('Init-id-0');
|
||||
expect(state2.routes[0].key).toEqual('Init-id-1');
|
||||
expect(state1.routes[0].key).toEqual('id-0');
|
||||
expect(state2.routes[0].key).toEqual('id-1');
|
||||
state1.routes[0].key = state2.routes[0].key;
|
||||
expect(state1).toEqual(state2);
|
||||
const state3 = TestRouter.getStateForAction(
|
||||
@@ -140,7 +139,7 @@ test('Handles deep action', () => {
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Bar',
|
||||
},
|
||||
],
|
||||
@@ -180,8 +179,8 @@ test('Supports lazily-evaluated getScreen', () => {
|
||||
immediate: true,
|
||||
routeName: 'Qux',
|
||||
});
|
||||
expect(state1.routes[0].key).toEqual('Init-id-0');
|
||||
expect(state2.routes[0].key).toEqual('Init-id-1');
|
||||
expect(state1.routes[0].key).toEqual('id-0');
|
||||
expect(state2.routes[0].key).toEqual('id-1');
|
||||
state1.routes[0].key = state2.routes[0].key;
|
||||
expect(state1).toEqual(state2);
|
||||
const state3 = TestRouter.getStateForAction(
|
||||
|
||||
@@ -355,7 +355,7 @@ describe('StackRouter', () => {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'StackRouterRoot',
|
||||
routes: [{ key: 'Init-id-0', routeName: 'foo' }],
|
||||
routes: [{ key: 'id-0', routeName: 'foo' }],
|
||||
});
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'qux' }),
|
||||
@@ -513,6 +513,40 @@ describe('StackRouter', () => {
|
||||
expect(pushedTwiceState.routes[2].routeName).toEqual('bar');
|
||||
});
|
||||
|
||||
test('Navigate from top propagates to any arbitary depth of stacks', () => {
|
||||
const GrandChildNavigator = () => <div />;
|
||||
GrandChildNavigator.router = StackRouter({
|
||||
Quux: { screen: () => <div /> },
|
||||
Corge: { screen: () => <div /> },
|
||||
});
|
||||
|
||||
const ChildNavigator = () => <div />;
|
||||
ChildNavigator.router = StackRouter({
|
||||
Baz: { screen: () => <div /> },
|
||||
Woo: { screen: () => <div /> },
|
||||
Qux: { screen: GrandChildNavigator },
|
||||
});
|
||||
|
||||
const Parent = StackRouter({
|
||||
Foo: { screen: () => <div /> },
|
||||
Bar: { screen: ChildNavigator },
|
||||
});
|
||||
|
||||
const state = Parent.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = Parent.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Corge',
|
||||
},
|
||||
state
|
||||
);
|
||||
|
||||
expect(state2.index).toEqual(1);
|
||||
expect(state2.routes[1].index).toEqual(1);
|
||||
expect(state2.routes[1].routes[1].index).toEqual(1);
|
||||
expect(state2.routes[1].routes[1].routes[1].routeName).toEqual('Corge');
|
||||
});
|
||||
|
||||
test('Navigate with key is idempotent', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
@@ -553,6 +587,37 @@ describe('StackRouter', () => {
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('Navigate backwards with key removes leading routes', () => {
|
||||
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
|
||||
);
|
||||
const pushedTwiceState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'bar', key: 'b`' }),
|
||||
pushedState
|
||||
);
|
||||
const pushedThriceState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({ routeName: 'foo', key: 'c`' }),
|
||||
pushedTwiceState
|
||||
);
|
||||
expect(pushedThriceState.routes.length).toEqual(4);
|
||||
|
||||
const navigatedBackToFirstRouteState = TestRouter.getStateForAction(
|
||||
NavigationActions.navigate({
|
||||
routeName: 'foo',
|
||||
key: pushedThriceState.routes[0].key,
|
||||
}),
|
||||
pushedThriceState
|
||||
);
|
||||
expect(navigatedBackToFirstRouteState.index).toEqual(0);
|
||||
expect(navigatedBackToFirstRouteState.routes.length).toEqual(1);
|
||||
});
|
||||
|
||||
test('Handle basic stack logic for plain components', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
@@ -571,7 +636,7 @@ describe('StackRouter', () => {
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Foo',
|
||||
},
|
||||
],
|
||||
@@ -599,7 +664,7 @@ describe('StackRouter', () => {
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Foo',
|
||||
},
|
||||
],
|
||||
@@ -696,7 +761,7 @@ describe('StackRouter', () => {
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Foo',
|
||||
},
|
||||
],
|
||||
@@ -724,7 +789,7 @@ describe('StackRouter', () => {
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Foo',
|
||||
},
|
||||
],
|
||||
@@ -798,7 +863,7 @@ describe('StackRouter', () => {
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Bar',
|
||||
},
|
||||
],
|
||||
@@ -907,14 +972,14 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'foobar' },
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].routes[0].routes).toEqual([
|
||||
{
|
||||
key: 'Init-id-0',
|
||||
key: 'id-0',
|
||||
routeName: 'Quux',
|
||||
params: { name: 'foobar' },
|
||||
},
|
||||
@@ -1133,6 +1198,126 @@ describe('StackRouter', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('Handles the navigate action with params and nested StackRouter as a first action', () => {
|
||||
const state = TestStackRouter.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'main',
|
||||
params: {
|
||||
code: 'test',
|
||||
foo: 'bar',
|
||||
},
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'profile',
|
||||
params: {
|
||||
id: '4',
|
||||
code: 'test',
|
||||
foo: 'bar',
|
||||
},
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'list',
|
||||
params: {
|
||||
id: '10259959195',
|
||||
code: 'test',
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'id-2',
|
||||
params: { code: 'test', foo: 'bar' },
|
||||
routeName: 'main',
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'id-1',
|
||||
params: { code: 'test', foo: 'bar', id: '4' },
|
||||
routeName: 'profile',
|
||||
routes: [
|
||||
{
|
||||
key: 'id-0',
|
||||
params: { code: 'test', foo: 'bar', id: '10259959195' },
|
||||
routeName: 'list',
|
||||
type: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const state2 = TestStackRouter.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'main',
|
||||
params: {
|
||||
code: '',
|
||||
foo: 'bar',
|
||||
},
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'profile',
|
||||
params: {
|
||||
id: '4',
|
||||
code: '',
|
||||
foo: 'bar',
|
||||
},
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'list',
|
||||
params: {
|
||||
id: '10259959195',
|
||||
code: '',
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(state2).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'StackRouterRoot',
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'id-5',
|
||||
params: { code: '', foo: 'bar' },
|
||||
routeName: 'main',
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'id-4',
|
||||
params: { code: '', foo: 'bar', id: '4' },
|
||||
routeName: 'profile',
|
||||
routes: [
|
||||
{
|
||||
key: 'id-3',
|
||||
params: { code: '', foo: 'bar', id: '10259959195' },
|
||||
routeName: 'list',
|
||||
type: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('Handles the navigate action with params and nested TabRouter', () => {
|
||||
const ChildNavigator = () => <div />;
|
||||
ChildNavigator.router = TabRouter({
|
||||
@@ -1355,6 +1540,28 @@ describe('StackRouter', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('URI encoded string get passed to deep link', () => {
|
||||
const uri = 'people/2018%2F02%2F07';
|
||||
const action = TestStackRouter.getActionForPathAndParams(uri);
|
||||
expect(action).toEqual({
|
||||
routeName: 'person',
|
||||
params: {
|
||||
id: '2018/02/07',
|
||||
},
|
||||
type: NavigationActions.NAVIGATE,
|
||||
});
|
||||
|
||||
const malformedUri = 'people/%E0%A4%A';
|
||||
const action2 = TestStackRouter.getActionForPathAndParams(malformedUri);
|
||||
expect(action2).toEqual({
|
||||
routeName: 'person',
|
||||
params: {
|
||||
id: '%E0%A4%A',
|
||||
},
|
||||
type: NavigationActions.NAVIGATE,
|
||||
});
|
||||
});
|
||||
|
||||
test('Querystring params get passed to nested deep link', () => {
|
||||
// uri with two non-empty query param values
|
||||
const uri = 'main/p/4/list/10259959195?code=test&foo=bar';
|
||||
|
||||
@@ -175,23 +175,3 @@ test('should throw if the route does not exist', () => {
|
||||
"There is no route defined for key Settings.\nMust be one of: 'Home'"
|
||||
);
|
||||
});
|
||||
|
||||
test('should throw if the screen is not defined under the route config', () => {
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
|
||||
const getScreenOptions = createConfigGetter({
|
||||
Home: {},
|
||||
});
|
||||
|
||||
const routes = [{ key: 'B', routeName: 'Home' }];
|
||||
|
||||
expect(() =>
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({
|
||||
state: routes[0],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
})
|
||||
)
|
||||
).toThrowError('Route Home must define a screen or a getScreen.');
|
||||
});
|
||||
|
||||
@@ -39,9 +39,7 @@ describe('validateRouteConfigMap', () => {
|
||||
Home: {
|
||||
screen: ProfileNavigator,
|
||||
},
|
||||
Chat: {
|
||||
screen: ListScreen,
|
||||
},
|
||||
Chat: ListScreen,
|
||||
};
|
||||
validateRouteConfigMap(invalidMap);
|
||||
});
|
||||
|
||||
@@ -32,5 +32,5 @@ export default function getScreenForRouteName(routeConfigs, routeName) {
|
||||
return screen;
|
||||
}
|
||||
|
||||
throw new Error(`Route ${routeName} must define a screen or a getScreen.`);
|
||||
return routeConfig;
|
||||
}
|
||||
|
||||
@@ -14,44 +14,37 @@ function validateRouteConfigMap(routeConfigs) {
|
||||
routeNames.forEach(routeName => {
|
||||
const routeConfig = routeConfigs[routeName];
|
||||
|
||||
if (!routeConfig.screen && !routeConfig.getScreen) {
|
||||
throw new Error(
|
||||
`Route '${routeName}' should declare a screen. ` +
|
||||
'For example:\n\n' +
|
||||
"import MyScreen from './MyScreen';\n" +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyScreen,\n' +
|
||||
'}'
|
||||
);
|
||||
} else if (routeConfig.screen && routeConfig.getScreen) {
|
||||
throw new Error(
|
||||
`Route '${routeName}' should declare a screen or ` +
|
||||
'a getScreen, not both.'
|
||||
);
|
||||
}
|
||||
const screenComponent = routeConfig.screen
|
||||
? routeConfig.screen
|
||||
: routeConfig;
|
||||
|
||||
if (
|
||||
routeConfig.screen &&
|
||||
typeof routeConfig.screen !== 'function' &&
|
||||
typeof routeConfig.screen !== 'string'
|
||||
screenComponent &&
|
||||
typeof screenComponent !== 'function' &&
|
||||
typeof screenComponent !== 'string' &&
|
||||
!routeConfig.getScreen
|
||||
) {
|
||||
throw new Error(
|
||||
`The component for route '${routeName}' must be a ` +
|
||||
'React component. For example:\n\n' +
|
||||
"import MyScreen from './MyScreen';\n" +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyScreen,\n' +
|
||||
`${routeName}: MyScreen,\n` +
|
||||
'}\n\n' +
|
||||
'You can also use a navigator:\n\n' +
|
||||
"import MyNavigator from './MyNavigator';\n" +
|
||||
'...\n' +
|
||||
`${routeName}: {\n` +
|
||||
' screen: MyNavigator,\n' +
|
||||
`${routeName}: MyNavigator,\n` +
|
||||
'}'
|
||||
);
|
||||
}
|
||||
|
||||
if (routeConfig.screen && routeConfig.getScreen) {
|
||||
throw new Error(
|
||||
`Route '${routeName}' should declare a screen or ` +
|
||||
'a getScreen, not both.'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class CardStack extends React.Component {
|
||||
if (props.screenProps !== this.props.screenProps) {
|
||||
this._screenDetails = {};
|
||||
}
|
||||
props.scenes.forEach(newScene => {
|
||||
props.transitionProps.scenes.forEach(newScene => {
|
||||
if (
|
||||
this._screenDetails[newScene.key] &&
|
||||
this._screenDetails[newScene.key].state !== newScene.route
|
||||
@@ -96,7 +96,7 @@ class CardStack extends React.Component {
|
||||
}
|
||||
|
||||
_getScreenDetails = scene => {
|
||||
const { screenProps, navigation, router } = this.props;
|
||||
const { screenProps, transitionProps: { navigation }, router } = this.props;
|
||||
let screenDetails = this._screenDetails[scene.key];
|
||||
if (!screenDetails || screenDetails.state !== scene.route) {
|
||||
const screenNavigation = addNavigationHelpers({
|
||||
@@ -131,12 +131,19 @@ class CardStack extends React.Component {
|
||||
headerRightInterpolator,
|
||||
} = this._getTransitionConfig();
|
||||
|
||||
const { mode, ...passProps } = this.props;
|
||||
const {
|
||||
mode,
|
||||
transitionProps,
|
||||
prevTransitionProps,
|
||||
...passProps
|
||||
} = this.props;
|
||||
|
||||
return renderHeader({
|
||||
...passProps,
|
||||
...transitionProps,
|
||||
scene,
|
||||
mode: headerMode,
|
||||
transitionPreset: this._getHeaderTransitionPreset(),
|
||||
getScreenDetails: this._getScreenDetails,
|
||||
leftInterpolator: headerLeftInterpolator,
|
||||
titleInterpolator: headerTitleInterpolator,
|
||||
@@ -153,22 +160,22 @@ class CardStack extends React.Component {
|
||||
// when we'd do that with the current structure we have. `stopAnimation` callback
|
||||
// is also broken with native animated values that have no listeners so if we
|
||||
// want to remove this we have to fix this too.
|
||||
animatedSubscribeValue(props.layout.width);
|
||||
animatedSubscribeValue(props.layout.height);
|
||||
animatedSubscribeValue(props.position);
|
||||
animatedSubscribeValue(props.transitionProps.layout.width);
|
||||
animatedSubscribeValue(props.transitionProps.layout.height);
|
||||
animatedSubscribeValue(props.transitionProps.position);
|
||||
}
|
||||
|
||||
_reset(resetToIndex, duration) {
|
||||
Animated.timing(this.props.position, {
|
||||
Animated.timing(this.props.transitionProps.position, {
|
||||
toValue: resetToIndex,
|
||||
duration,
|
||||
easing: EaseInOut,
|
||||
useNativeDriver: this.props.position.__isNative,
|
||||
useNativeDriver: this.props.transitionProps.position.__isNative,
|
||||
}).start();
|
||||
}
|
||||
|
||||
_goBack(backFromIndex, duration) {
|
||||
const { navigation, position, scenes } = this.props;
|
||||
const { navigation, position, scenes } = this.props.transitionProps;
|
||||
const toValue = Math.max(backFromIndex - 1, 0);
|
||||
|
||||
// set temporary index for gesture handler to respect until the action is
|
||||
@@ -198,9 +205,15 @@ class CardStack extends React.Component {
|
||||
let floatingHeader = null;
|
||||
const headerMode = this._getHeaderMode();
|
||||
if (headerMode === 'float') {
|
||||
floatingHeader = this._renderHeader(this.props.scene, headerMode);
|
||||
floatingHeader = this._renderHeader(
|
||||
this.props.transitionProps.scene,
|
||||
headerMode
|
||||
);
|
||||
}
|
||||
const { navigation, position, layout, scene, scenes, mode } = this.props;
|
||||
const {
|
||||
transitionProps: { navigation, position, layout, scene, scenes },
|
||||
mode,
|
||||
} = this.props;
|
||||
const { index } = navigation.state;
|
||||
const isVertical = mode === 'modal';
|
||||
const { options } = this._getScreenDetails(scene);
|
||||
@@ -363,6 +376,21 @@ class CardStack extends React.Component {
|
||||
return 'float';
|
||||
}
|
||||
|
||||
_getHeaderTransitionPreset() {
|
||||
// On Android or with header mode screen, we always just use in-place,
|
||||
// we ignore the option entirely (at least until we have other presets)
|
||||
if (Platform.OS === 'android' || this._getHeaderMode() === 'screen') {
|
||||
return 'fade-in-place';
|
||||
}
|
||||
|
||||
// TODO: validations: 'fade-in-place' or 'uikit' are valid
|
||||
if (this.props.headerTransitionPreset) {
|
||||
return this.props.headerTransitionPreset;
|
||||
} else {
|
||||
return 'fade-in-place';
|
||||
}
|
||||
}
|
||||
|
||||
_renderInnerScene(SceneComponent, scene) {
|
||||
const { navigation } = this._getScreenDetails(scene);
|
||||
const { screenProps } = this.props;
|
||||
@@ -395,8 +423,8 @@ class CardStack extends React.Component {
|
||||
|
||||
return TransitionConfigs.getTransitionConfig(
|
||||
this.props.transitionConfig,
|
||||
{},
|
||||
{},
|
||||
this.props.transitionProps,
|
||||
this.props.prevTransitionProps,
|
||||
isModal
|
||||
);
|
||||
};
|
||||
@@ -404,15 +432,19 @@ class CardStack extends React.Component {
|
||||
_renderCard = scene => {
|
||||
const { screenInterpolator } = this._getTransitionConfig();
|
||||
const style =
|
||||
screenInterpolator && screenInterpolator({ ...this.props, scene });
|
||||
screenInterpolator &&
|
||||
screenInterpolator({ ...this.props.transitionProps, scene });
|
||||
|
||||
const SceneComponent = this.props.router.getComponentForRouteName(
|
||||
scene.route.routeName
|
||||
);
|
||||
|
||||
const { transitionProps, ...props } = this.props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
{...this.props}
|
||||
{...props}
|
||||
{...transitionProps}
|
||||
key={`card_${scene.key}`}
|
||||
style={[style, this.props.cardStyle]}
|
||||
scene={scene}
|
||||
|
||||
@@ -53,10 +53,11 @@ class CardStackTransitioner extends React.Component {
|
||||
return transitionSpec;
|
||||
};
|
||||
|
||||
_render = props => {
|
||||
_render = (props, prevProps) => {
|
||||
const {
|
||||
screenProps,
|
||||
headerMode,
|
||||
headerTransitionPreset,
|
||||
mode,
|
||||
router,
|
||||
cardStyle,
|
||||
@@ -66,11 +67,13 @@ class CardStackTransitioner extends React.Component {
|
||||
<CardStack
|
||||
screenProps={screenProps}
|
||||
headerMode={headerMode}
|
||||
headerTransitionPreset={headerTransitionPreset}
|
||||
mode={mode}
|
||||
router={router}
|
||||
cardStyle={cardStyle}
|
||||
transitionConfig={transitionConfig}
|
||||
{...props}
|
||||
transitionProps={props}
|
||||
prevTransitionProps={prevProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,15 +3,18 @@ import React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Dimensions,
|
||||
Image,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
View,
|
||||
ViewPropTypes,
|
||||
} from 'react-native';
|
||||
import { MaskedViewIOS } from '../../PlatformHelpers';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
import HeaderBackButton from './HeaderBackButton';
|
||||
import ModularHeaderBackButton from './ModularHeaderBackButton';
|
||||
import HeaderStyleInterpolator from './HeaderStyleInterpolator';
|
||||
import withOrientation from '../withOrientation';
|
||||
|
||||
@@ -19,9 +22,18 @@ const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56;
|
||||
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0;
|
||||
const TITLE_OFFSET = Platform.OS === 'ios' ? 70 : 56;
|
||||
|
||||
const getAppBarHeight = isLandscape => {
|
||||
return Platform.OS === 'ios'
|
||||
? isLandscape && !Platform.isPad ? 32 : 44
|
||||
: 56;
|
||||
};
|
||||
|
||||
class Header extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
leftInterpolator: HeaderStyleInterpolator.forLeft,
|
||||
leftButtonInterpolator: HeaderStyleInterpolator.forLeftButton,
|
||||
leftLabelInterpolator: HeaderStyleInterpolator.forLeftLabel,
|
||||
titleFromLeftInterpolator: HeaderStyleInterpolator.forCenterFromLeft,
|
||||
titleInterpolator: HeaderStyleInterpolator.forCenter,
|
||||
rightInterpolator: HeaderStyleInterpolator.forRight,
|
||||
};
|
||||
@@ -116,15 +128,18 @@ class Header extends React.PureComponent {
|
||||
|
||||
_renderLeftComponent = props => {
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
|
||||
if (
|
||||
React.isValidElement(options.headerLeft) ||
|
||||
options.headerLeft === null
|
||||
) {
|
||||
return options.headerLeft;
|
||||
}
|
||||
|
||||
if (props.scene.index === 0) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||
props.scene
|
||||
@@ -147,6 +162,36 @@ class Header extends React.PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
_renderModularLeftComponent = (
|
||||
props,
|
||||
ButtonContainerComponent,
|
||||
LabelContainerComponent
|
||||
) => {
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||
props.scene
|
||||
);
|
||||
const width = this.state.widths[props.scene.key]
|
||||
? (this.props.layout.initWidth - this.state.widths[props.scene.key]) / 2
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<ModularHeaderBackButton
|
||||
onPress={this._navigateBack}
|
||||
ButtonContainerComponent={ButtonContainerComponent}
|
||||
LabelContainerComponent={LabelContainerComponent}
|
||||
pressColorAndroid={options.headerPressColorAndroid}
|
||||
tintColor={options.headerTintColor}
|
||||
buttonImage={options.headerBackImage}
|
||||
title={backButtonTitle}
|
||||
truncatedTitle={truncatedBackButtonTitle}
|
||||
titleStyle={options.headerBackTitleStyle}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
_renderRightComponent = props => {
|
||||
const details = this.props.getScreenDetails(props.scene);
|
||||
const { headerRight } = details.options;
|
||||
@@ -154,16 +199,38 @@ class Header extends React.PureComponent {
|
||||
};
|
||||
|
||||
_renderLeft(props) {
|
||||
return this._renderSubView(
|
||||
props,
|
||||
'left',
|
||||
this._renderLeftComponent,
|
||||
this.props.leftInterpolator
|
||||
);
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
|
||||
const { transitionPreset } = this.props;
|
||||
|
||||
// On Android, or if we have a custom header left, or if we have a custom back image, we
|
||||
// do not use the modular header (which is the one that imitates UINavigationController)
|
||||
if (
|
||||
transitionPreset !== 'uikit' ||
|
||||
options.headerBackImage ||
|
||||
options.headerLeft ||
|
||||
options.headerLeft === null
|
||||
) {
|
||||
return this._renderSubView(
|
||||
props,
|
||||
'left',
|
||||
this._renderLeftComponent,
|
||||
this.props.leftInterpolator
|
||||
);
|
||||
} else {
|
||||
return this._renderModularSubView(
|
||||
props,
|
||||
'left',
|
||||
this._renderModularLeftComponent,
|
||||
this.props.leftLabelInterpolator,
|
||||
this.props.leftButtonInterpolator
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_renderTitle(props, options) {
|
||||
const style = {};
|
||||
const { transitionPreset } = this.props;
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
if (!options.hasLeftComponent) {
|
||||
@@ -185,7 +252,9 @@ class Header extends React.PureComponent {
|
||||
{ ...props, style },
|
||||
'title',
|
||||
this._renderTitleComponent,
|
||||
this.props.titleInterpolator
|
||||
transitionPreset === 'uikit'
|
||||
? this.props.titleFromLeftInterpolator
|
||||
: this.props.titleInterpolator
|
||||
);
|
||||
}
|
||||
|
||||
@@ -198,6 +267,64 @@ class Header extends React.PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
_renderModularSubView(
|
||||
props,
|
||||
name,
|
||||
renderer,
|
||||
labelStyleInterpolator,
|
||||
buttonStyleInterpolator
|
||||
) {
|
||||
const { scene } = props;
|
||||
const { index, isStale, key } = scene;
|
||||
|
||||
// Never render a modular back button on the first screen in a stack.
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = this.props.navigation.state.index - index;
|
||||
|
||||
if (Math.abs(offset) > 2) {
|
||||
// Scene is far away from the active scene. Hides it to avoid unnecessary
|
||||
// rendering.
|
||||
return null;
|
||||
}
|
||||
|
||||
const ButtonContainer = ({ children }) => (
|
||||
<Animated.View
|
||||
style={[buttonStyleInterpolator({ ...this.props, ...props })]}
|
||||
>
|
||||
{children}
|
||||
</Animated.View>
|
||||
);
|
||||
|
||||
const LabelContainer = ({ children }) => (
|
||||
<Animated.View
|
||||
style={[labelStyleInterpolator({ ...this.props, ...props })]}
|
||||
>
|
||||
{children}
|
||||
</Animated.View>
|
||||
);
|
||||
|
||||
const subView = renderer(props, ButtonContainer, LabelContainer);
|
||||
|
||||
if (subView === null) {
|
||||
return subView;
|
||||
}
|
||||
|
||||
const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none';
|
||||
|
||||
return (
|
||||
<View
|
||||
key={`${name}_${key}`}
|
||||
pointerEvents={pointerEvents}
|
||||
style={[styles.item, styles[name], props.style]}
|
||||
>
|
||||
{subView}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSubView(props, name, renderer, styleInterpolator) {
|
||||
const { scene } = props;
|
||||
const { index, isStale, key } = scene;
|
||||
@@ -246,16 +373,47 @@ class Header extends React.PureComponent {
|
||||
hasRightComponent: !!right,
|
||||
});
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[StyleSheet.absoluteFill, styles.header]}
|
||||
key={`scene_${props.scene.key}`}
|
||||
>
|
||||
{title}
|
||||
{left}
|
||||
{right}
|
||||
</View>
|
||||
);
|
||||
const wrapperProps = {
|
||||
style: [StyleSheet.absoluteFill, styles.header],
|
||||
key: `scene_${props.scene.key}`,
|
||||
};
|
||||
|
||||
const { isLandscape, transitionPreset } = this.props;
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
|
||||
if (
|
||||
options.headerLeft ||
|
||||
options.headerBackImage ||
|
||||
Platform.OS !== 'ios' ||
|
||||
transitionPreset !== 'uikit'
|
||||
) {
|
||||
return (
|
||||
<View {...wrapperProps}>
|
||||
{title}
|
||||
{left}
|
||||
{right}
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<MaskedViewIOS
|
||||
{...wrapperProps}
|
||||
maskElement={
|
||||
<View style={styles.iconMaskContainer}>
|
||||
<Image
|
||||
source={require('../assets/back-icon-mask.png')}
|
||||
style={styles.iconMask}
|
||||
/>
|
||||
<View style={styles.iconMaskFillerRect} />
|
||||
</View>
|
||||
}
|
||||
>
|
||||
{title}
|
||||
{left}
|
||||
{right}
|
||||
</MaskedViewIOS>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -293,8 +451,7 @@ class Header extends React.PureComponent {
|
||||
|
||||
const { options } = this.props.getScreenDetails(scene);
|
||||
const { headerStyle } = options;
|
||||
const appBarHeight =
|
||||
Platform.OS === 'ios' ? (isLandscape && !Platform.isPad ? 32 : 44) : 56;
|
||||
const appBarHeight = getAppBarHeight(isLandscape);
|
||||
const containerStyles = [
|
||||
styles.container,
|
||||
{
|
||||
@@ -350,6 +507,25 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
iconMaskContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
iconMaskFillerRect: {
|
||||
flex: 1,
|
||||
backgroundColor: '#d8d8d8',
|
||||
marginLeft: -3,
|
||||
},
|
||||
iconMask: {
|
||||
// These are mostly the same as the icon in ModularHeaderBackButton
|
||||
height: 21,
|
||||
width: 12,
|
||||
marginLeft: 9,
|
||||
marginTop: -0.5, // resizes down to 20.5
|
||||
alignSelf: 'center',
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
title: {
|
||||
bottom: 0,
|
||||
left: TITLE_OFFSET,
|
||||
|
||||
@@ -107,7 +107,7 @@ const styles = StyleSheet.create({
|
||||
? {
|
||||
height: 21,
|
||||
width: 13,
|
||||
marginLeft: 10,
|
||||
marginLeft: 9,
|
||||
marginRight: 22,
|
||||
marginVertical: 12,
|
||||
resizeMode: 'contain',
|
||||
@@ -123,7 +123,7 @@ const styles = StyleSheet.create({
|
||||
iconWithTitle:
|
||||
Platform.OS === 'ios'
|
||||
? {
|
||||
marginRight: 5,
|
||||
marginRight: 6,
|
||||
}
|
||||
: {},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { I18nManager } from 'react-native';
|
||||
|
||||
import { Dimensions, I18nManager } from 'react-native';
|
||||
import getSceneIndicesForInterpolationInputRange from '../../utils/getSceneIndicesForInterpolationInputRange';
|
||||
|
||||
const crossFadeInterpolation = (first, index, last) => ({
|
||||
inputRange: [first, index - 0.75, index - 0.5, index, index + 0.5, last],
|
||||
outputRange: [0, 0, 0.2, 1, 0.5, 0],
|
||||
});
|
||||
|
||||
/**
|
||||
* Utility that builds the style for the navigation header.
|
||||
*
|
||||
@@ -23,16 +27,7 @@ function forLeft(props) {
|
||||
const index = scene.index;
|
||||
|
||||
return {
|
||||
opacity: position.interpolate({
|
||||
inputRange: [
|
||||
first,
|
||||
first + Math.abs(index - first) / 2,
|
||||
index,
|
||||
last - Math.abs(last - index) / 2,
|
||||
last,
|
||||
],
|
||||
outputRange: [0, 0, 1, 0, 0],
|
||||
}),
|
||||
opacity: position.interpolate(crossFadeInterpolation(first, index, last)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,21 +39,9 @@ function forCenter(props) {
|
||||
|
||||
const { first, last } = interpolate;
|
||||
const index = scene.index;
|
||||
const inputRange = [first, index, last];
|
||||
|
||||
return {
|
||||
opacity: position.interpolate({
|
||||
inputRange,
|
||||
outputRange: [0, 1, 0],
|
||||
}),
|
||||
transform: [
|
||||
{
|
||||
translateX: position.interpolate({
|
||||
inputRange,
|
||||
outputRange: I18nManager.isRTL ? [-200, 0, 200] : [200, 0, -200],
|
||||
}),
|
||||
},
|
||||
],
|
||||
opacity: position.interpolate(crossFadeInterpolation(first, index, last)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,16 +53,125 @@ function forRight(props) {
|
||||
const { first, last } = interpolate;
|
||||
const index = scene.index;
|
||||
|
||||
return {
|
||||
opacity: position.interpolate(crossFadeInterpolation(first, index, last)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* iOS UINavigationController style interpolators
|
||||
*/
|
||||
|
||||
function forLeftButton(props) {
|
||||
const { position, scene, scenes } = props;
|
||||
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||
|
||||
if (!interpolate) return { opacity: 0 };
|
||||
|
||||
const { first, last } = interpolate;
|
||||
const index = scene.index;
|
||||
|
||||
return {
|
||||
opacity: position.interpolate({
|
||||
inputRange: [first, index, last],
|
||||
outputRange: [0, 1, 0],
|
||||
inputRange: [
|
||||
first,
|
||||
first + Math.abs(index - first) / 2,
|
||||
index,
|
||||
last - Math.abs(last - index) / 2,
|
||||
last,
|
||||
],
|
||||
outputRange: [0, 0.5, 1, 0.5, 0],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* 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. 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)
|
||||
*/
|
||||
const LEFT_LABEL_OFFSET = Dimensions.get('window').width / 2 - 70 - 25;
|
||||
function forLeftLabel(props) {
|
||||
const { position, scene, scenes } = props;
|
||||
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||
|
||||
if (!interpolate) return { opacity: 0 };
|
||||
|
||||
const { first, last } = interpolate;
|
||||
const index = scene.index;
|
||||
|
||||
const offset = LEFT_LABEL_OFFSET;
|
||||
|
||||
return {
|
||||
// For now we fade out the label before fading in the title, so the
|
||||
// differences between the label and title position can be hopefully not so
|
||||
// noticable to the user
|
||||
opacity: position.interpolate({
|
||||
inputRange: [first, index - 0.35, index, index + 0.5, last],
|
||||
outputRange: [0, 0, 1, 0.5, 0],
|
||||
}),
|
||||
transform: [
|
||||
{
|
||||
translateX: position.interpolate({
|
||||
inputRange: [first, index, last],
|
||||
outputRange: I18nManager.isRTL
|
||||
? [-offset, 0, offset]
|
||||
: [offset, 0, -offset * 1.5],
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
const TITLE_OFFSET_IOS = Dimensions.get('window').width / 2 - 70 + 25;
|
||||
function forCenterFromLeft(props) {
|
||||
const { position, scene } = props;
|
||||
const interpolate = getSceneIndicesForInterpolationInputRange(props);
|
||||
|
||||
if (!interpolate) return { opacity: 0 };
|
||||
|
||||
const { first, last } = interpolate;
|
||||
const index = scene.index;
|
||||
const inputRange = [first, index - 0.5, index, index + 0.5, last];
|
||||
const offset = TITLE_OFFSET_IOS;
|
||||
|
||||
return {
|
||||
opacity: position.interpolate({
|
||||
inputRange: [first, index - 0.5, index, index + 0.7, last],
|
||||
outputRange: [0, 0, 1, 0, 0],
|
||||
}),
|
||||
transform: [
|
||||
{
|
||||
translateX: position.interpolate({
|
||||
inputRange: [first, index, last],
|
||||
outputRange: I18nManager.isRTL
|
||||
? [-offset, 0, offset]
|
||||
: [offset, 0, -offset],
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
forLeft,
|
||||
forLeftButton,
|
||||
forLeftLabel,
|
||||
forCenterFromLeft,
|
||||
forCenter,
|
||||
forRight,
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ const HeaderTitle = ({ style, ...rest }) => (
|
||||
const styles = StyleSheet.create({
|
||||
title: {
|
||||
fontSize: Platform.OS === 'ios' ? 17 : 20,
|
||||
fontWeight: Platform.OS === 'ios' ? '600' : '500',
|
||||
fontWeight: Platform.OS === 'ios' ? '700' : '500',
|
||||
color: 'rgba(0, 0, 0, .9)',
|
||||
textAlign: Platform.OS === 'ios' ? 'center' : 'left',
|
||||
marginHorizontal: 16,
|
||||
|
||||
118
src/views/Header/ModularHeaderBackButton.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import React from 'react';
|
||||
import { I18nManager, Image, Text, View, StyleSheet } from 'react-native';
|
||||
|
||||
import TouchableItem from '../TouchableItem';
|
||||
|
||||
class ModularHeaderBackButton extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
tintColor: '#037aff',
|
||||
truncatedTitle: 'Back',
|
||||
// eslint-disable-next-line global-require
|
||||
buttonImage: require('../assets/back-icon.png'),
|
||||
};
|
||||
|
||||
state = {};
|
||||
|
||||
_onTextLayout = e => {
|
||||
if (this.state.initialTextWidth) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
initialTextWidth: e.nativeEvent.layout.x + e.nativeEvent.layout.width,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
buttonImage,
|
||||
onPress,
|
||||
width,
|
||||
title,
|
||||
titleStyle,
|
||||
tintColor,
|
||||
truncatedTitle,
|
||||
} = this.props;
|
||||
|
||||
const renderTruncated =
|
||||
this.state.initialTextWidth && width
|
||||
? this.state.initialTextWidth > width
|
||||
: false;
|
||||
|
||||
let backButtonTitle = renderTruncated ? truncatedTitle : title;
|
||||
|
||||
// TODO: When we've sorted out measuring in the header, let's revisit this.
|
||||
// This is clearly a bad workaround.
|
||||
if (backButtonTitle && backButtonTitle.length > 8) {
|
||||
backButtonTitle = truncatedTitle;
|
||||
}
|
||||
|
||||
const { ButtonContainerComponent, LabelContainerComponent } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableItem
|
||||
accessibilityComponentType="button"
|
||||
accessibilityLabel={backButtonTitle}
|
||||
accessibilityTraits="button"
|
||||
testID="header-back"
|
||||
delayPressIn={0}
|
||||
onPress={onPress}
|
||||
style={styles.container}
|
||||
borderless
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<ButtonContainerComponent>
|
||||
<Image
|
||||
style={[
|
||||
styles.icon,
|
||||
!!title && styles.iconWithTitle,
|
||||
!!tintColor && { tintColor },
|
||||
]}
|
||||
source={buttonImage}
|
||||
/>
|
||||
</ButtonContainerComponent>
|
||||
{typeof backButtonTitle === 'string' && (
|
||||
<LabelContainerComponent>
|
||||
<Text
|
||||
onLayout={this._onTextLayout}
|
||||
style={[
|
||||
styles.title,
|
||||
!!tintColor && { color: tintColor },
|
||||
titleStyle,
|
||||
]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{backButtonTitle}
|
||||
</Text>
|
||||
</LabelContainerComponent>
|
||||
)}
|
||||
</View>
|
||||
</TouchableItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
title: {
|
||||
fontSize: 17,
|
||||
paddingRight: 10,
|
||||
},
|
||||
icon: {
|
||||
height: 21,
|
||||
width: 12,
|
||||
marginLeft: 9,
|
||||
marginRight: 22,
|
||||
marginVertical: 12,
|
||||
resizeMode: 'contain',
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
},
|
||||
iconWithTitle: {
|
||||
marginRight: 3,
|
||||
},
|
||||
});
|
||||
|
||||
export default ModularHeaderBackButton;
|
||||
112
src/views/ResourceSavingSceneView.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import SceneView from './SceneView';
|
||||
|
||||
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
|
||||
|
||||
export default class ResourceSavingSceneView extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super();
|
||||
|
||||
const key = props.childNavigation.state.key;
|
||||
const focusedIndex = props.navigation.state.index;
|
||||
const focusedKey = props.navigation.state.routes[focusedIndex].key;
|
||||
const isFocused = key === focusedKey;
|
||||
|
||||
this.state = {
|
||||
awake: props.lazy ? isFocused : true,
|
||||
visible: isFocused,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this._actionListener = this.props.navigation.addListener(
|
||||
'action',
|
||||
this._onAction
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._actionListener.remove();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { awake, visible } = this.state;
|
||||
const {
|
||||
childNavigation,
|
||||
navigation,
|
||||
removeClippedSubviews,
|
||||
lazy,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.container}
|
||||
collapsable={false}
|
||||
removeClippedSubviews={
|
||||
Platform.OS === 'android'
|
||||
? removeClippedSubviews
|
||||
: !visible && removeClippedSubviews
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
this._mustAlwaysBeVisible() || visible
|
||||
? styles.innerAttached
|
||||
: styles.innerDetached
|
||||
}
|
||||
>
|
||||
{awake ? <SceneView {...rest} navigation={childNavigation} /> : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_mustAlwaysBeVisible = () => {
|
||||
return this.props.animationEnabled || this.props.swipeEnabled;
|
||||
};
|
||||
|
||||
_onAction = payload => {
|
||||
// We do not care about transition complete events, they won't actually change the state
|
||||
if (
|
||||
payload.action.type == 'Navigation/COMPLETE_TRANSITION' ||
|
||||
!payload.state
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { routes, index } = payload.state;
|
||||
const key = this.props.childNavigation.state.key;
|
||||
|
||||
if (routes[index].key === key) {
|
||||
if (!this.state.visible) {
|
||||
let nextState = { visible: true };
|
||||
if (!this.state.awake) {
|
||||
nextState.awake = true;
|
||||
}
|
||||
this.setState(nextState);
|
||||
}
|
||||
} else {
|
||||
if (this.state.visible) {
|
||||
this.setState({ visible: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
innerAttached: {
|
||||
flex: 1,
|
||||
},
|
||||
innerDetached: {
|
||||
flex: 1,
|
||||
top: FAR_FAR_AWAY,
|
||||
},
|
||||
});
|
||||
@@ -63,6 +63,7 @@ class TabBarBottom extends React.PureComponent {
|
||||
if (typeof label === 'string') {
|
||||
return (
|
||||
<Animated.Text
|
||||
numberOfLines={1}
|
||||
style={[
|
||||
styles.label,
|
||||
{ color },
|
||||
|
||||
@@ -3,11 +3,13 @@ import { View, StyleSheet, Platform } from 'react-native';
|
||||
import { TabViewAnimated, TabViewPagerPan } from 'react-native-tab-view';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import SceneView from '../SceneView';
|
||||
import ResourceSavingSceneView from '../ResourceSavingSceneView';
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
|
||||
class TabView extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
lazy: true,
|
||||
removedClippedSubviews: true,
|
||||
// fix for https://github.com/react-native-community/react-native-tab-view/issues/312
|
||||
initialLayout: Platform.select({
|
||||
android: { width: 1, height: 0 },
|
||||
@@ -25,14 +27,18 @@ class TabView extends React.PureComponent {
|
||||
const TabComponent = this.props.router.getComponentForRouteName(
|
||||
route.routeName
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.page}>
|
||||
<SceneView
|
||||
screenProps={screenProps}
|
||||
component={TabComponent}
|
||||
navigation={childNavigation}
|
||||
/>
|
||||
</View>
|
||||
<ResourceSavingSceneView
|
||||
lazy={this.props.lazy}
|
||||
removeClippedSubViews={this.props.removeClippedSubviews}
|
||||
animationEnabled={this.props.animationEnabled}
|
||||
swipeEnabled={this.props.swipeEnabled}
|
||||
screenProps={screenProps}
|
||||
component={TabComponent}
|
||||
navigation={this.props.navigation}
|
||||
childNavigation={childNavigation}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -140,11 +146,15 @@ class TabView extends React.PureComponent {
|
||||
const tabBarVisible =
|
||||
options.tabBarVisible == null ? true : options.tabBarVisible;
|
||||
|
||||
const swipeEnabled =
|
||||
let swipeEnabled =
|
||||
options.swipeEnabled == null
|
||||
? this.props.swipeEnabled
|
||||
: options.swipeEnabled;
|
||||
|
||||
if (typeof swipeEnabled === 'function') {
|
||||
swipeEnabled = swipeEnabled(state);
|
||||
}
|
||||
|
||||
if (tabBarComponent !== undefined && tabBarVisible) {
|
||||
if (tabBarPosition === 'bottom') {
|
||||
renderFooter = this._renderTabBar;
|
||||
@@ -185,9 +195,4 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
page: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -179,9 +179,19 @@ class Transitioner extends React.Component {
|
||||
const prevTransitionProps = this._prevTransitionProps;
|
||||
this._prevTransitionProps = null;
|
||||
|
||||
const scenes = this.state.scenes.filter(isSceneNotStale);
|
||||
|
||||
const nextState = {
|
||||
...this.state,
|
||||
scenes: this.state.scenes.filter(isSceneNotStale),
|
||||
/**
|
||||
* Array.prototype.filter creates a new instance of an array
|
||||
* even if there were no elements removed. There are cases when
|
||||
* `this.state.scenes` will have no stale scenes (typically when
|
||||
* pushing a new route). As a result, components that rely on this prop
|
||||
* might enter an unnecessary render cycle.
|
||||
*/
|
||||
scenes:
|
||||
this.state.scenes.length === scenes.length ? this.state.scenes : scenes,
|
||||
};
|
||||
|
||||
this._transitionProps = buildTransitionProps(this.props, nextState);
|
||||
|
||||
@@ -124,6 +124,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
@@ -224,6 +225,8 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
@@ -232,25 +235,34 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
}
|
||||
>
|
||||
<View
|
||||
navigation={
|
||||
style={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": undefined,
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"key": "s1",
|
||||
"routeName": "s1",
|
||||
},
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
screenProps={undefined}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": undefined,
|
||||
"getParam": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"key": "s1",
|
||||
"routeName": "s1",
|
||||
},
|
||||
}
|
||||
}
|
||||
screenProps={undefined}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollContentView>
|
||||
|
||||
BIN
src/views/assets/back-icon-mask.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 491 B |
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 491 B |
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 966 B |
|
Before Width: | Height: | Size: 732 B After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 760 B After Width: | Height: | Size: 2.5 KiB |
@@ -3,16 +3,25 @@ import propTypes from 'prop-types';
|
||||
import hoistStatics from 'hoist-non-react-statics';
|
||||
|
||||
export default function withNavigation(Component) {
|
||||
const componentWithNavigation = (props, { navigation }) => (
|
||||
<Component {...props} navigation={navigation} />
|
||||
);
|
||||
class ComponentWithNavigation extends React.Component {
|
||||
static displayName = `withNavigation(${Component.displayName ||
|
||||
Component.name})`;
|
||||
|
||||
const displayName = Component.displayName || Component.name;
|
||||
componentWithNavigation.displayName = `withNavigation(${displayName})`;
|
||||
static contextTypes = {
|
||||
navigation: propTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentWithNavigation.contextTypes = {
|
||||
navigation: propTypes.object.isRequired,
|
||||
};
|
||||
render() {
|
||||
const { navigation } = this.context;
|
||||
return (
|
||||
<Component
|
||||
{...this.props}
|
||||
navigation={navigation}
|
||||
ref={this.props.onRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return hoistStatics(componentWithNavigation, Component);
|
||||
return hoistStatics(ComponentWithNavigation, Component);
|
||||
}
|
||||
|
||||
56
src/views/withNavigationFocus.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
import hoistStatics from 'hoist-non-react-statics';
|
||||
import invariant from '../utils/invariant';
|
||||
|
||||
export default function withNavigationFocus(Component) {
|
||||
class ComponentWithNavigationFocus extends React.Component {
|
||||
static displayName = `withNavigationFocus(${Component.displayName ||
|
||||
Component.name})`;
|
||||
|
||||
static contextTypes = {
|
||||
navigation: propTypes.object.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
isFocused: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const navigation = this.getNavigation();
|
||||
this.subscriptions = [
|
||||
navigation.addListener('didFocus', () =>
|
||||
this.setState({ isFocused: true })
|
||||
),
|
||||
navigation.addListener('willBlur', () =>
|
||||
this.setState({ isFocused: false })
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscriptions.forEach(sub => sub.remove());
|
||||
}
|
||||
|
||||
getNavigation = () => {
|
||||
const navigation = this.props.navigation || this.context.navigation;
|
||||
invariant(
|
||||
!!navigation,
|
||||
'withNavigationFocus can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.'
|
||||
);
|
||||
return navigation;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Component
|
||||
{...this.props}
|
||||
isFocused={this.state.isFocused}
|
||||
ref={this.props.onRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return hoistStatics(ComponentWithNavigationFocus, Component);
|
||||
}
|
||||
312
yarn.lock
@@ -52,7 +52,7 @@ acorn@^3.0.4:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
|
||||
|
||||
acorn@^5.0.0, acorn@^5.2.1, acorn@^5.3.0:
|
||||
acorn@^5.0.0, acorn@^5.3.0, acorn@^5.4.0:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102"
|
||||
|
||||
@@ -363,8 +363,8 @@ babel-eslint@^7.2.3:
|
||||
babylon "^6.17.0"
|
||||
|
||||
babel-generator@^6.18.0, babel-generator@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
|
||||
version "6.26.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
|
||||
dependencies:
|
||||
babel-messages "^6.23.0"
|
||||
babel-runtime "^6.26.0"
|
||||
@@ -372,7 +372,7 @@ babel-generator@^6.18.0, babel-generator@^6.26.0:
|
||||
detect-indent "^4.0.0"
|
||||
jsesc "^1.3.0"
|
||||
lodash "^4.17.4"
|
||||
source-map "^0.5.6"
|
||||
source-map "^0.5.7"
|
||||
trim-right "^1.0.1"
|
||||
|
||||
babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
|
||||
@@ -492,12 +492,12 @@ babel-jest@^20.0.3:
|
||||
babel-plugin-istanbul "^4.0.0"
|
||||
babel-preset-jest "^20.0.3"
|
||||
|
||||
babel-jest@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.1.0.tgz#7fae6f655fffe77e818a8c2868c754a42463fdfd"
|
||||
babel-jest@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.2.2.tgz#eda38dca284e32cc5257f96a9b51351975de4e04"
|
||||
dependencies:
|
||||
babel-plugin-istanbul "^4.1.5"
|
||||
babel-preset-jest "^22.1.0"
|
||||
babel-preset-jest "^22.2.0"
|
||||
|
||||
babel-messages@^6.23.0:
|
||||
version "6.23.0"
|
||||
@@ -529,9 +529,9 @@ babel-plugin-jest-hoist@^20.0.3:
|
||||
version "20.0.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz#afedc853bd3f8dc3548ea671fbe69d03cc2c1767"
|
||||
|
||||
babel-plugin-jest-hoist@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.1.0.tgz#c1281dd7887d77a1711dc760468c3b8285dde9ee"
|
||||
babel-plugin-jest-hoist@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.2.0.tgz#bd34f39d652406669713b8c89e23ef25c890b993"
|
||||
|
||||
babel-plugin-react-transform@2.0.2:
|
||||
version "2.0.2"
|
||||
@@ -860,11 +860,11 @@ babel-preset-jest@^20.0.3:
|
||||
dependencies:
|
||||
babel-plugin-jest-hoist "^20.0.3"
|
||||
|
||||
babel-preset-jest@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-22.1.0.tgz#ff4e704102f9642765e2254226050561d8942ec9"
|
||||
babel-preset-jest@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-22.2.0.tgz#f77b43f06ef4d8547214b2e206cc76a25c3ba0e2"
|
||||
dependencies:
|
||||
babel-plugin-jest-hoist "^22.1.0"
|
||||
babel-plugin-jest-hoist "^22.2.0"
|
||||
babel-plugin-syntax-object-rest-spread "^6.13.0"
|
||||
|
||||
babel-preset-react-native@^2.1.0:
|
||||
@@ -1095,8 +1095,8 @@ bplist-parser@0.1.1:
|
||||
big-integer "^1.6.7"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
@@ -1325,9 +1325,9 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@^2.11.0, commander@^2.9.0, commander@~2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
|
||||
commander@^2.11.0, commander@^2.9.0, commander@~2.14.1:
|
||||
version "2.14.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
|
||||
|
||||
common-tags@^1.4.0:
|
||||
version "1.7.2"
|
||||
@@ -1852,8 +1852,8 @@ eslint-visitor-keys@^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:
|
||||
version "4.16.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.16.0.tgz#934ada9e98715e1d7bbfd6f6f0519ed2fab35cc1"
|
||||
version "4.17.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.17.0.tgz#dc24bb51ede48df629be7031c71d9dc0ee4f3ddf"
|
||||
dependencies:
|
||||
ajv "^5.3.0"
|
||||
babel-code-frame "^6.22.0"
|
||||
@@ -1894,10 +1894,10 @@ eslint@^4.2.0, eslint@^4.5.0:
|
||||
text-table "~0.2.0"
|
||||
|
||||
espree@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca"
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.3.tgz#931e0af64e7fbbed26b050a29daad1fc64799fa6"
|
||||
dependencies:
|
||||
acorn "^5.2.1"
|
||||
acorn "^5.4.0"
|
||||
acorn-jsx "^3.0.0"
|
||||
|
||||
esprima@^3.1.3:
|
||||
@@ -1938,8 +1938,8 @@ event-target-shim@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-1.1.1.tgz#a86e5ee6bdaa16054475da797ccddf0c55698491"
|
||||
|
||||
eventemitter3@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.0.0.tgz#fc29ecf233bd19fbd527bb4089bbf665dc90c1e3"
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.0.1.tgz#4ce66c3fc5b5a6b9f2245e359e1938f1ab10f960"
|
||||
|
||||
exec-sh@^0.2.0:
|
||||
version "0.2.1"
|
||||
@@ -1991,15 +1991,15 @@ expand-range@^1.8.1:
|
||||
dependencies:
|
||||
fill-range "^2.1.0"
|
||||
|
||||
expect@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-22.1.0.tgz#f8f9b019ab275d859cbefed531fbaefe8972431d"
|
||||
expect@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/expect/-/expect-22.2.2.tgz#6cb6ae2eeb651a4187b9096de70333a018fab63f"
|
||||
dependencies:
|
||||
ansi-styles "^3.2.0"
|
||||
jest-diff "^22.1.0"
|
||||
jest-get-type "^22.1.0"
|
||||
jest-matcher-utils "^22.1.0"
|
||||
jest-message-util "^22.1.0"
|
||||
jest-matcher-utils "^22.2.0"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-regex-util "^22.1.0"
|
||||
|
||||
express-session@~1.11.3:
|
||||
@@ -2916,15 +2916,15 @@ istanbul-reports@^1.1.3:
|
||||
dependencies:
|
||||
handlebars "^4.0.3"
|
||||
|
||||
jest-changed-files@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.1.4.tgz#1f7844bcb739dec07e5899a633c0cb6d5069834e"
|
||||
jest-changed-files@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.2.0.tgz#517610c4a8ca0925bdc88b0ca53bd678aa8d019e"
|
||||
dependencies:
|
||||
throat "^4.0.0"
|
||||
|
||||
jest-cli@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.1.4.tgz#0fe9f3ac881b0cdc00227114c58583a2ebefcc04"
|
||||
jest-cli@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.2.2.tgz#4431a93a29549da5dcb6d4a41dd03503c9198cd6"
|
||||
dependencies:
|
||||
ansi-escapes "^3.0.0"
|
||||
chalk "^2.0.1"
|
||||
@@ -2937,21 +2937,21 @@ jest-cli@^22.1.4:
|
||||
istanbul-lib-coverage "^1.1.1"
|
||||
istanbul-lib-instrument "^1.8.0"
|
||||
istanbul-lib-source-maps "^1.2.1"
|
||||
jest-changed-files "^22.1.4"
|
||||
jest-config "^22.1.4"
|
||||
jest-environment-jsdom "^22.1.4"
|
||||
jest-changed-files "^22.2.0"
|
||||
jest-config "^22.2.2"
|
||||
jest-environment-jsdom "^22.2.2"
|
||||
jest-get-type "^22.1.0"
|
||||
jest-haste-map "^22.1.0"
|
||||
jest-message-util "^22.1.0"
|
||||
jest-haste-map "^22.2.2"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-regex-util "^22.1.0"
|
||||
jest-resolve-dependencies "^22.1.0"
|
||||
jest-runner "^22.1.4"
|
||||
jest-runtime "^22.1.4"
|
||||
jest-snapshot "^22.1.2"
|
||||
jest-util "^22.1.4"
|
||||
jest-worker "^22.1.0"
|
||||
jest-runner "^22.2.2"
|
||||
jest-runtime "^22.2.2"
|
||||
jest-snapshot "^22.2.0"
|
||||
jest-util "^22.2.2"
|
||||
jest-worker "^22.2.2"
|
||||
micromatch "^2.3.11"
|
||||
node-notifier "^5.1.2"
|
||||
node-notifier "^5.2.1"
|
||||
realpath-native "^1.0.0"
|
||||
rimraf "^2.5.4"
|
||||
slash "^1.0.0"
|
||||
@@ -2960,20 +2960,20 @@ jest-cli@^22.1.4:
|
||||
which "^1.2.12"
|
||||
yargs "^10.0.3"
|
||||
|
||||
jest-config@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.1.4.tgz#075ffacce83c3e38cf85b1b9ba0d21bd3ee27ad0"
|
||||
jest-config@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.2.2.tgz#6b8ed615bc51239847d15460086f174dad4a7015"
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
glob "^7.1.1"
|
||||
jest-environment-jsdom "^22.1.4"
|
||||
jest-environment-node "^22.1.4"
|
||||
jest-environment-jsdom "^22.2.2"
|
||||
jest-environment-node "^22.2.2"
|
||||
jest-get-type "^22.1.0"
|
||||
jest-jasmine2 "^22.1.4"
|
||||
jest-jasmine2 "^22.2.2"
|
||||
jest-regex-util "^22.1.0"
|
||||
jest-resolve "^22.1.4"
|
||||
jest-util "^22.1.4"
|
||||
jest-validate "^22.1.2"
|
||||
jest-resolve "^22.2.2"
|
||||
jest-util "^22.2.2"
|
||||
jest-validate "^22.2.2"
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-diff@^22.1.0:
|
||||
@@ -2985,7 +2985,7 @@ jest-diff@^22.1.0:
|
||||
jest-get-type "^22.1.0"
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-docblock@22.1.0, jest-docblock@^22.1.0:
|
||||
jest-docblock@22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.1.0.tgz#3fe5986d5444cbcb149746eb4b07c57c5a464dfd"
|
||||
dependencies:
|
||||
@@ -2995,20 +2995,26 @@ jest-docblock@^21.0.0:
|
||||
version "21.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
|
||||
|
||||
jest-environment-jsdom@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.1.4.tgz#704518ce8375f7ec5de048d1e9c4268b08a03e00"
|
||||
jest-docblock@^22.1.0, jest-docblock@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.2.2.tgz#617f13edb16ec64202002b3c336cd14ae36c0631"
|
||||
dependencies:
|
||||
jest-mock "^22.1.0"
|
||||
jest-util "^22.1.4"
|
||||
detect-newline "^2.1.0"
|
||||
|
||||
jest-environment-jsdom@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.2.2.tgz#3513ccdccc2bc41daf9cdee199b7069b0d9feebc"
|
||||
dependencies:
|
||||
jest-mock "^22.2.0"
|
||||
jest-util "^22.2.2"
|
||||
jsdom "^11.5.1"
|
||||
|
||||
jest-environment-node@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.1.4.tgz#0f2946e8f8686ce6c5d8fa280ce1cd8d58e869eb"
|
||||
jest-environment-node@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.2.2.tgz#570896eef2dd0f939c71bd5712ef4321958c1270"
|
||||
dependencies:
|
||||
jest-mock "^22.1.0"
|
||||
jest-util "^22.1.4"
|
||||
jest-mock "^22.2.0"
|
||||
jest-util "^22.2.2"
|
||||
|
||||
jest-get-type@^21.2.0:
|
||||
version "21.2.0"
|
||||
@@ -3018,7 +3024,7 @@ jest-get-type@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.1.0.tgz#4e90af298ed6181edc85d2da500dbd2753e0d5a9"
|
||||
|
||||
jest-haste-map@22.1.0, jest-haste-map@^22.1.0:
|
||||
jest-haste-map@22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.1.0.tgz#1174c6ff393f9818ebf1163710d8868b5370da2a"
|
||||
dependencies:
|
||||
@@ -3029,20 +3035,31 @@ jest-haste-map@22.1.0, jest-haste-map@^22.1.0:
|
||||
micromatch "^2.3.11"
|
||||
sane "^2.0.0"
|
||||
|
||||
jest-jasmine2@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.1.4.tgz#cada0baf50a220c616a9575728b80d4ddedebe8b"
|
||||
jest-haste-map@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.2.2.tgz#9d3d5a14bd5e05ab9176979f2a5fbb4ddc80eb20"
|
||||
dependencies:
|
||||
fb-watchman "^2.0.0"
|
||||
graceful-fs "^4.1.11"
|
||||
jest-docblock "^22.2.2"
|
||||
jest-worker "^22.2.2"
|
||||
micromatch "^2.3.11"
|
||||
sane "^2.0.0"
|
||||
|
||||
jest-jasmine2@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.2.2.tgz#9065255c8f635ae9dfa33fc66068f59adf53c9aa"
|
||||
dependencies:
|
||||
callsites "^2.0.0"
|
||||
chalk "^2.0.1"
|
||||
co "^4.6.0"
|
||||
expect "^22.1.0"
|
||||
expect "^22.2.2"
|
||||
graceful-fs "^4.1.11"
|
||||
is-generator-fn "^1.0.0"
|
||||
jest-diff "^22.1.0"
|
||||
jest-matcher-utils "^22.1.0"
|
||||
jest-message-util "^22.1.0"
|
||||
jest-snapshot "^22.1.2"
|
||||
jest-matcher-utils "^22.2.0"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-snapshot "^22.2.0"
|
||||
source-map-support "^0.5.0"
|
||||
|
||||
jest-leak-detector@^22.1.0:
|
||||
@@ -3051,17 +3068,17 @@ jest-leak-detector@^22.1.0:
|
||||
dependencies:
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-matcher-utils@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.1.0.tgz#e164665b5d313636ac29f7f6fe9ef0a6ce04febc"
|
||||
jest-matcher-utils@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.2.0.tgz#5390f823c18c748543d463825aa8e4df0db253ca"
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
jest-get-type "^22.1.0"
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-message-util@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.1.0.tgz#51ba0794cb6e579bfc4e9adfac452f9f1a0293fc"
|
||||
jest-message-util@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.2.0.tgz#84a6bb34186d8b9af7e0732fabbef63f7355f7b2"
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0-beta.35"
|
||||
chalk "^2.0.1"
|
||||
@@ -3069,9 +3086,9 @@ jest-message-util@^22.1.0:
|
||||
slash "^1.0.0"
|
||||
stack-utils "^1.0.1"
|
||||
|
||||
jest-mock@^22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.1.0.tgz#87ec21c0599325671c9a23ad0e05c86fb5879b61"
|
||||
jest-mock@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.2.0.tgz#444b3f9488a7473adae09bc8a77294afded397a7"
|
||||
|
||||
jest-regex-util@^22.1.0:
|
||||
version "22.1.0"
|
||||
@@ -3083,45 +3100,45 @@ jest-resolve-dependencies@^22.1.0:
|
||||
dependencies:
|
||||
jest-regex-util "^22.1.0"
|
||||
|
||||
jest-resolve@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.1.4.tgz#72b9b371eaac48f84aad4ad732222ffe37692602"
|
||||
jest-resolve@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.2.2.tgz#6f49d91e3680c86a4d5e5f72ccdab3996d1cbc19"
|
||||
dependencies:
|
||||
browser-resolve "^1.11.2"
|
||||
chalk "^2.0.1"
|
||||
|
||||
jest-runner@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.1.4.tgz#e039039110cb1b31febc0f99e349bf7c94304a2f"
|
||||
jest-runner@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.2.2.tgz#17fff27a61b63b58cf104c9cdcc0fdfccd3878ce"
|
||||
dependencies:
|
||||
exit "^0.1.2"
|
||||
jest-config "^22.1.4"
|
||||
jest-docblock "^22.1.0"
|
||||
jest-haste-map "^22.1.0"
|
||||
jest-jasmine2 "^22.1.4"
|
||||
jest-config "^22.2.2"
|
||||
jest-docblock "^22.2.2"
|
||||
jest-haste-map "^22.2.2"
|
||||
jest-jasmine2 "^22.2.2"
|
||||
jest-leak-detector "^22.1.0"
|
||||
jest-message-util "^22.1.0"
|
||||
jest-runtime "^22.1.4"
|
||||
jest-util "^22.1.4"
|
||||
jest-worker "^22.1.0"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-runtime "^22.2.2"
|
||||
jest-util "^22.2.2"
|
||||
jest-worker "^22.2.2"
|
||||
throat "^4.0.0"
|
||||
|
||||
jest-runtime@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.1.4.tgz#1474d9f5cda518b702e0b25a17d4ef3fc563a20c"
|
||||
jest-runtime@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.2.2.tgz#256d0efb65deae1c23b819d88cec5ab43d7a4ed6"
|
||||
dependencies:
|
||||
babel-core "^6.0.0"
|
||||
babel-jest "^22.1.0"
|
||||
babel-jest "^22.2.2"
|
||||
babel-plugin-istanbul "^4.1.5"
|
||||
chalk "^2.0.1"
|
||||
convert-source-map "^1.4.0"
|
||||
exit "^0.1.2"
|
||||
graceful-fs "^4.1.11"
|
||||
jest-config "^22.1.4"
|
||||
jest-haste-map "^22.1.0"
|
||||
jest-config "^22.2.2"
|
||||
jest-haste-map "^22.2.2"
|
||||
jest-regex-util "^22.1.0"
|
||||
jest-resolve "^22.1.4"
|
||||
jest-util "^22.1.4"
|
||||
jest-resolve "^22.2.2"
|
||||
jest-util "^22.2.2"
|
||||
json-stable-stringify "^1.0.1"
|
||||
micromatch "^2.3.11"
|
||||
realpath-native "^1.0.0"
|
||||
@@ -3130,27 +3147,27 @@ jest-runtime@^22.1.4:
|
||||
write-file-atomic "^2.1.0"
|
||||
yargs "^10.0.3"
|
||||
|
||||
jest-snapshot@^22.1.2:
|
||||
version "22.1.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-22.1.2.tgz#b270cf6e3098f33aceeafda02b13eb0933dc6139"
|
||||
jest-snapshot@^22.2.0:
|
||||
version "22.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-22.2.0.tgz#0c0ba152d296ef70fa198cc84977a2cc269ee4cf"
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
jest-diff "^22.1.0"
|
||||
jest-matcher-utils "^22.1.0"
|
||||
jest-matcher-utils "^22.2.0"
|
||||
mkdirp "^0.5.1"
|
||||
natural-compare "^1.4.0"
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-util@^22.1.4:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.1.4.tgz#ac8cbd43ee654102f1941f3f0e9d1d789a8b6a9b"
|
||||
jest-util@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.2.2.tgz#335484b6aeae0c5a1ae498401630324977fe3465"
|
||||
dependencies:
|
||||
callsites "^2.0.0"
|
||||
chalk "^2.0.1"
|
||||
graceful-fs "^4.1.11"
|
||||
is-ci "^1.0.10"
|
||||
jest-message-util "^22.1.0"
|
||||
jest-validate "^22.1.2"
|
||||
jest-message-util "^22.2.0"
|
||||
jest-validate "^22.2.2"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
jest-validate@^21.1.0:
|
||||
@@ -3162,26 +3179,33 @@ jest-validate@^21.1.0:
|
||||
leven "^2.1.0"
|
||||
pretty-format "^21.2.1"
|
||||
|
||||
jest-validate@^22.1.2:
|
||||
version "22.1.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.1.2.tgz#c3b06bcba7bd9a850919fe336b5f2a8c3a239404"
|
||||
jest-validate@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.2.2.tgz#9cdce422c93cc28395e907ac6bbc929158d9a6ba"
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
jest-get-type "^22.1.0"
|
||||
leven "^2.1.0"
|
||||
pretty-format "^22.1.0"
|
||||
|
||||
jest-worker@22.1.0, jest-worker@^22.1.0:
|
||||
jest-worker@22.1.0:
|
||||
version "22.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.1.0.tgz#0987832fe58fbdc205357f4c19b992446368cafb"
|
||||
dependencies:
|
||||
merge-stream "^1.0.1"
|
||||
|
||||
jest@^22.1.3:
|
||||
version "22.1.4"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-22.1.4.tgz#9ec71373a38f40ff92a3e5e96ae85687c181bb72"
|
||||
jest-worker@^22.1.0, jest-worker@^22.2.2:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.2.2.tgz#c1f5dc39976884b81f68ec50cb8532b2cbab3390"
|
||||
dependencies:
|
||||
jest-cli "^22.1.4"
|
||||
merge-stream "^1.0.1"
|
||||
|
||||
jest@^22.1.3:
|
||||
version "22.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-22.2.2.tgz#26aca0f5e4eaa76d52f2792b14033a3d1e7be2bd"
|
||||
dependencies:
|
||||
import-local "^1.0.0"
|
||||
jest-cli "^22.2.2"
|
||||
|
||||
js-tokens@^3.0.0, js-tokens@^3.0.2:
|
||||
version "3.0.2"
|
||||
@@ -3495,8 +3519,8 @@ lodash.keys@^3.0.0:
|
||||
lodash.isarray "^3.0.0"
|
||||
|
||||
lodash.merge@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
|
||||
|
||||
lodash.pad@^4.1.0:
|
||||
version "4.5.1"
|
||||
@@ -3548,8 +3572,8 @@ lodash@^3.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
|
||||
|
||||
lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.16.6, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.6.1:
|
||||
version "4.17.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
||||
version "4.17.5"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
|
||||
|
||||
log-symbols@^1.0.2:
|
||||
version "1.0.2"
|
||||
@@ -3853,7 +3877,7 @@ node-int64@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
|
||||
|
||||
node-notifier@^5.1.2:
|
||||
node-notifier@^5.1.2, node-notifier@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.2.1.tgz#fa313dd08f5517db0e2502e5758d664ac69f9dea"
|
||||
dependencies:
|
||||
@@ -4299,9 +4323,9 @@ private@^0.1.6, private@^0.1.7:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||
|
||||
process-nextick-args@~1.0.6:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
|
||||
process@~0.5.1:
|
||||
version "0.5.2"
|
||||
@@ -4561,13 +4585,13 @@ read-pkg@^2.0.0:
|
||||
path-type "^2.0.0"
|
||||
|
||||
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~1.0.6"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.0.3"
|
||||
util-deprecate "~1.0.1"
|
||||
@@ -4874,8 +4898,8 @@ safe-buffer@~5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
|
||||
|
||||
sane@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/sane/-/sane-2.3.0.tgz#3f3df584abf69e63d4bb74f0f8c42468e4d7d46b"
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/sane/-/sane-2.4.1.tgz#29f991208cf28636720efdc584293e7fd66663a5"
|
||||
dependencies:
|
||||
anymatch "^1.3.0"
|
||||
exec-sh "^0.2.0"
|
||||
@@ -5044,7 +5068,7 @@ source-map@^0.4.4:
|
||||
dependencies:
|
||||
amdefine ">=0.0.4"
|
||||
|
||||
source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.6:
|
||||
source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.6:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
|
||||
@@ -5163,8 +5187,8 @@ string_decoder@~1.0.3:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
stringify-object@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.1.tgz#2720c2eff940854c819f6ee252aaeb581f30624d"
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.2.tgz#9853052e5a88fb605a44cd27445aa257ad7ffbcd"
|
||||
dependencies:
|
||||
get-own-enumerable-property-symbols "^2.0.1"
|
||||
is-obj "^1.0.1"
|
||||
@@ -5374,10 +5398,10 @@ ua-parser-js@^0.7.9:
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
||||
|
||||
uglify-es@^3.1.9:
|
||||
version "3.3.9"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
|
||||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.10.tgz#8b0b7992cebe20edc26de1bf325cef797b8f3fa5"
|
||||
dependencies:
|
||||
commander "~2.13.0"
|
||||
commander "~2.14.1"
|
||||
source-map "~0.6.1"
|
||||
|
||||
uglify-js@^2.6:
|
||||
|
||||