mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-05-11 15:46:25 +08:00
[BREAKING] New createNavigator API (#3392)
* New createNavigator and View API See the RFC here: https://github.com/react-navigation/rfcs/blob/master/text/0002-navigator-view-api.md * shattered dreams of flow * fix export * Fix tab view issues found by brent
This commit is contained in:
committed by
Brent Vatne
parent
6785729fb5
commit
e27ad22c57
@@ -18,7 +18,6 @@ import {
|
||||
createNavigationContainer,
|
||||
SafeAreaView,
|
||||
TabRouter,
|
||||
addNavigationHelpers,
|
||||
} from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
@@ -66,19 +65,14 @@ const CustomTabBar = ({ navigation }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const CustomTabView = ({ router, navigation }) => {
|
||||
const CustomTabView = ({ descriptors, navigation }) => {
|
||||
const { routes, index } = navigation.state;
|
||||
const ActiveScreen = router.getComponentForRouteName(routes[index].routeName);
|
||||
const descriptor = descriptors[routes[index].key];
|
||||
const ActiveScreen = descriptor.getComponent();
|
||||
return (
|
||||
<SafeAreaView forceInset={{ top: 'always' }}>
|
||||
<CustomTabBar navigation={navigation} />
|
||||
<ActiveScreen
|
||||
navigation={addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: routes[index],
|
||||
})}
|
||||
screenProps={{}}
|
||||
/>
|
||||
<ActiveScreen navigation={descriptor.navigation} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
@@ -105,7 +99,7 @@ const CustomTabRouter = TabRouter(
|
||||
);
|
||||
|
||||
const CustomTabs = createNavigationContainer(
|
||||
createNavigator(CustomTabRouter)(CustomTabView)
|
||||
createNavigator(CustomTabView, CustomTabRouter, {})
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
SafeAreaView,
|
||||
StackRouter,
|
||||
createNavigationContainer,
|
||||
addNavigationHelpers,
|
||||
createNavigator,
|
||||
} from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
@@ -45,11 +44,12 @@ const MySettingsScreen = ({ navigation }) => (
|
||||
|
||||
class CustomNavigationView extends Component {
|
||||
render() {
|
||||
const { navigation, router } = this.props;
|
||||
const { navigation, router, descriptors } = this.props;
|
||||
|
||||
return (
|
||||
<Transitioner
|
||||
configureTransition={this._configureTransition}
|
||||
descriptors={descriptors}
|
||||
navigation={navigation}
|
||||
render={this._render}
|
||||
/>
|
||||
@@ -86,16 +86,10 @@ class CustomNavigationView extends Component {
|
||||
transform: [{ scale: animatedValue }],
|
||||
};
|
||||
|
||||
// The prop `router` is populated when we call `createNavigator`.
|
||||
const Scene = router.getComponentForRouteName(scene.route.routeName);
|
||||
const Scene = scene.descriptor.getComponent();
|
||||
return (
|
||||
<Animated.View key={index} style={[styles.view, animation]}>
|
||||
<Scene
|
||||
navigation={addNavigationHelpers({
|
||||
...navigation,
|
||||
state: routes[index],
|
||||
})}
|
||||
/>
|
||||
<Scene navigation={scene.descriptor.navigation} />
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
@@ -107,7 +101,7 @@ const CustomRouter = StackRouter({
|
||||
});
|
||||
|
||||
const CustomTransitioner = createNavigationContainer(
|
||||
createNavigator(CustomRouter)(CustomNavigationView)
|
||||
createNavigator(CustomNavigationView, CustomRouter, {})
|
||||
);
|
||||
|
||||
export default CustomTransitioner;
|
||||
|
||||
61
flow/react-navigation.js
vendored
61
flow/react-navigation.js
vendored
@@ -260,7 +260,8 @@ declare module 'react-navigation' {
|
||||
|
||||
declare export type NavigationComponent =
|
||||
| NavigationScreenComponent<NavigationRoute, *, *>
|
||||
| NavigationContainer<NavigationStateRoute, *, *>;
|
||||
| NavigationContainer<*, *, *>
|
||||
| any;
|
||||
|
||||
declare export type NavigationScreenComponent<
|
||||
Route: NavigationRoute,
|
||||
@@ -492,29 +493,6 @@ declare module 'react-navigation' {
|
||||
navigationOptions?: O,
|
||||
}>;
|
||||
|
||||
//declare export type NavigationNavigatorProps<O: {}, S: {}> =
|
||||
// | {}
|
||||
// | { navigation: NavigationScreenProp<S> }
|
||||
// | { screenProps: {} }
|
||||
// | { navigationOptions: O }
|
||||
// | {
|
||||
// navigation: NavigationScreenProp<S>,
|
||||
// screenProps: {},
|
||||
// }
|
||||
// | {
|
||||
// navigation: NavigationScreenProp<S>,
|
||||
// navigationOptions: O,
|
||||
// }
|
||||
// | {
|
||||
// screenProps: {},
|
||||
// navigationOptions: O,
|
||||
// }
|
||||
// | {
|
||||
// navigation: NavigationScreenProp<S>,
|
||||
// screenProps: {},
|
||||
// navigationOptions: O,
|
||||
// };
|
||||
|
||||
/**
|
||||
* Navigation container
|
||||
*/
|
||||
@@ -689,10 +667,6 @@ declare module 'react-navigation' {
|
||||
) => NavigationState,
|
||||
};
|
||||
|
||||
declare export function addNavigationHelpers<S: {}>(
|
||||
navigation: NavigationProp<S>
|
||||
): NavigationScreenProp<S>;
|
||||
|
||||
declare export var NavigationActions: {
|
||||
BACK: 'Navigation/BACK',
|
||||
INIT: 'Navigation/INIT',
|
||||
@@ -740,23 +714,24 @@ declare module 'react-navigation' {
|
||||
declare type _RouterProp<S: NavigationState, O: {}> = {
|
||||
router: NavigationRouter<S, O>,
|
||||
};
|
||||
declare type _NavigatorCreator<
|
||||
NavigationViewProps: {},
|
||||
S: NavigationState,
|
||||
O: {}
|
||||
> = (
|
||||
NavigationView: React$ComponentType<_RouterProp<S, O> & NavigationViewProps>
|
||||
) => NavigationNavigator<S, O, NavigationViewProps>;
|
||||
declare export function createNavigator<
|
||||
S: NavigationState,
|
||||
O: {},
|
||||
NavigatorConfig: {},
|
||||
NavigationViewProps: NavigationNavigatorProps<O, S>
|
||||
>(
|
||||
|
||||
declare type NavigationDescriptor = {
|
||||
key: string,
|
||||
state: NavigationLeafRoute | NavigationStateRoute,
|
||||
navigation: NavigationScreenProp<*>,
|
||||
getComponent: () => React$ComponentType<{}>,
|
||||
};
|
||||
|
||||
declare type NavigationView<O, S> = React$ComponentType<{
|
||||
descriptors: { [key: string]: NavigationDescriptor },
|
||||
navigation: NavigationScreenProp<S>,
|
||||
}>;
|
||||
|
||||
declare export function createNavigator<O: *, S: *, NavigatorConfig: *>(
|
||||
view: NavigationView<O, S>,
|
||||
router: NavigationRouter<S, O>,
|
||||
routeConfigs?: NavigationRouteConfigMap,
|
||||
navigatorConfig?: NavigatorConfig
|
||||
): _NavigatorCreator<NavigationViewProps, S, O>;
|
||||
): any;
|
||||
|
||||
declare export function StackNavigator(
|
||||
routeConfigMap: NavigationRouteConfigMap,
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'react-native';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
import NavigationActions from '../NavigationActions';
|
||||
import StackNavigator from '../navigators/StackNavigator';
|
||||
import StackNavigator from '../navigators/createStackNavigator';
|
||||
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
|
||||
@@ -49,57 +49,37 @@ const DefaultDrawerConfig = {
|
||||
const DrawerNavigator = (routeConfigs, config = {}) => {
|
||||
const mergedConfig = { ...DefaultDrawerConfig, ...config };
|
||||
const {
|
||||
containerConfig,
|
||||
drawerWidth,
|
||||
drawerLockMode,
|
||||
contentComponent,
|
||||
contentOptions,
|
||||
drawerPosition,
|
||||
useNativeAnimations,
|
||||
drawerBackgroundColor,
|
||||
drawerOpenRoute,
|
||||
drawerCloseRoute,
|
||||
drawerToggleRoute,
|
||||
...tabsConfig
|
||||
order,
|
||||
paths,
|
||||
initialRouteName,
|
||||
backBehavior,
|
||||
...drawerConfig
|
||||
} = mergedConfig;
|
||||
|
||||
const tabsConfig = {
|
||||
order,
|
||||
paths,
|
||||
initialRouteName,
|
||||
backBehavior,
|
||||
};
|
||||
const contentRouter = TabRouter(routeConfigs, tabsConfig);
|
||||
const drawerRouter = TabRouter(
|
||||
{
|
||||
[drawerCloseRoute]: {
|
||||
screen: createNavigator(contentRouter, routeConfigs, config)(props => (
|
||||
<DrawerScreen {...props} />
|
||||
)),
|
||||
[drawerConfig.drawerCloseRoute]: {
|
||||
screen: createNavigator(DrawerScreen, contentRouter, config),
|
||||
},
|
||||
[drawerOpenRoute]: {
|
||||
[drawerConfig.drawerOpenRoute]: {
|
||||
screen: () => null,
|
||||
},
|
||||
[drawerToggleRoute]: {
|
||||
[drawerConfig.drawerToggleRoute]: {
|
||||
screen: () => null,
|
||||
},
|
||||
},
|
||||
{
|
||||
initialRouteName: drawerCloseRoute,
|
||||
initialRouteName: drawerConfig.drawerCloseRoute,
|
||||
}
|
||||
);
|
||||
|
||||
const navigator = createNavigator(drawerRouter, routeConfigs, config)(
|
||||
props => (
|
||||
<DrawerView
|
||||
{...props}
|
||||
drawerBackgroundColor={drawerBackgroundColor}
|
||||
drawerLockMode={drawerLockMode}
|
||||
useNativeAnimations={useNativeAnimations}
|
||||
drawerWidth={drawerWidth}
|
||||
contentComponent={contentComponent}
|
||||
contentOptions={contentOptions}
|
||||
drawerPosition={drawerPosition}
|
||||
drawerOpenRoute={drawerOpenRoute}
|
||||
drawerCloseRoute={drawerCloseRoute}
|
||||
drawerToggleRoute={drawerToggleRoute}
|
||||
/>
|
||||
)
|
||||
);
|
||||
const navigator = createNavigator(DrawerView, drawerRouter, drawerConfig);
|
||||
|
||||
return createNavigationContainer(navigator);
|
||||
};
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import React from 'react';
|
||||
import createNavigationContainer from '../createNavigationContainer';
|
||||
import createNavigator from './createNavigator';
|
||||
import CardStackTransitioner from '../views/CardStack/CardStackTransitioner';
|
||||
import StackRouter from '../routers/StackRouter';
|
||||
import NavigationActions from '../NavigationActions';
|
||||
|
||||
// A stack navigators props are the intersection between
|
||||
// the base navigator props (navgiation, screenProps, etc)
|
||||
// and the view's props
|
||||
|
||||
export default (routeConfigMap, stackConfig = {}) => {
|
||||
const {
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
headerMode,
|
||||
headerTransitionPreset,
|
||||
mode,
|
||||
cardStyle,
|
||||
transitionConfig,
|
||||
onTransitionStart,
|
||||
onTransitionEnd,
|
||||
navigationOptions,
|
||||
} = stackConfig;
|
||||
|
||||
const stackRouterConfig = {
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
navigationOptions,
|
||||
};
|
||||
|
||||
const router = StackRouter(routeConfigMap, stackRouterConfig);
|
||||
|
||||
// Create a navigator with CardStackTransitioner as the view
|
||||
const navigator = createNavigator(router, routeConfigMap, stackConfig)(
|
||||
props => (
|
||||
<CardStackTransitioner
|
||||
{...props}
|
||||
headerMode={headerMode}
|
||||
headerTransitionPreset={headerTransitionPreset}
|
||||
mode={mode}
|
||||
cardStyle={cardStyle}
|
||||
transitionConfig={transitionConfig}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={(lastTransition, transition) => {
|
||||
const { state, dispatch } = props.navigation;
|
||||
dispatch(NavigationActions.completeTransition({ key: state.key }));
|
||||
onTransitionEnd && onTransitionEnd();
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
return createNavigationContainer(navigator);
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
import StackNavigator from '../StackNavigator';
|
||||
import StackNavigator from '../createStackNavigator';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
header: {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
import TabNavigator from '../TabNavigator';
|
||||
import TabNavigator from '../createTabNavigator';
|
||||
|
||||
class HomeScreen extends Component {
|
||||
static navigationOptions = ({ navigation }) => ({
|
||||
|
||||
@@ -48,7 +48,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
pointerEvents="auto"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#EFEFF4",
|
||||
"backgroundColor": "#E9E9EF",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
@@ -234,7 +234,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
pointerEvents="auto"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#EFEFF4",
|
||||
"backgroundColor": "#E9E9EF",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
|
||||
@@ -20,60 +20,110 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
style={
|
||||
<RCTScrollView
|
||||
DEPRECATED_sendUpdatedChildFrames={false}
|
||||
alwaysBounceHorizontal={false}
|
||||
alwaysBounceVertical={false}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
bounces={false}
|
||||
contentContainerStyle={
|
||||
Object {
|
||||
"alignItems": "stretch",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
contentOffset={
|
||||
Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
directionalLockEnabled={true}
|
||||
horizontal={true}
|
||||
keyboardDismissMode="on-drag"
|
||||
keyboardShouldPersistTaps="always"
|
||||
onContentSizeChange={null}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={undefined}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
onScrollShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
onTouchCancel={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchMove={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
overScrollMode="never"
|
||||
pagingEnabled={true}
|
||||
scrollEnabled={undefined}
|
||||
scrollEventThrottle={1}
|
||||
scrollsToTop={false}
|
||||
sendMomentumEvents={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"flexGrow": 1,
|
||||
"flexShrink": 1,
|
||||
"overflow": "scroll",
|
||||
},
|
||||
Object {
|
||||
"flex": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<RCTScrollContentView
|
||||
collapsable={false}
|
||||
removeClippedSubviews={undefined}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
},
|
||||
Object {
|
||||
"flex": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollContentView>
|
||||
</RCTScrollView>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
style={undefined}
|
||||
|
||||
@@ -1,19 +1,49 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Creates a navigator based on a router and a view that renders the screens.
|
||||
*/
|
||||
export default function createNavigator(router, routeConfigs, navigatorConfig) {
|
||||
return NavigationView => {
|
||||
class Navigator extends React.Component {
|
||||
static router = router;
|
||||
static navigationOptions = null;
|
||||
import getChildEventSubscriber from '../getChildEventSubscriber';
|
||||
import addNavigationHelpers from '../addNavigationHelpers';
|
||||
|
||||
render() {
|
||||
return <NavigationView {...this.props} router={router} />;
|
||||
}
|
||||
function createNavigator(NavigatorView, router, navigationConfig) {
|
||||
class Navigator extends React.Component {
|
||||
static router = router;
|
||||
static navigationOptions = null;
|
||||
|
||||
render() {
|
||||
const { navigation, screenProps } = this.props;
|
||||
const { dispatch, state, addListener } = navigation;
|
||||
const { routes } = state;
|
||||
|
||||
const descriptors = {};
|
||||
routes.forEach(route => {
|
||||
const getComponent = () =>
|
||||
router.getComponentForRouteName(route.routeName);
|
||||
|
||||
const childNavigation = addNavigationHelpers({
|
||||
dispatch,
|
||||
state: route,
|
||||
addListener: getChildEventSubscriber(addListener, route.key),
|
||||
});
|
||||
const options = router.getScreenOptions(childNavigation, screenProps);
|
||||
descriptors[route.key] = {
|
||||
key: route.key,
|
||||
getComponent,
|
||||
options,
|
||||
state: route,
|
||||
navigation: childNavigation,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<NavigatorView
|
||||
screenProps={screenProps}
|
||||
navigation={navigation}
|
||||
navigationConfig={navigationConfig}
|
||||
descriptors={descriptors}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return Navigator;
|
||||
};
|
||||
}
|
||||
return Navigator;
|
||||
}
|
||||
|
||||
export default createNavigator;
|
||||
|
||||
31
src/navigators/createStackNavigator.js
Normal file
31
src/navigators/createStackNavigator.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as React from 'react';
|
||||
import createNavigationContainer from '../createNavigationContainer';
|
||||
import createNavigator from './createNavigator';
|
||||
import StackView from '../views/StackView/StackView';
|
||||
import StackRouter from '../routers/StackRouter';
|
||||
|
||||
function createStackNavigator(routeConfigMap, stackConfig = {}) {
|
||||
const {
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
navigationOptions,
|
||||
} = stackConfig;
|
||||
|
||||
const stackRouterConfig = {
|
||||
initialRouteName,
|
||||
initialRouteParams,
|
||||
paths,
|
||||
navigationOptions,
|
||||
};
|
||||
|
||||
const router = StackRouter(routeConfigMap, stackRouterConfig);
|
||||
|
||||
// Create a navigator with StackView as the view
|
||||
const Navigator = createNavigator(StackView, router, stackConfig);
|
||||
|
||||
// HOC to provide the navigation prop for the top-level navigator (when the prop is missing)
|
||||
return createNavigationContainer(Navigator);
|
||||
}
|
||||
|
||||
export default createStackNavigator;
|
||||
@@ -8,42 +8,13 @@ import TabView from '../views/TabView/TabView';
|
||||
import TabBarTop from '../views/TabView/TabBarTop';
|
||||
import TabBarBottom from '../views/TabView/TabBarBottom';
|
||||
|
||||
// A tab navigators props are the intersection between
|
||||
// the base navigator props (navgiation, screenProps, etc)
|
||||
// and the view's props
|
||||
|
||||
const TabNavigator = (routeConfigs, config = {}) => {
|
||||
// Use the look native to the platform by default
|
||||
const mergedConfig = { ...TabNavigator.Presets.Default, ...config };
|
||||
const {
|
||||
tabBarComponent,
|
||||
tabBarPosition,
|
||||
tabBarOptions,
|
||||
lazy,
|
||||
removeClippedSubviews,
|
||||
swipeEnabled,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
initialLayout,
|
||||
...tabsConfig
|
||||
} = mergedConfig;
|
||||
const tabsConfig = { ...TabNavigator.Presets.Default, ...config };
|
||||
|
||||
const router = TabRouter(routeConfigs, tabsConfig);
|
||||
|
||||
const navigator = createNavigator(router, routeConfigs, config)(props => (
|
||||
<TabView
|
||||
{...props}
|
||||
lazy={lazy}
|
||||
removeClippedSubviews={removeClippedSubviews}
|
||||
tabBarComponent={tabBarComponent}
|
||||
tabBarPosition={tabBarPosition}
|
||||
tabBarOptions={tabBarOptions}
|
||||
swipeEnabled={swipeEnabled}
|
||||
animationEnabled={animationEnabled}
|
||||
configureTransition={configureTransition}
|
||||
initialLayout={initialLayout}
|
||||
/>
|
||||
));
|
||||
const navigator = createNavigator(TabView, router, tabsConfig);
|
||||
|
||||
return createNavigationContainer(navigator);
|
||||
};
|
||||
15
src/react-navigation.js
vendored
15
src/react-navigation.js
vendored
@@ -20,10 +20,10 @@ module.exports = {
|
||||
return require('./navigators/createNavigator').default;
|
||||
},
|
||||
get StackNavigator() {
|
||||
return require('./navigators/StackNavigator').default;
|
||||
return require('./navigators/createStackNavigator').default;
|
||||
},
|
||||
get TabNavigator() {
|
||||
return require('./navigators/TabNavigator').default;
|
||||
return require('./navigators/createTabNavigator').default;
|
||||
},
|
||||
get DrawerNavigator() {
|
||||
return require('./navigators/DrawerNavigator').default;
|
||||
@@ -41,14 +41,11 @@ module.exports = {
|
||||
get Transitioner() {
|
||||
return require('./views/Transitioner').default;
|
||||
},
|
||||
get CardStackTransitioner() {
|
||||
return require('./views/CardStack/CardStackTransitioner').default;
|
||||
get StackView() {
|
||||
return require('./views/StackView/StackView').default;
|
||||
},
|
||||
get CardStack() {
|
||||
return require('./views/CardStack/CardStack').default;
|
||||
},
|
||||
get Card() {
|
||||
return require('./views/CardStack/Card').default;
|
||||
get StackViewCard() {
|
||||
return require('./views/StackView/StackViewCard').default;
|
||||
},
|
||||
get SafeAreaView() {
|
||||
return require('react-native-safe-area-view').default;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import invariant from '../utils/invariant';
|
||||
|
||||
import getScreenForRouteName from './getScreenForRouteName';
|
||||
import addNavigationHelpers from '../addNavigationHelpers';
|
||||
import validateScreenOptions from './validateScreenOptions';
|
||||
import getChildEventSubscriber from '../getChildEventSubscriber';
|
||||
|
||||
@@ -38,28 +37,6 @@ export default (routeConfigs, navigatorScreenConfig) => (
|
||||
|
||||
const Component = getScreenForRouteName(routeConfigs, route.routeName);
|
||||
|
||||
let outputConfig = {};
|
||||
|
||||
const router = Component.router;
|
||||
if (router) {
|
||||
const { routes, index } = route;
|
||||
if (!route || !routes || index == null) {
|
||||
throw new Error(
|
||||
`Expect nav state to have routes and index, ${JSON.stringify(route)}`
|
||||
);
|
||||
}
|
||||
const childRoute = routes[index];
|
||||
const childNavigation = addNavigationHelpers({
|
||||
state: childRoute,
|
||||
dispatch,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
childRoute.key
|
||||
),
|
||||
});
|
||||
outputConfig = router.getScreenOptions(childNavigation, screenProps);
|
||||
}
|
||||
|
||||
const routeConfig = routeConfigs[route.routeName];
|
||||
|
||||
const routeScreenConfig = routeConfig.navigationOptions;
|
||||
@@ -67,11 +44,7 @@ export default (routeConfigs, navigatorScreenConfig) => (
|
||||
|
||||
const configOptions = { navigation, screenProps: screenProps || {} };
|
||||
|
||||
outputConfig = applyConfig(
|
||||
navigatorScreenConfig,
|
||||
outputConfig,
|
||||
configOptions
|
||||
);
|
||||
let outputConfig = applyConfig(navigatorScreenConfig, {}, configOptions);
|
||||
outputConfig = applyConfig(
|
||||
componentScreenConfig,
|
||||
outputConfig,
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import React from 'react';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import CardStack from './CardStack';
|
||||
import CardStackStyleInterpolator from './CardStackStyleInterpolator';
|
||||
import Transitioner from '../Transitioner';
|
||||
import TransitionConfigs from './TransitionConfigs';
|
||||
|
||||
const NativeAnimatedModule =
|
||||
NativeModules && NativeModules.NativeAnimatedModule;
|
||||
|
||||
class CardStackTransitioner extends React.Component {
|
||||
static defaultProps = {
|
||||
mode: 'card',
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Transitioner
|
||||
configureTransition={this._configureTransition}
|
||||
navigation={this.props.navigation}
|
||||
render={this._render}
|
||||
onTransitionStart={this.props.onTransitionStart}
|
||||
onTransitionEnd={this.props.onTransitionEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_configureTransition = (
|
||||
// props for the new screen
|
||||
transitionProps,
|
||||
// props for the old screen
|
||||
prevTransitionProps
|
||||
) => {
|
||||
const isModal = this.props.mode === 'modal';
|
||||
// Copy the object so we can assign useNativeDriver below
|
||||
const transitionSpec = {
|
||||
...TransitionConfigs.getTransitionConfig(
|
||||
this.props.transitionConfig,
|
||||
transitionProps,
|
||||
prevTransitionProps,
|
||||
isModal
|
||||
).transitionSpec,
|
||||
};
|
||||
if (
|
||||
!!NativeAnimatedModule &&
|
||||
// Native animation support also depends on the transforms used:
|
||||
CardStackStyleInterpolator.canUseNativeDriver()
|
||||
) {
|
||||
// Internal undocumented prop
|
||||
transitionSpec.useNativeDriver = true;
|
||||
}
|
||||
return transitionSpec;
|
||||
};
|
||||
|
||||
_render = (props, prevProps) => {
|
||||
const {
|
||||
screenProps,
|
||||
headerMode,
|
||||
headerTransitionPreset,
|
||||
mode,
|
||||
router,
|
||||
cardStyle,
|
||||
transitionConfig,
|
||||
} = this.props;
|
||||
return (
|
||||
<CardStack
|
||||
screenProps={screenProps}
|
||||
headerMode={headerMode}
|
||||
headerTransitionPreset={headerTransitionPreset}
|
||||
mode={mode}
|
||||
router={router}
|
||||
cardStyle={cardStyle}
|
||||
transitionConfig={transitionConfig}
|
||||
transitionProps={props}
|
||||
prevTransitionProps={prevProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default CardStackTransitioner;
|
||||
@@ -1,30 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
import SceneView from '../SceneView';
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
|
||||
/**
|
||||
* Component that renders the child screen of the drawer.
|
||||
*/
|
||||
class DrawerScreen extends React.PureComponent {
|
||||
render() {
|
||||
const {
|
||||
router,
|
||||
navigation,
|
||||
childNavigationProps,
|
||||
screenProps,
|
||||
} = this.props;
|
||||
const { descriptors, navigation, screenProps } = this.props;
|
||||
const { routes, index } = navigation.state;
|
||||
const childNavigation = childNavigationProps[routes[index].key];
|
||||
const Content = router.getComponentForRouteName(routes[index].routeName);
|
||||
const descriptor = descriptors[routes[index].key];
|
||||
const Content = descriptor.getComponent();
|
||||
return (
|
||||
<SceneView
|
||||
screenProps={screenProps}
|
||||
component={Content}
|
||||
navigation={childNavigation}
|
||||
navigation={descriptor.navigation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withCachedChildNavigation(DrawerScreen);
|
||||
export default DrawerScreen;
|
||||
|
||||
@@ -2,32 +2,21 @@ import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import invariant from '../../utils/invariant';
|
||||
|
||||
/**
|
||||
* Component that renders the sidebar screen of the drawer.
|
||||
*/
|
||||
|
||||
class DrawerSidebar extends React.PureComponent {
|
||||
_getScreenOptions = routeKey => {
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName(
|
||||
'DrawerClose'
|
||||
);
|
||||
const descriptor = this.props.descriptors[routeKey];
|
||||
invariant(
|
||||
DrawerScreen.router,
|
||||
'NavigationComponent with routeName DrawerClose should be a Navigator'
|
||||
);
|
||||
const { [routeKey]: childNavigation } = this.props.childNavigationProps;
|
||||
return DrawerScreen.router.getScreenOptions(
|
||||
childNavigation.state.index !== undefined // if the child screen is a StackRouter then always show the screen options of its first screen (see #1914)
|
||||
? {
|
||||
...childNavigation,
|
||||
state: { ...childNavigation.state, index: 0 },
|
||||
}
|
||||
: childNavigation,
|
||||
this.props.screenProps
|
||||
descriptor.options,
|
||||
'Cannot access screen descriptor options from drawer sidebar'
|
||||
);
|
||||
return descriptor.options;
|
||||
};
|
||||
|
||||
_getLabel = ({ focused, tintColor, route }) => {
|
||||
@@ -86,6 +75,7 @@ class DrawerSidebar extends React.PureComponent {
|
||||
<ContentComponent
|
||||
{...this.props.contentOptions}
|
||||
navigation={this.props.navigation}
|
||||
descriptors={this.props.descriptors}
|
||||
items={state.routes}
|
||||
activeItemKey={
|
||||
state.routes[state.index] ? state.routes[state.index].key : null
|
||||
@@ -94,7 +84,6 @@ class DrawerSidebar extends React.PureComponent {
|
||||
getLabel={this._getLabel}
|
||||
renderIcon={this._renderIcon}
|
||||
onItemPress={this._onItemPress}
|
||||
router={this.props.router}
|
||||
drawerPosition={this.props.drawerPosition}
|
||||
/>
|
||||
</View>
|
||||
@@ -102,7 +91,7 @@ class DrawerSidebar extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export default withCachedChildNavigation(DrawerSidebar);
|
||||
export default DrawerSidebar;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
||||
@@ -12,14 +12,12 @@ import getChildEventSubscriber from '../../getChildEventSubscriber';
|
||||
export default class DrawerView extends React.PureComponent {
|
||||
state = {
|
||||
drawerWidth:
|
||||
typeof this.props.drawerWidth === 'function'
|
||||
? this.props.drawerWidth()
|
||||
: this.props.drawerWidth,
|
||||
typeof this.props.navigationConfig.drawerWidth === 'function'
|
||||
? this.props.navigationConfig.drawerWidth()
|
||||
: this.props.navigationConfig.drawerWidth,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this._updateScreenNavigation(this.props.navigation);
|
||||
|
||||
Dimensions.addEventListener('change', this._updateWidth);
|
||||
}
|
||||
|
||||
@@ -35,7 +33,7 @@ export default class DrawerView extends React.PureComponent {
|
||||
drawerOpenRoute,
|
||||
drawerCloseRoute,
|
||||
drawerToggleRoute,
|
||||
} = this.props;
|
||||
} = this.props.navigationConfig;
|
||||
const { routes, index } = nextProps.navigation.state;
|
||||
if (routes[index].routeName === drawerOpenRoute) {
|
||||
this._drawer.openDrawer();
|
||||
@@ -49,11 +47,11 @@ export default class DrawerView extends React.PureComponent {
|
||||
this._drawer.closeDrawer();
|
||||
}
|
||||
}
|
||||
this._updateScreenNavigation(nextProps.navigation);
|
||||
}
|
||||
|
||||
_handleDrawerOpen = () => {
|
||||
const { navigation, drawerOpenRoute } = this.props;
|
||||
const { navigation, navigationConfig } = this.props;
|
||||
const { drawerOpenRoute } = navigationConfig;
|
||||
const { routes, index } = navigation.state;
|
||||
if (routes[index].routeName !== drawerOpenRoute) {
|
||||
this.props.navigation.navigate(drawerOpenRoute);
|
||||
@@ -61,39 +59,19 @@ export default class DrawerView extends React.PureComponent {
|
||||
};
|
||||
|
||||
_handleDrawerClose = () => {
|
||||
const { navigation, drawerCloseRoute } = this.props;
|
||||
const { navigation, navigationConfig } = this.props;
|
||||
const { drawerCloseRoute } = navigationConfig;
|
||||
const { routes, index } = navigation.state;
|
||||
if (routes[index].routeName !== drawerCloseRoute) {
|
||||
this.props.navigation.navigate(drawerCloseRoute);
|
||||
}
|
||||
};
|
||||
|
||||
_updateScreenNavigation = navigation => {
|
||||
const { drawerCloseRoute } = this.props;
|
||||
const navigationState = navigation.state.routes.find(
|
||||
route => route.routeName === drawerCloseRoute
|
||||
);
|
||||
if (
|
||||
this._screenNavigationProp &&
|
||||
this._screenNavigationProp.state === navigationState
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._screenNavigationProp = addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: navigationState,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
navigationState.key
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
_updateWidth = () => {
|
||||
const drawerWidth =
|
||||
typeof this.props.drawerWidth === 'function'
|
||||
? this.props.drawerWidth()
|
||||
: this.props.drawerWidth;
|
||||
typeof this.props.navigationConfig.drawerWidth === 'function'
|
||||
? this.props.navigationConfig.drawerWidth()
|
||||
: this.props.navigationConfig.drawerWidth;
|
||||
|
||||
if (this.state.drawerWidth !== drawerWidth) {
|
||||
this.setState({ drawerWidth });
|
||||
@@ -101,34 +79,68 @@ export default class DrawerView extends React.PureComponent {
|
||||
};
|
||||
|
||||
_getNavigationState = navigation => {
|
||||
const { drawerCloseRoute } = this.props;
|
||||
const { drawerCloseRoute } = this.props.navigationConfig;
|
||||
const navigationState = navigation.state.routes.find(
|
||||
route => route.routeName === drawerCloseRoute
|
||||
);
|
||||
|
||||
return navigationState;
|
||||
};
|
||||
|
||||
_renderNavigationView = () => (
|
||||
<DrawerSidebar
|
||||
screenProps={this.props.screenProps}
|
||||
navigation={this._screenNavigationProp}
|
||||
router={this.props.router}
|
||||
contentComponent={this.props.contentComponent}
|
||||
contentOptions={this.props.contentOptions}
|
||||
drawerPosition={this.props.drawerPosition}
|
||||
style={this.props.style}
|
||||
/>
|
||||
);
|
||||
_renderNavigationView = () => {
|
||||
const details = Object.values(this.props.descriptors).find(
|
||||
d => d.state.routeName === this.props.navigationConfig.drawerCloseRoute
|
||||
);
|
||||
|
||||
const router = details.getComponent().router;
|
||||
const { state, addListener, dispatch } = this.props.navigation;
|
||||
const { routes } = details.state;
|
||||
|
||||
const tabDescriptors = {};
|
||||
routes.forEach(route => {
|
||||
const getComponent = () =>
|
||||
router.getComponentForRouteName(route.routeName);
|
||||
|
||||
const childNavigation = addNavigationHelpers({
|
||||
dispatch,
|
||||
state: route,
|
||||
addListener: getChildEventSubscriber(addListener, route.key),
|
||||
});
|
||||
const options = router.getScreenOptions(
|
||||
childNavigation,
|
||||
this.props.screenProps
|
||||
);
|
||||
tabDescriptors[route.key] = {
|
||||
key: route.key,
|
||||
getComponent,
|
||||
options,
|
||||
state: route,
|
||||
navigation: childNavigation,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<DrawerSidebar
|
||||
screenProps={this.props.screenProps}
|
||||
navigation={details.navigation}
|
||||
descriptors={tabDescriptors}
|
||||
contentComponent={this.props.navigationConfig.contentComponent}
|
||||
contentOptions={this.props.navigationConfig.contentOptions}
|
||||
drawerPosition={this.props.navigationConfig.drawerPosition}
|
||||
style={this.props.navigationConfig.style}
|
||||
{...this.props.navigationConfig}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const DrawerScreen = this.props.router.getComponentForRouteName(
|
||||
this.props.drawerCloseRoute
|
||||
const descriptor = Object.values(this.props.descriptors).find(
|
||||
d => d.state.routeName === this.props.navigationConfig.drawerCloseRoute
|
||||
);
|
||||
|
||||
const config = this.props.router.getScreenOptions(
|
||||
this._screenNavigationProp,
|
||||
this.props.screenProps
|
||||
);
|
||||
const DrawerScreen = descriptor.getComponent();
|
||||
|
||||
const { drawerLockMode } = descriptor.options;
|
||||
|
||||
return (
|
||||
<DrawerLayout
|
||||
@@ -137,23 +149,25 @@ export default class DrawerView extends React.PureComponent {
|
||||
}}
|
||||
drawerLockMode={
|
||||
(this.props.screenProps && this.props.screenProps.drawerLockMode) ||
|
||||
(config && config.drawerLockMode)
|
||||
this.props.navigationConfig.drawerLockMode
|
||||
}
|
||||
drawerBackgroundColor={
|
||||
this.props.navigationConfig.drawerBackgroundColor
|
||||
}
|
||||
drawerBackgroundColor={this.props.drawerBackgroundColor}
|
||||
drawerWidth={this.state.drawerWidth}
|
||||
onDrawerOpen={this._handleDrawerOpen}
|
||||
onDrawerClose={this._handleDrawerClose}
|
||||
useNativeAnimations={this.props.useNativeAnimations}
|
||||
useNativeAnimations={this.props.navigationConfig.useNativeAnimations}
|
||||
renderNavigationView={this._renderNavigationView}
|
||||
drawerPosition={
|
||||
this.props.drawerPosition === 'right'
|
||||
this.props.navigationConfig.drawerPosition === 'right'
|
||||
? DrawerLayout.positions.Right
|
||||
: DrawerLayout.positions.Left
|
||||
}
|
||||
>
|
||||
<DrawerScreen
|
||||
screenProps={this.props.screenProps}
|
||||
navigation={this._screenNavigationProp}
|
||||
navigation={descriptor.navigation}
|
||||
/>
|
||||
</DrawerLayout>
|
||||
);
|
||||
|
||||
@@ -47,11 +47,11 @@ class Header extends React.PureComponent {
|
||||
};
|
||||
|
||||
_getHeaderTitleString(scene) {
|
||||
const sceneOptions = this.props.getScreenDetails(scene).options;
|
||||
if (typeof sceneOptions.headerTitle === 'string') {
|
||||
return sceneOptions.headerTitle;
|
||||
const options = scene.descriptor.options;
|
||||
if (typeof options.headerTitle === 'string') {
|
||||
return options.headerTitle;
|
||||
}
|
||||
return sceneOptions.title;
|
||||
return options.title;
|
||||
}
|
||||
|
||||
_getLastScene(scene) {
|
||||
@@ -63,7 +63,7 @@ class Header extends React.PureComponent {
|
||||
if (!lastScene) {
|
||||
return null;
|
||||
}
|
||||
const { headerBackTitle } = this.props.getScreenDetails(lastScene).options;
|
||||
const { headerBackTitle } = lastScene.descriptor.options;
|
||||
if (headerBackTitle || headerBackTitle === null) {
|
||||
return headerBackTitle;
|
||||
}
|
||||
@@ -75,27 +75,20 @@ class Header extends React.PureComponent {
|
||||
if (!lastScene) {
|
||||
return null;
|
||||
}
|
||||
return this.props.getScreenDetails(lastScene).options
|
||||
.headerTruncatedBackTitle;
|
||||
return lastScene.descriptor.options.headerTruncatedBackTitle;
|
||||
}
|
||||
|
||||
_navigateBack = () => {
|
||||
requestAnimationFrame(() => {
|
||||
this.props.navigation.goBack(this.props.scene.route.key);
|
||||
});
|
||||
};
|
||||
|
||||
_renderTitleComponent = props => {
|
||||
const details = this.props.getScreenDetails(props.scene);
|
||||
const headerTitle = details.options.headerTitle;
|
||||
const { options } = props.scene.descriptor;
|
||||
const headerTitle = options.headerTitle;
|
||||
if (React.isValidElement(headerTitle)) {
|
||||
return headerTitle;
|
||||
}
|
||||
const titleString = this._getHeaderTitleString(props.scene);
|
||||
|
||||
const titleStyle = details.options.headerTitleStyle;
|
||||
const color = details.options.headerTintColor;
|
||||
const allowFontScaling = details.options.headerTitleAllowFontScaling;
|
||||
const titleStyle = options.headerTitleStyle;
|
||||
const color = options.headerTintColor;
|
||||
const allowFontScaling = options.headerTitleAllowFontScaling;
|
||||
|
||||
// On iOS, width of left/right components depends on the calculated
|
||||
// size of the title.
|
||||
@@ -127,8 +120,7 @@ class Header extends React.PureComponent {
|
||||
};
|
||||
|
||||
_renderLeftComponent = props => {
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
|
||||
const { options } = props.scene.descriptor;
|
||||
if (
|
||||
React.isValidElement(options.headerLeft) ||
|
||||
options.headerLeft === null
|
||||
@@ -148,9 +140,15 @@ class Header extends React.PureComponent {
|
||||
? (this.props.layout.initWidth - this.state.widths[props.scene.key]) / 2
|
||||
: undefined;
|
||||
const RenderedLeftComponent = options.headerLeft || HeaderBackButton;
|
||||
const goBack = () => {
|
||||
// Go back on next tick because button ripple effect needs to happen on Android
|
||||
requestAnimationFrame(() => {
|
||||
this.props.navigation.goBack(props.scene.descriptor.key);
|
||||
});
|
||||
};
|
||||
return (
|
||||
<RenderedLeftComponent
|
||||
onPress={this._navigateBack}
|
||||
onPress={goBack}
|
||||
pressColorAndroid={options.headerPressColorAndroid}
|
||||
tintColor={options.headerTintColor}
|
||||
buttonImage={options.headerBackImage}
|
||||
@@ -167,7 +165,7 @@ class Header extends React.PureComponent {
|
||||
ButtonContainerComponent,
|
||||
LabelContainerComponent
|
||||
) => {
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
const { options } = props.scene.descriptor;
|
||||
const backButtonTitle = this._getBackButtonTitleString(props.scene);
|
||||
const truncatedBackButtonTitle = this._getTruncatedBackButtonTitle(
|
||||
props.scene
|
||||
@@ -193,13 +191,12 @@ class Header extends React.PureComponent {
|
||||
};
|
||||
|
||||
_renderRightComponent = props => {
|
||||
const details = this.props.getScreenDetails(props.scene);
|
||||
const { headerRight } = details.options;
|
||||
const { headerRight } = props.scene.descriptor.options;
|
||||
return headerRight || null;
|
||||
};
|
||||
|
||||
_renderLeft(props) {
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
const { options } = props.scene.descriptor;
|
||||
|
||||
const { transitionPreset } = this.props;
|
||||
|
||||
@@ -374,7 +371,7 @@ class Header extends React.PureComponent {
|
||||
});
|
||||
|
||||
const { isLandscape, transitionPreset } = this.props;
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
const { options } = props.scene.descriptor;
|
||||
|
||||
const wrapperProps = {
|
||||
style: styles.header,
|
||||
@@ -439,7 +436,7 @@ class Header extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
const { options } = this.props.getScreenDetails(scene);
|
||||
const { options } = scene.descriptor;
|
||||
const { headerStyle = {} } = options;
|
||||
const headerStyleObj = StyleSheet.flatten(headerStyle);
|
||||
const appBarHeight = getAppBarHeight(isLandscape);
|
||||
|
||||
@@ -59,7 +59,12 @@ function areRoutesShallowEqual(one, two) {
|
||||
return shallowEqual(one, two);
|
||||
}
|
||||
|
||||
export default function ScenesReducer(scenes, nextState, prevState) {
|
||||
export default function ScenesReducer(
|
||||
scenes,
|
||||
nextState,
|
||||
prevState,
|
||||
descriptors
|
||||
) {
|
||||
if (prevState === nextState) {
|
||||
return scenes;
|
||||
}
|
||||
@@ -80,12 +85,16 @@ export default function ScenesReducer(scenes, nextState, prevState) {
|
||||
const nextKeys = new Set();
|
||||
nextState.routes.forEach((route, index) => {
|
||||
const key = SCENE_KEY_PREFIX + route.key;
|
||||
|
||||
let descriptor = descriptors && descriptors[route.key];
|
||||
|
||||
const scene = {
|
||||
index,
|
||||
isActive: false,
|
||||
isStale: false,
|
||||
key,
|
||||
route,
|
||||
descriptor,
|
||||
};
|
||||
invariant(
|
||||
!nextKeys.has(key),
|
||||
@@ -109,12 +118,16 @@ export default function ScenesReducer(scenes, nextState, prevState) {
|
||||
if (freshScenes.has(key)) {
|
||||
return;
|
||||
}
|
||||
const lastScene = scenes.find(scene => scene.route.key === route.key);
|
||||
const descriptor = lastScene && lastScene.descriptor;
|
||||
|
||||
staleScenes.set(key, {
|
||||
index,
|
||||
isActive: false,
|
||||
isStale: true,
|
||||
key,
|
||||
route,
|
||||
descriptor,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
66
src/views/StackView/StackView.js
Normal file
66
src/views/StackView/StackView.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import StackViewLayout from './StackViewLayout';
|
||||
import Transitioner from '../Transitioner';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import TransitionConfigs from './StackViewTransitionConfigs';
|
||||
|
||||
const NativeAnimatedModule =
|
||||
NativeModules && NativeModules.NativeAnimatedModule;
|
||||
|
||||
class StackView extends React.Component {
|
||||
static defaultProps = {
|
||||
navigationConfig: {
|
||||
mode: 'card',
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Transitioner
|
||||
render={this._render}
|
||||
configureTransition={this._configureTransition}
|
||||
navigation={this.props.navigation}
|
||||
descriptors={this.props.descriptors}
|
||||
onTransitionStart={this.props.onTransitionStart}
|
||||
onTransitionEnd={(lastTransition, transition) => {
|
||||
const { onTransitionEnd, navigation } = this.props;
|
||||
navigation.dispatch(
|
||||
NavigationActions.completeTransition({
|
||||
key: navigation.state.key,
|
||||
})
|
||||
);
|
||||
onTransitionEnd && onTransitionEnd(lastTransition, transition);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_configureTransition = (transitionProps, prevTransitionProps) => {
|
||||
return {
|
||||
...TransitionConfigs.getTransitionConfig(
|
||||
this.props.navigationConfig.transitionConfig,
|
||||
transitionProps,
|
||||
prevTransitionProps,
|
||||
this.props.navigationConfig.mode === 'modal'
|
||||
).transitionSpec,
|
||||
useNativeDriver: !!NativeAnimatedModule,
|
||||
};
|
||||
};
|
||||
|
||||
_render = (transitionProps, lastTransitionProps) => {
|
||||
const { screenProps, navigationConfig } = this.props;
|
||||
return (
|
||||
<StackViewLayout
|
||||
{...navigationConfig}
|
||||
screenProps={screenProps}
|
||||
descriptors={this.props.descriptors}
|
||||
transitionProps={transitionProps}
|
||||
lastTransitionProps={lastTransitionProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default StackView;
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Animated, StyleSheet } from 'react-native';
|
||||
import createPointerEventsContainer from './PointerEventsContainer';
|
||||
import createPointerEventsContainer from './createPointerEventsContainer';
|
||||
|
||||
/**
|
||||
* Component that renders the scene as card for the <NavigationCardStack />.
|
||||
* Component that renders the scene as card for the <StackView />.
|
||||
*/
|
||||
class Card extends React.Component {
|
||||
render() {
|
||||
@@ -22,16 +22,12 @@ class Card extends React.Component {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
main: {
|
||||
backgroundColor: '#EFEFF4',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: '#E9E9EF',
|
||||
shadowColor: 'black',
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 5,
|
||||
top: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import clamp from 'clamp';
|
||||
import {
|
||||
@@ -11,14 +11,13 @@ import {
|
||||
Easing,
|
||||
} from 'react-native';
|
||||
|
||||
import Card from './Card';
|
||||
import Card from './StackViewCard';
|
||||
import Header from '../Header/Header';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import addNavigationHelpers from '../../addNavigationHelpers';
|
||||
import getChildEventSubscriber from '../../getChildEventSubscriber';
|
||||
import SceneView from '../SceneView';
|
||||
|
||||
import TransitionConfigs from './TransitionConfigs';
|
||||
import TransitionConfigs from './StackViewTransitionConfigs';
|
||||
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
||||
|
||||
const emptyFunction = () => {};
|
||||
@@ -59,7 +58,7 @@ const animatedSubscribeValue = animatedValue => {
|
||||
}
|
||||
};
|
||||
|
||||
class CardStack extends React.Component {
|
||||
class StackViewLayout extends React.Component {
|
||||
/**
|
||||
* Used to identify the starting point of the position when the gesture starts, such that it can
|
||||
* be updated according to its relative position. This means that a card can effectively be
|
||||
@@ -80,52 +79,15 @@ class CardStack extends React.Component {
|
||||
*/
|
||||
_immediateIndex = null;
|
||||
|
||||
_screenDetails = {};
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
if (props.screenProps !== this.props.screenProps) {
|
||||
this._screenDetails = {};
|
||||
}
|
||||
props.transitionProps.scenes.forEach(newScene => {
|
||||
if (
|
||||
this._screenDetails[newScene.key] &&
|
||||
this._screenDetails[newScene.key].state !== newScene.route
|
||||
) {
|
||||
this._screenDetails[newScene.key] = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_getScreenDetails = scene => {
|
||||
const { screenProps, transitionProps: { navigation }, router } = this.props;
|
||||
let screenDetails = this._screenDetails[scene.key];
|
||||
if (!screenDetails || screenDetails.state !== scene.route) {
|
||||
const screenNavigation = addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: scene.route,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
scene.route.key
|
||||
),
|
||||
});
|
||||
screenDetails = {
|
||||
state: scene.route,
|
||||
navigation: screenNavigation,
|
||||
options: router.getScreenOptions(screenNavigation, screenProps),
|
||||
};
|
||||
this._screenDetails[scene.key] = screenDetails;
|
||||
}
|
||||
return screenDetails;
|
||||
};
|
||||
|
||||
_renderHeader(scene, headerMode) {
|
||||
const { header } = this._getScreenDetails(scene).options;
|
||||
const { options } = scene.descriptor;
|
||||
const { header } = options;
|
||||
|
||||
if (typeof header !== 'undefined' && typeof header !== 'function') {
|
||||
return header;
|
||||
}
|
||||
|
||||
const renderHeader = header || (props => <Header {...props} />);
|
||||
const renderHeader = header || ((props: *) => <Header {...props} />);
|
||||
const {
|
||||
headerLeftInterpolator,
|
||||
headerTitleInterpolator,
|
||||
@@ -145,7 +107,6 @@ class CardStack extends React.Component {
|
||||
scene,
|
||||
mode: headerMode,
|
||||
transitionPreset: this._getHeaderTransitionPreset(),
|
||||
getScreenDetails: this._getScreenDetails,
|
||||
leftInterpolator: headerLeftInterpolator,
|
||||
titleInterpolator: headerTitleInterpolator,
|
||||
rightInterpolator: headerRightInterpolator,
|
||||
@@ -245,7 +206,8 @@ class CardStack extends React.Component {
|
||||
} = this.props;
|
||||
const { index } = navigation.state;
|
||||
const isVertical = mode === 'modal';
|
||||
const { options } = this._getScreenDetails(scene);
|
||||
const { options } = scene.descriptor;
|
||||
|
||||
const gestureDirectionInverted = options.gestureDirection === 'inverted';
|
||||
|
||||
const gesturesEnabled =
|
||||
@@ -261,7 +223,7 @@ class CardStack extends React.Component {
|
||||
this._reset(index, 0);
|
||||
},
|
||||
onPanResponderGrant: () => {
|
||||
position.stopAnimation(value => {
|
||||
position.stopAnimation((value: number) => {
|
||||
this._isResponding = true;
|
||||
this._gestureStartValue = value;
|
||||
});
|
||||
@@ -285,9 +247,12 @@ class CardStack extends React.Component {
|
||||
? axisLength - (currentDragPosition - currentDragDistance)
|
||||
: currentDragPosition - currentDragDistance;
|
||||
// Compare to the gesture distance relavant to card or modal
|
||||
|
||||
const { options } = scene.descriptor;
|
||||
|
||||
const {
|
||||
gestureResponseDistance: userGestureResponseDistance = {},
|
||||
} = this._getScreenDetails(scene).options;
|
||||
} = options;
|
||||
const gestureResponseDistance = isVertical
|
||||
? userGestureResponseDistance.vertical ||
|
||||
GESTURE_RESPONSE_DISTANCE_VERTICAL
|
||||
@@ -388,7 +353,7 @@ class CardStack extends React.Component {
|
||||
return (
|
||||
<View {...handlers} style={containerStyle}>
|
||||
<View style={styles.scenes}>
|
||||
{scenes.map(s => this._renderCard(s))}
|
||||
{scenes.map((s: *) => this._renderCard(s))}
|
||||
</View>
|
||||
{floatingHeader}
|
||||
</View>
|
||||
@@ -420,8 +385,10 @@ class CardStack extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
_renderInnerScene(SceneComponent, scene) {
|
||||
const { navigation } = this._getScreenDetails(scene);
|
||||
_renderInnerScene(scene) {
|
||||
const { options, navigation, getComponent } = scene.descriptor;
|
||||
const SceneComponent = getComponent();
|
||||
|
||||
const { screenProps } = this.props;
|
||||
const headerMode = this._getHeaderMode();
|
||||
if (headerMode === 'screen') {
|
||||
@@ -440,7 +407,7 @@ class CardStack extends React.Component {
|
||||
}
|
||||
return (
|
||||
<SceneView
|
||||
screenProps={this.props.screenProps}
|
||||
screenProps={screenProps}
|
||||
navigation={navigation}
|
||||
component={SceneComponent}
|
||||
/>
|
||||
@@ -464,21 +431,14 @@ class CardStack extends React.Component {
|
||||
screenInterpolator &&
|
||||
screenInterpolator({ ...this.props.transitionProps, scene });
|
||||
|
||||
const SceneComponent = this.props.router.getComponentForRouteName(
|
||||
scene.route.routeName
|
||||
);
|
||||
|
||||
const { transitionProps, ...props } = this.props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
{...props}
|
||||
{...transitionProps}
|
||||
{...this.props.transitionProps}
|
||||
key={`card_${scene.key}`}
|
||||
style={[style, this.props.cardStyle]}
|
||||
scene={scene}
|
||||
>
|
||||
{this._renderInnerScene(SceneComponent, scene)}
|
||||
{this._renderInnerScene(scene)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -498,4 +458,4 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default CardStack;
|
||||
export default StackViewLayout;
|
||||
@@ -159,17 +159,9 @@ function forFade(props) {
|
||||
};
|
||||
}
|
||||
|
||||
function canUseNativeDriver() {
|
||||
// The native driver can be enabled for this interpolator animating
|
||||
// opacity, translateX, and translateY is supported by the native animation
|
||||
// driver on iOS and Android.
|
||||
return true;
|
||||
}
|
||||
|
||||
export default {
|
||||
forHorizontal,
|
||||
forVertical,
|
||||
forFadeFromBottomAndroid,
|
||||
forFade,
|
||||
canUseNativeDriver,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Animated, Easing, Platform } from 'react-native';
|
||||
import CardStackStyleInterpolator from './CardStackStyleInterpolator';
|
||||
import StyleInterpolator from './StackViewStyleInterpolator';
|
||||
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
||||
|
||||
let IOSTransitionSpec;
|
||||
@@ -23,7 +23,7 @@ if (ReactNativeFeatures.supportsImprovedSpringAnimation()) {
|
||||
// Standard iOS navigation transition
|
||||
const SlideFromRightIOS = {
|
||||
transitionSpec: IOSTransitionSpec,
|
||||
screenInterpolator: CardStackStyleInterpolator.forHorizontal,
|
||||
screenInterpolator: StyleInterpolator.forHorizontal,
|
||||
containerStyle: {
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
@@ -32,7 +32,7 @@ const SlideFromRightIOS = {
|
||||
// Standard iOS navigation transition for modals
|
||||
const ModalSlideFromBottomIOS = {
|
||||
transitionSpec: IOSTransitionSpec,
|
||||
screenInterpolator: CardStackStyleInterpolator.forVertical,
|
||||
screenInterpolator: StyleInterpolator.forVertical,
|
||||
containerStyle: {
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
@@ -46,7 +46,7 @@ const FadeInFromBottomAndroid = {
|
||||
easing: Easing.out(Easing.poly(5)), // decelerate
|
||||
timing: Animated.timing,
|
||||
},
|
||||
screenInterpolator: CardStackStyleInterpolator.forFadeFromBottomAndroid,
|
||||
screenInterpolator: StyleInterpolator.forFadeFromBottomAndroid,
|
||||
};
|
||||
|
||||
// Standard Android navigation transition when closing an Activity
|
||||
@@ -57,15 +57,12 @@ const FadeOutToBottomAndroid = {
|
||||
easing: Easing.in(Easing.poly(4)), // accelerate
|
||||
timing: Animated.timing,
|
||||
},
|
||||
screenInterpolator: CardStackStyleInterpolator.forFadeFromBottomAndroid,
|
||||
screenInterpolator: StyleInterpolator.forFadeFromBottomAndroid,
|
||||
};
|
||||
|
||||
function defaultTransitionConfig(
|
||||
// props for the new screen
|
||||
transitionProps,
|
||||
// props for the old screen
|
||||
prevTransitionProps,
|
||||
// whether we're animating in/out a modal screen
|
||||
isModal
|
||||
) {
|
||||
if (Platform.OS === 'android') {
|
||||
@@ -89,9 +86,7 @@ function defaultTransitionConfig(
|
||||
|
||||
function getTransitionConfig(
|
||||
transitionConfigurer,
|
||||
// props for the new screen
|
||||
transitionProps,
|
||||
// props for the old screen
|
||||
prevTransitionProps,
|
||||
isModal
|
||||
) {
|
||||
@@ -9,7 +9,7 @@ const MIN_POSITION_OFFSET = 0.01;
|
||||
* `pointerEvents` property for a component whenever navigation position
|
||||
* changes.
|
||||
*/
|
||||
export default function create(Component) {
|
||||
export default function createPointerEventsContainer(Component) {
|
||||
class Container extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
@@ -4,7 +4,6 @@ import { TabViewAnimated, TabViewPagerPan } from 'react-native-tab-view';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import ResourceSavingSceneView from '../ResourceSavingSceneView';
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
|
||||
class TabView extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
@@ -22,31 +21,33 @@ class TabView extends React.PureComponent {
|
||||
};
|
||||
|
||||
_renderScene = ({ route }) => {
|
||||
const { screenProps } = this.props;
|
||||
const childNavigation = this.props.childNavigationProps[route.key];
|
||||
const TabComponent = this.props.router.getComponentForRouteName(
|
||||
route.routeName
|
||||
);
|
||||
|
||||
const { screenProps, descriptors } = this.props;
|
||||
const {
|
||||
lazy,
|
||||
removeClippedSubviews,
|
||||
animationEnabled,
|
||||
swipeEnabled,
|
||||
} = this.props.navigationConfig;
|
||||
const descriptor = descriptors[route.key];
|
||||
const TabComponent = descriptor.getComponent();
|
||||
return (
|
||||
<ResourceSavingSceneView
|
||||
lazy={this.props.lazy}
|
||||
removeClippedSubViews={this.props.removeClippedSubviews}
|
||||
animationEnabled={this.props.animationEnabled}
|
||||
swipeEnabled={this.props.swipeEnabled}
|
||||
lazy={lazy}
|
||||
removeClippedSubViews={removeClippedSubviews}
|
||||
animationEnabled={animationEnabled}
|
||||
swipeEnabled={swipeEnabled}
|
||||
screenProps={screenProps}
|
||||
component={TabComponent}
|
||||
navigation={this.props.navigation}
|
||||
childNavigation={childNavigation}
|
||||
childNavigation={descriptor.navigation}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
_getLabel = ({ route, tintColor, focused }) => {
|
||||
const options = this.props.router.getScreenOptions(
|
||||
this.props.childNavigationProps[route.key],
|
||||
this.props.screenProps || {}
|
||||
);
|
||||
const { screenProps, descriptors } = this.props;
|
||||
const descriptor = descriptors[route.key];
|
||||
const options = descriptor.options;
|
||||
|
||||
if (options.tabBarLabel) {
|
||||
return typeof options.tabBarLabel === 'function'
|
||||
@@ -62,19 +63,17 @@ class TabView extends React.PureComponent {
|
||||
};
|
||||
|
||||
_getOnPress = (previousScene, { route }) => {
|
||||
const options = this.props.router.getScreenOptions(
|
||||
this.props.childNavigationProps[route.key],
|
||||
this.props.screenProps || {}
|
||||
);
|
||||
const { descriptors } = this.props;
|
||||
const descriptor = descriptors[route.key];
|
||||
const options = descriptor.options;
|
||||
|
||||
return options.tabBarOnPress;
|
||||
};
|
||||
|
||||
_getTestIDProps = ({ route, focused }) => {
|
||||
const options = this.props.router.getScreenOptions(
|
||||
this.props.childNavigationProps[route.key],
|
||||
this.props.screenProps || {}
|
||||
);
|
||||
_getTestIDProps = ({ route }) => {
|
||||
const { descriptors } = this.props;
|
||||
const descriptor = descriptors[route.key];
|
||||
const options = descriptor.options;
|
||||
|
||||
return typeof options.tabBarTestIDProps === 'function'
|
||||
? options.tabBarTestIDProps({ focused })
|
||||
@@ -82,10 +81,10 @@ class TabView extends React.PureComponent {
|
||||
};
|
||||
|
||||
_renderIcon = ({ focused, route, tintColor }) => {
|
||||
const options = this.props.router.getScreenOptions(
|
||||
this.props.childNavigationProps[route.key],
|
||||
this.props.screenProps || {}
|
||||
);
|
||||
const { descriptors } = this.props;
|
||||
const descriptor = descriptors[route.key];
|
||||
const options = descriptor.options;
|
||||
|
||||
if (options.tabBarIcon) {
|
||||
return typeof options.tabBarIcon === 'function'
|
||||
? options.tabBarIcon({ tintColor, focused })
|
||||
@@ -99,7 +98,8 @@ class TabView extends React.PureComponent {
|
||||
tabBarOptions,
|
||||
tabBarComponent: TabBarComponent,
|
||||
animationEnabled,
|
||||
} = this.props;
|
||||
tabBarPosition,
|
||||
} = this.props.navigationConfig;
|
||||
if (typeof TabBarComponent === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class TabView extends React.PureComponent {
|
||||
<TabBarComponent
|
||||
{...props}
|
||||
{...tabBarOptions}
|
||||
tabBarPosition={this.props.tabBarPosition}
|
||||
tabBarPosition={tabBarPosition}
|
||||
screenProps={this.props.screenProps}
|
||||
navigation={this.props.navigation}
|
||||
getLabel={this._getLabel}
|
||||
@@ -124,31 +124,29 @@ class TabView extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
router,
|
||||
tabBarComponent,
|
||||
tabBarPosition,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
initialLayout,
|
||||
screenProps,
|
||||
} = this.props;
|
||||
} = this.props.navigationConfig;
|
||||
|
||||
let renderHeader;
|
||||
let renderFooter;
|
||||
let renderPager;
|
||||
|
||||
const { state } = this.props.navigation;
|
||||
const options = router.getScreenOptions(
|
||||
this.props.childNavigationProps[state.routes[state.index].key],
|
||||
screenProps || {}
|
||||
);
|
||||
const route = state.routes[state.index];
|
||||
const { descriptors } = this.props;
|
||||
const descriptor = descriptors[route.key];
|
||||
const options = descriptor.options;
|
||||
|
||||
const tabBarVisible =
|
||||
options.tabBarVisible == null ? true : options.tabBarVisible;
|
||||
|
||||
let swipeEnabled =
|
||||
options.swipeEnabled == null
|
||||
? this.props.swipeEnabled
|
||||
? this.props.navigationConfig.swipeEnabled
|
||||
: options.swipeEnabled;
|
||||
|
||||
if (typeof swipeEnabled === 'function') {
|
||||
@@ -181,7 +179,6 @@ class TabView extends React.PureComponent {
|
||||
renderScene: this._renderScene,
|
||||
onIndexChange: this._handlePageChanged,
|
||||
navigationState: this.props.navigation.state,
|
||||
screenProps: this.props.screenProps,
|
||||
style: styles.container,
|
||||
};
|
||||
|
||||
@@ -189,7 +186,7 @@ class TabView extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export default withCachedChildNavigation(TabView);
|
||||
export default TabView;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
||||
@@ -29,7 +29,12 @@ class Transitioner extends React.Component {
|
||||
layout,
|
||||
position: new Animated.Value(this.props.navigation.state.index),
|
||||
progress: new Animated.Value(1),
|
||||
scenes: NavigationScenesReducer([], this.props.navigation.state),
|
||||
scenes: NavigationScenesReducer(
|
||||
[],
|
||||
this.props.navigation.state,
|
||||
null,
|
||||
this.props.descriptors
|
||||
),
|
||||
};
|
||||
|
||||
this._prevTransitionProps = null;
|
||||
@@ -56,7 +61,8 @@ class Transitioner extends React.Component {
|
||||
const nextScenes = NavigationScenesReducer(
|
||||
this.state.scenes,
|
||||
nextProps.navigation.state,
|
||||
this.props.navigation.state
|
||||
this.props.navigation.state,
|
||||
nextProps.descriptors
|
||||
);
|
||||
|
||||
if (nextScenes === this.state.scenes) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import renderer from 'react-test-renderer';
|
||||
import TabRouter from '../../routers/TabRouter';
|
||||
|
||||
import TabView from '../TabView/TabView';
|
||||
import TabBarBottom from '../TabView/TabBarBottom';
|
||||
@@ -12,21 +11,30 @@ const dummyEventSubscriber = (name, handler) => ({
|
||||
|
||||
describe('TabBarBottom', () => {
|
||||
it('renders successfully', () => {
|
||||
const route = { key: 's1', routeName: 's1' };
|
||||
const navigation = {
|
||||
state: {
|
||||
index: 0,
|
||||
routes: [{ key: 's1', routeName: 's1' }],
|
||||
routes: [route],
|
||||
},
|
||||
addListener: dummyEventSubscriber,
|
||||
};
|
||||
const router = TabRouter({ s1: { screen: View } });
|
||||
|
||||
const rendered = renderer
|
||||
.create(
|
||||
<TabView
|
||||
tabBarComponent={TabBarBottom}
|
||||
navigation={navigation}
|
||||
router={router}
|
||||
navigationConfig={{}}
|
||||
descriptors={{
|
||||
s1: {
|
||||
state: route,
|
||||
key: route.key,
|
||||
options: {},
|
||||
navigation: { state: route },
|
||||
getComponent: () => View,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
@@ -20,127 +20,6 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
style={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderTopColor": "rgba(0, 0, 0, .3)",
|
||||
"borderTopWidth": 0.5,
|
||||
"flexDirection": "row",
|
||||
"height": 49,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityComponentType={undefined}
|
||||
accessibilityLabel={undefined}
|
||||
accessibilityTraits={undefined}
|
||||
accessible={true}
|
||||
collapsable={undefined}
|
||||
hitSlop={undefined}
|
||||
nativeID={undefined}
|
||||
onLayout={undefined}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(0, 0, 0, 0)",
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
},
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "flex-end",
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"bottom": 0,
|
||||
"justifyContent": "center",
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"bottom": 0,
|
||||
"justifyContent": "center",
|
||||
"left": 0,
|
||||
"opacity": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"color": "rgba(52, 120, 246, 1)",
|
||||
"fontSize": 10,
|
||||
"marginBottom": 1.5,
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
s1
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<RCTScrollView
|
||||
DEPRECATED_sendUpdatedChildFrames={false}
|
||||
alwaysBounceHorizontal={false}
|
||||
@@ -244,16 +123,6 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
<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",
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import React from 'react';
|
||||
import addNavigationHelpers from './addNavigationHelpers';
|
||||
import getChildEventSubscriber from './getChildEventSubscriber';
|
||||
|
||||
/**
|
||||
* HOC which caches the child navigation items.
|
||||
*/
|
||||
export default function withCachedChildNavigation(Comp) {
|
||||
const displayName = Comp.displayName || Comp.name;
|
||||
return class extends React.PureComponent {
|
||||
static displayName = `withCachedChildNavigation(${displayName})`;
|
||||
|
||||
componentWillMount() {
|
||||
this._updateNavigationProps(this.props.navigation);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this._updateNavigationProps(nextProps.navigation);
|
||||
}
|
||||
|
||||
_updateNavigationProps = navigation => {
|
||||
// Update props for each child route
|
||||
if (!this._childNavigationProps) {
|
||||
this._childNavigationProps = {};
|
||||
}
|
||||
navigation.state.routes.forEach(route => {
|
||||
const childNavigation = this._childNavigationProps[route.key];
|
||||
if (childNavigation && childNavigation.state === route) {
|
||||
return;
|
||||
}
|
||||
this._childNavigationProps[route.key] = addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: route,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
route.key
|
||||
),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Comp
|
||||
{...this.props}
|
||||
childNavigationProps={this._childNavigationProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user