mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-26 09:08:38 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a37473c5e4 | ||
|
|
23eb0839cb | ||
|
|
e84473db78 | ||
|
|
c8531d0f38 | ||
|
|
276fb8f7b3 | ||
|
|
ab758bcaaa | ||
|
|
2bb91a6740 | ||
|
|
ffa3a92534 | ||
|
|
6dda8c30a9 | ||
|
|
065fdf61d8 |
@@ -15,9 +15,26 @@ jobs:
|
||||
- v3-react-navigation-master
|
||||
- run: yarn # install root deps
|
||||
- run: ./scripts/test.sh # run tests
|
||||
- setup_remote_docker
|
||||
- deploy:
|
||||
command: |
|
||||
set -x
|
||||
VER="17.03.0-ce"
|
||||
curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz
|
||||
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
|
||||
mv /tmp/docker/* /usr/bin
|
||||
|
||||
yarn global add exp
|
||||
set +x
|
||||
exp login -u "$EXPO_USERNAME" -p "$EXPO_PASSWORD"
|
||||
set -x
|
||||
cd examples/NavigationPlayground && yarn && exp publish --release-channel "${CIRCLE_SHA1}"
|
||||
- save_cache:
|
||||
key: v3-react-navigation-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- ~/react-navigation/examples/NavigationPlayground/node_modules
|
||||
- ~/react-navigation/examples/ReduxExample/node_modules
|
||||
notify:
|
||||
webhooks:
|
||||
- url: https://react-navigation-ci.now.sh
|
||||
@@ -45,8 +45,8 @@ This library is a community effort: it can only be great if we all help out in o
|
||||
* Creating public example repositories or [Snacks](https://snack.expo.io/) of navigation problems you have solved and sharing the links in [Community Resources](https://github.com/react-navigation/react-navigation/blob/master/COMMUNITY_RESOURCES.md).
|
||||
* Answering questions on [Stack Overflow](https://stackoverflow.com/search?q=react-navigation).
|
||||
* Answering questions in our [Reactiflux](https://www.reactiflux.com/) channel.
|
||||
* Providing feedback on the open [PRs](https://github.com/react-community/react-navigation/pulls).
|
||||
* Providing feedback on the open [RFCs](https://github.com/react-community/rfcs/pulls).
|
||||
* Providing feedback on the open [PRs](https://github.com/react-navigation/react-navigation/pulls).
|
||||
* Providing feedback on the open [RFCs](https://github.com/react-navigation/rfcs).
|
||||
* Improving the [website](https://github.com/react-navigation/react-navigation.github.io).
|
||||
|
||||
If you would like to submit a pull request, please follow the [Contributors guide](https://reactnavigation.org/docs/contributing.html) to find out how. If you don't know where to start, check the ones with the label [`good first issue`](https://github.com/react-community/react-navigation/labels/good%20first%20issue) - even [fixing a typo in the documentation](https://github.com/react-community/react-navigation/pull/2727) is a worthy contribution!
|
||||
@@ -57,7 +57,7 @@ Certainly not! There other libraries - which, depending on your needs, can be be
|
||||
|
||||
#### Can I use this library for web?
|
||||
|
||||
Currently this is [not a priority](https://github.com/react-community/react-navigation/issues/2585#issuecomment-330338793), but the architecture of this library allows for it (and it has worked in the past). If you would like to lead this charge, please reach out with your ideas for how to move forward on the [RFCs repository](https://github.com/react-navigation/rfcs) and we would be happy to discuss.
|
||||
Web support was [not a priority for the 1.0 release](https://github.com/react-community/react-navigation/issues/2585#issuecomment-330338793), but the architecture of this library allows for it (and it has worked in the past). If you would like to lead this charge, please reach out with your ideas for how to move forward on the [RFCs repository](https://github.com/react-navigation/rfcs) and we would be happy to discuss.
|
||||
|
||||
## Code of conduct
|
||||
|
||||
|
||||
@@ -65,11 +65,13 @@ module.file_ext=.jsx
|
||||
module.file_ext=.json
|
||||
module.file_ext=.native.js
|
||||
|
||||
suppress_type=$FlowIgnore
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIgnore\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
@@ -29,6 +29,7 @@ import StacksOverTabs from './StacksOverTabs';
|
||||
import StacksWithKeys from './StacksWithKeys';
|
||||
import SimpleStack from './SimpleStack';
|
||||
import StackWithHeaderPreset from './StackWithHeaderPreset';
|
||||
import StackWithTranslucentHeader from './StackWithTranslucentHeader';
|
||||
import SimpleTabs from './SimpleTabs';
|
||||
import TabAnimations from './TabAnimations';
|
||||
import TabsWithNavigationFocus from './TabsWithNavigationFocus';
|
||||
@@ -50,6 +51,14 @@ const ExampleInfo = {
|
||||
name: 'UIKit-style Header Transitions',
|
||||
description: 'Masked back button and sliding header items. iOS only.',
|
||||
},
|
||||
StackWithHeaderPreset: {
|
||||
name: 'UIKit-style Header Transitions',
|
||||
description: 'Masked back button and sliding header items. iOS only.',
|
||||
},
|
||||
StackWithTranslucentHeader: {
|
||||
name: 'Translucent Header',
|
||||
description: 'Render arbitrary translucent content in header background.',
|
||||
},
|
||||
// MultipleDrawer: {
|
||||
// name: 'Multiple Drawer Example',
|
||||
// description: 'Add any drawer you need',
|
||||
@@ -114,6 +123,7 @@ const ExampleRoutes = {
|
||||
// screen: MultipleDrawer,
|
||||
// },
|
||||
StackWithHeaderPreset: StackWithHeaderPreset,
|
||||
StackWithTranslucentHeader: StackWithTranslucentHeader,
|
||||
TabsInDrawer: TabsInDrawer,
|
||||
CustomTabs: CustomTabs,
|
||||
CustomTransitioner: CustomTransitioner,
|
||||
|
||||
@@ -178,20 +178,18 @@ 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;
|
||||
|
||||
234
examples/NavigationPlayground/js/StackWithTranslucentHeader.js
Normal file
234
examples/NavigationPlayground/js/StackWithTranslucentHeader.js
Normal file
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {
|
||||
NavigationScreenProp,
|
||||
NavigationEventSubscription,
|
||||
} from 'react-navigation';
|
||||
|
||||
import { isIphoneX } from 'react-native-iphone-x-helper';
|
||||
|
||||
import * as React from 'react';
|
||||
import { BlurView, Constants } from 'expo';
|
||||
import {
|
||||
Button,
|
||||
Dimensions,
|
||||
Platform,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Header, StackNavigator } from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
type MyNavScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
banner: React.Node,
|
||||
};
|
||||
|
||||
class MyNavScreen extends React.Component<MyNavScreenProps> {
|
||||
render() {
|
||||
const { navigation, banner } = this.props;
|
||||
return (
|
||||
<ScrollView style={{ flex: 1 }} {...this.getHeaderInset()}>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.push('Profile', { name: 'Jane' })}
|
||||
title="Push a profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Photos', { name: 'Jane' })}
|
||||
title="Navigate to a photos screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.replace('Profile', { name: 'Lucy' })}
|
||||
title="Replace with profile"
|
||||
/>
|
||||
<Button onPress={() => navigation.popToTop()} title="Pop to top" />
|
||||
<Button onPress={() => navigation.pop()} title="Pop" />
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
// Inset to compensate for navigation bar being transparent.
|
||||
// And improved abstraction for this will be built in to react-navigation
|
||||
// at some point.
|
||||
|
||||
getHeaderInset() {
|
||||
const NOTCH_HEIGHT = isIphoneX() ? 25 : 0;
|
||||
|
||||
// $FlowIgnore: we will remove the HEIGHT static soon enough
|
||||
const BASE_HEADER_HEIGHT = Header.HEIGHT;
|
||||
|
||||
const HEADER_HEIGHT =
|
||||
Platform.OS === 'ios'
|
||||
? BASE_HEADER_HEIGHT + NOTCH_HEIGHT
|
||||
: BASE_HEADER_HEIGHT + Constants.statusBarHeight;
|
||||
|
||||
return Platform.select({
|
||||
ios: {
|
||||
contentInset: { top: HEADER_HEIGHT },
|
||||
contentOffset: { y: -HEADER_HEIGHT },
|
||||
},
|
||||
android: {
|
||||
contentContainerStyle: {
|
||||
paddingTop: HEADER_HEIGHT,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type MyHomeScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
};
|
||||
|
||||
class MyHomeScreen extends React.Component<MyHomeScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
_s0: NavigationEventSubscription;
|
||||
_s1: NavigationEventSubscription;
|
||||
_s2: NavigationEventSubscription;
|
||||
_s3: NavigationEventSubscription;
|
||||
|
||||
componentDidMount() {
|
||||
this._s0 = this.props.navigation.addListener('willFocus', this._onWF);
|
||||
this._s1 = this.props.navigation.addListener('didFocus', this._onDF);
|
||||
this._s2 = this.props.navigation.addListener('willBlur', this._onWB);
|
||||
this._s3 = this.props.navigation.addListener('didBlur', this._onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._s0.remove();
|
||||
this._s1.remove();
|
||||
this._s2.remove();
|
||||
this._s3.remove();
|
||||
}
|
||||
_onWF = a => {
|
||||
console.log('_willFocus HomeScreen', a);
|
||||
};
|
||||
_onDF = a => {
|
||||
console.log('_didFocus HomeScreen', a);
|
||||
};
|
||||
_onWB = a => {
|
||||
console.log('_willBlur HomeScreen', a);
|
||||
};
|
||||
_onDB = a => {
|
||||
console.log('_didBlur HomeScreen', a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return <MyNavScreen banner="Home Screen" navigation={navigation} />;
|
||||
}
|
||||
}
|
||||
|
||||
type MyPhotosScreenProps = {
|
||||
navigation: NavigationScreenProp<*>,
|
||||
};
|
||||
class MyPhotosScreen extends React.Component<MyPhotosScreenProps> {
|
||||
static navigationOptions = {
|
||||
title: 'Photos',
|
||||
};
|
||||
_s0: NavigationEventSubscription;
|
||||
_s1: NavigationEventSubscription;
|
||||
_s2: NavigationEventSubscription;
|
||||
_s3: NavigationEventSubscription;
|
||||
|
||||
componentDidMount() {
|
||||
this._s0 = this.props.navigation.addListener('willFocus', this._onWF);
|
||||
this._s1 = this.props.navigation.addListener('didFocus', this._onDF);
|
||||
this._s2 = this.props.navigation.addListener('willBlur', this._onWB);
|
||||
this._s3 = this.props.navigation.addListener('didBlur', this._onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._s0.remove();
|
||||
this._s1.remove();
|
||||
this._s2.remove();
|
||||
this._s3.remove();
|
||||
}
|
||||
_onWF = a => {
|
||||
console.log('_willFocus PhotosScreen', a);
|
||||
};
|
||||
_onDF = a => {
|
||||
console.log('_didFocus PhotosScreen', a);
|
||||
};
|
||||
_onWB = a => {
|
||||
console.log('_willBlur PhotosScreen', a);
|
||||
};
|
||||
_onDB = a => {
|
||||
console.log('_didBlur PhotosScreen', a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params.name}'s Photos`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MyProfileScreen = ({ navigation }) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params.mode === 'edit' ? 'Now Editing ' : ''}${
|
||||
navigation.state.params.name
|
||||
}'s Profile`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
|
||||
MyProfileScreen.navigationOptions = props => {
|
||||
const { navigation } = props;
|
||||
const { state, setParams } = navigation;
|
||||
const { params } = state;
|
||||
return {
|
||||
headerBackImage: params.headerBackImage,
|
||||
headerTitle: `${params.name}'s Profile!`,
|
||||
// Render a button on the right side of the header.
|
||||
// When pressed switches the screen to edit mode.
|
||||
headerRight: (
|
||||
<Button
|
||||
title={params.mode === 'edit' ? 'Done' : 'Edit'}
|
||||
onPress={() =>
|
||||
setParams({ mode: params.mode === 'edit' ? '' : 'edit' })
|
||||
}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const StackWithTranslucentHeader = StackNavigator(
|
||||
{
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Profile: {
|
||||
path: 'people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
Photos: {
|
||||
path: 'photos/:name',
|
||||
screen: MyPhotosScreen,
|
||||
},
|
||||
},
|
||||
{
|
||||
headerTransitionPreset: 'uikit',
|
||||
navigationOptions: {
|
||||
headerTransparent: true,
|
||||
headerBackground: Platform.select({
|
||||
ios: <BlurView style={{ flex: 1 }} intensity={98} />,
|
||||
android: (
|
||||
<View style={{ flex: 1, backgroundColor: 'rgba(255,255,255,0.7)' }} />
|
||||
),
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default StackWithTranslucentHeader;
|
||||
@@ -14,11 +14,12 @@
|
||||
"expo": "^25.0.0",
|
||||
"react": "16.2.0",
|
||||
"react-native": "^0.52.0",
|
||||
"react-native-iphone-x-helper": "^1.0.2",
|
||||
"react-navigation": "link:../.."
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-transform-remove-console": "^6.9.0",
|
||||
"babel-jest": "^21.0.0",
|
||||
"babel-plugin-transform-remove-console": "^6.9.0",
|
||||
"flow-bin": "^0.61.0",
|
||||
"jest": "^21.0.1",
|
||||
"jest-expo": "^25.1.0",
|
||||
|
||||
@@ -5173,13 +5173,17 @@ react-native-gesture-handler@1.0.0-alpha.39:
|
||||
invariant "^2.2.2"
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-native-iphone-x-helper@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.2.tgz#7dbca530930f7c1ce8633cc8fd13ba94102992e1"
|
||||
|
||||
react-native-maps@0.19.0:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-maps/-/react-native-maps-0.19.0.tgz#ce94fad1cf360e335cb4338a68c95f791e869074"
|
||||
|
||||
react-native-safe-area-view@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.6.0.tgz#ce01eb27905a77780219537e0f53fe9c783a8b3d"
|
||||
react-native-safe-area-view@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.7.0.tgz#38f5ab9368d6ef9e5d18ab64212938af3ec39421"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.3.1"
|
||||
|
||||
|
||||
61
flow/react-navigation.js
vendored
61
flow/react-navigation.js
vendored
@@ -71,25 +71,11 @@ declare module 'react-navigation' {
|
||||
key?: string,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationNavigateAction = {|
|
||||
type: 'Navigate',
|
||||
routeName: string,
|
||||
params?: NavigationParams,
|
||||
|
||||
// The action to run inside the sub-router
|
||||
action?: NavigationNavigateAction | DeprecatedNavigationNavigateAction,
|
||||
|};
|
||||
|
||||
declare export type NavigationBackAction = {|
|
||||
type: 'Navigation/BACK',
|
||||
key?: ?string,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationBackAction = {|
|
||||
type: 'Back',
|
||||
key?: ?string,
|
||||
|};
|
||||
|
||||
declare export type NavigationSetParamsAction = {|
|
||||
type: 'Navigation/SET_PARAMS',
|
||||
|
||||
@@ -100,26 +86,11 @@ declare module 'react-navigation' {
|
||||
params: NavigationParams,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationSetParamsAction = {|
|
||||
type: 'SetParams',
|
||||
|
||||
// The key of the route where the params should be set
|
||||
key: string,
|
||||
|
||||
// The new params to merge into the existing route params
|
||||
params: NavigationParams,
|
||||
|};
|
||||
|
||||
declare export type NavigationInitAction = {|
|
||||
type: 'Navigation/INIT',
|
||||
params?: NavigationParams,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationInitAction = {|
|
||||
type: 'Init',
|
||||
params?: NavigationParams,
|
||||
|};
|
||||
|
||||
declare export type NavigationResetAction = {|
|
||||
type: 'Navigation/RESET',
|
||||
index: number,
|
||||
@@ -127,25 +98,11 @@ declare module 'react-navigation' {
|
||||
actions: Array<NavigationNavigateAction>,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationResetAction = {|
|
||||
type: 'Reset',
|
||||
index: number,
|
||||
key?: ?string,
|
||||
actions: Array<
|
||||
NavigationNavigateAction | DeprecatedNavigationNavigateAction
|
||||
>,
|
||||
|};
|
||||
|
||||
declare export type NavigationUriAction = {|
|
||||
type: 'Navigation/URI',
|
||||
uri: string,
|
||||
|};
|
||||
|
||||
declare type DeprecatedNavigationUriAction = {|
|
||||
type: 'Uri',
|
||||
uri: string,
|
||||
|};
|
||||
|
||||
declare export type NavigationReplaceAction = {|
|
||||
+type: 'Navigation/REPLACE',
|
||||
+key: string,
|
||||
@@ -181,17 +138,6 @@ declare module 'react-navigation' {
|
||||
| NavigationSetParamsAction
|
||||
| NavigationResetAction;
|
||||
|
||||
declare type DeprecatedNavigationAction =
|
||||
| DeprecatedNavigationInitAction
|
||||
| DeprecatedNavigationNavigateAction
|
||||
| DeprecatedNavigationBackAction
|
||||
| DeprecatedNavigationSetParamsAction
|
||||
| DeprecatedNavigationResetAction;
|
||||
|
||||
declare export type PossiblyDeprecatedNavigationAction =
|
||||
| NavigationAction
|
||||
| DeprecatedNavigationAction;
|
||||
|
||||
/**
|
||||
* NavigationState is a tree of routes for a single navigator, where each
|
||||
* child route may either be a NavigationScreenRoute or a
|
||||
@@ -381,6 +327,7 @@ declare module 'react-navigation' {
|
||||
|
||||
declare export type NavigationStackScreenOptions = NavigationScreenOptions & {
|
||||
header?: ?(React$Node | (HeaderProps => React$Node)),
|
||||
headerTransparent?: boolean,
|
||||
headerTitle?: string | React$Node | React$ElementType,
|
||||
headerTitleStyle?: AnimatedTextStyleProp,
|
||||
headerTitleAllowFontScaling?: boolean,
|
||||
@@ -393,6 +340,7 @@ declare module 'react-navigation' {
|
||||
headerPressColorAndroid?: string,
|
||||
headerRight?: React$Node,
|
||||
headerStyle?: ViewStyleProp,
|
||||
headerBackground?: React$Node | React$ElementType,
|
||||
gesturesEnabled?: boolean,
|
||||
gestureResponseDistance?: { vertical?: number, horizontal?: number },
|
||||
gestureDirection?: 'default' | 'inverted',
|
||||
@@ -480,7 +428,7 @@ declare module 'react-navigation' {
|
||||
*/
|
||||
|
||||
declare export type NavigationDispatch = (
|
||||
action: PossiblyDeprecatedNavigationAction
|
||||
action: NavigationAction
|
||||
) => boolean;
|
||||
|
||||
declare export type NavigationProp<S> = {
|
||||
@@ -787,9 +735,6 @@ declare module 'react-navigation' {
|
||||
(payload: { uri: string }): NavigationUriAction,
|
||||
toString: () => string,
|
||||
},
|
||||
mapDeprecatedActionAndWarn: (
|
||||
action: PossiblyDeprecatedNavigationAction
|
||||
) => NavigationAction,
|
||||
};
|
||||
|
||||
declare type _RouterProp<S: NavigationState, O: {}> = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-navigation",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"description": "Routing and navigation for your React Native apps",
|
||||
"main": "src/react-navigation.js",
|
||||
"repository": {
|
||||
|
||||
@@ -57,6 +57,7 @@ const pop = createAction(POP, payload => ({
|
||||
const popToTop = createAction(POP_TO_TOP, payload => ({
|
||||
type: POP_TO_TOP,
|
||||
immediate: payload && payload.immediate,
|
||||
key: payload && payload.key,
|
||||
}));
|
||||
|
||||
const push = createAction(PUSH, payload => {
|
||||
@@ -106,59 +107,6 @@ const completeTransition = createAction(COMPLETE_TRANSITION, payload => ({
|
||||
key: payload && payload.key,
|
||||
}));
|
||||
|
||||
const mapDeprecatedNavigateAction = action => {
|
||||
if (action.type === 'Navigate') {
|
||||
const payload = {
|
||||
routeName: action.routeName,
|
||||
params: action.params,
|
||||
};
|
||||
if (action.action) {
|
||||
payload.action = mapDeprecatedNavigateAction(action.action);
|
||||
}
|
||||
return navigate(payload);
|
||||
}
|
||||
return action;
|
||||
};
|
||||
|
||||
const mapDeprecatedAction = action => {
|
||||
if (action.type === 'Back') {
|
||||
return back(action);
|
||||
} else if (action.type === 'Init') {
|
||||
return init(action);
|
||||
} else if (action.type === 'Navigate') {
|
||||
return mapDeprecatedNavigateAction(action);
|
||||
} else if (action.type === 'Reset') {
|
||||
return reset({
|
||||
index: action.index,
|
||||
key: action.key,
|
||||
actions: action.actions.map(mapDeprecatedNavigateAction),
|
||||
});
|
||||
} else if (action.type === 'SetParams') {
|
||||
return setParams(action);
|
||||
}
|
||||
return action;
|
||||
};
|
||||
|
||||
const mapDeprecatedActionAndWarn = action => {
|
||||
const newAction = mapDeprecatedAction(action);
|
||||
if (newAction !== action) {
|
||||
const oldType = action.type;
|
||||
const newType = newAction.type;
|
||||
console.warn(
|
||||
[
|
||||
`The action type '${oldType}' has been renamed to '${newType}'.`,
|
||||
`'${oldType}' will continue to work while in beta but will be removed`,
|
||||
'in the first major release. Moving forward, you should use the',
|
||||
'action constants and action creators exported by this library in',
|
||||
"the 'actions' object.",
|
||||
'See https://github.com/react-community/react-navigation/pull/120 for',
|
||||
'more details.',
|
||||
].join(' ')
|
||||
);
|
||||
}
|
||||
return newAction;
|
||||
};
|
||||
|
||||
export default {
|
||||
// Action constants
|
||||
BACK,
|
||||
@@ -185,7 +133,4 @@ export default {
|
||||
setParams,
|
||||
uri,
|
||||
completeTransition,
|
||||
|
||||
// TODO: Remove once old actions are deprecated
|
||||
mapDeprecatedActionAndWarn,
|
||||
};
|
||||
|
||||
@@ -166,8 +166,7 @@ export default function createNavigationContainer(Component) {
|
||||
|
||||
// Per-tick temporary storage for state.nav
|
||||
|
||||
dispatch = inputAction => {
|
||||
const action = NavigationActions.mapDeprecatedActionAndWarn(inputAction);
|
||||
dispatch = action => {
|
||||
if (!this._isStateful()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
pointerEvents="auto"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#E9E9EF",
|
||||
"backgroundColor": "#EFEFF4",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
@@ -75,163 +75,108 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
cardStyle={undefined}
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
headerTransitionPreset={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
"initHeight": 0,
|
||||
"initWidth": 0,
|
||||
"isMeasured": false,
|
||||
"width": 0,
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderBottomColor": "#A7A7AA",
|
||||
"borderBottomWidth": 0.5,
|
||||
"height": 64,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
}
|
||||
}
|
||||
leftButtonInterpolator={[Function]}
|
||||
leftInterpolator={[Function]}
|
||||
leftLabelInterpolator={[Function]}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": [Function],
|
||||
"getParam": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"isTransitioning": false,
|
||||
"key": "StackRouterRoot",
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "id-0-1",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
rightInterpolator={[Function]}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
"getComponentForRouteName": [Function],
|
||||
"getComponentForState": [Function],
|
||||
"getPathAndParamsForState": [Function],
|
||||
"getScreenConfig": [Function],
|
||||
"getScreenOptions": [Function],
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
titleFromLeftInterpolator={[Function]}
|
||||
titleInterpolator={[Function]}
|
||||
transitionConfig={undefined}
|
||||
transitionPreset="fade-in-place"
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderBottomColor": "rgba(0, 0, 0, .3)",
|
||||
"borderBottomWidth": 0.5,
|
||||
"height": 64,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"bottom": 0,
|
||||
"flexDirection": "row",
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
},
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "transparent",
|
||||
"bottom": 0,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"left": 70,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 70,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
<Text
|
||||
accessibilityTraits="header"
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
pointerEvents="box-none"
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "transparent",
|
||||
"bottom": 0,
|
||||
"justifyContent": "center",
|
||||
"left": 70,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 70,
|
||||
"top": 0,
|
||||
"color": "rgba(0, 0, 0, .9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "700",
|
||||
"marginHorizontal": 16,
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityTraits="header"
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, .9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "700",
|
||||
"marginHorizontal": 16,
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
Welcome anonymous
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "transparent",
|
||||
"bottom": 0,
|
||||
"justifyContent": "center",
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
Welcome anonymous
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "transparent",
|
||||
"bottom": 0,
|
||||
"flexDirection": "row",
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
>
|
||||
<View />
|
||||
</View>
|
||||
}
|
||||
>
|
||||
<View />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -288,7 +233,7 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
pointerEvents="auto"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#E9E9EF",
|
||||
"backgroundColor": "#EFEFF4",
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
@@ -315,145 +260,90 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
cardStyle={undefined}
|
||||
collapsable={undefined}
|
||||
getScreenDetails={[Function]}
|
||||
headerMode={undefined}
|
||||
headerTransitionPreset={undefined}
|
||||
index={0}
|
||||
layout={
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
"initHeight": 0,
|
||||
"initWidth": 0,
|
||||
"isMeasured": false,
|
||||
"width": 0,
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderBottomColor": "#A7A7AA",
|
||||
"borderBottomWidth": 0.5,
|
||||
"height": 64,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
}
|
||||
}
|
||||
leftButtonInterpolator={[Function]}
|
||||
leftInterpolator={[Function]}
|
||||
leftLabelInterpolator={[Function]}
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": [Function],
|
||||
"getParam": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"isTransitioning": false,
|
||||
"key": "StackRouterRoot",
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "id-0-0",
|
||||
"routeName": "Home",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
rightInterpolator={[Function]}
|
||||
router={
|
||||
Object {
|
||||
"getActionForPathAndParams": [Function],
|
||||
"getComponentForRouteName": [Function],
|
||||
"getComponentForState": [Function],
|
||||
"getPathAndParamsForState": [Function],
|
||||
"getScreenConfig": [Function],
|
||||
"getScreenOptions": [Function],
|
||||
"getStateForAction": [Function],
|
||||
}
|
||||
}
|
||||
titleFromLeftInterpolator={[Function]}
|
||||
titleInterpolator={[Function]}
|
||||
transitionConfig={undefined}
|
||||
transitionPreset="fade-in-place"
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
onLayout={[Function]}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#F7F7F7",
|
||||
"borderBottomColor": "rgba(0, 0, 0, .3)",
|
||||
"borderBottomWidth": 0.5,
|
||||
"height": 64,
|
||||
"paddingBottom": 0,
|
||||
"paddingLeft": 0,
|
||||
"paddingRight": 0,
|
||||
"paddingTop": 20,
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"bottom": 0,
|
||||
"flexDirection": "row",
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
collapsable={undefined}
|
||||
pointerEvents="box-none"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
},
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "transparent",
|
||||
"bottom": 0,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
<Text
|
||||
accessibilityTraits="header"
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
pointerEvents="box-none"
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "transparent",
|
||||
"bottom": 0,
|
||||
"justifyContent": "center",
|
||||
"left": 0,
|
||||
"opacity": 1,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"color": "rgba(0, 0, 0, .9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "700",
|
||||
"marginHorizontal": 16,
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityTraits="header"
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
collapsable={undefined}
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
onLayout={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(0, 0, 0, .9)",
|
||||
"fontSize": 17,
|
||||
"fontWeight": "700",
|
||||
"marginHorizontal": 16,
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
Welcome anonymous
|
||||
</Text>
|
||||
</View>
|
||||
Welcome anonymous
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -305,6 +305,12 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
|
||||
// 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) {
|
||||
// Refuse to handle pop to top if a key is given that doesn't correspond
|
||||
// to this router
|
||||
if (action.key && state.key !== action.key) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
@@ -457,6 +457,35 @@ describe('StackRouter', () => {
|
||||
expect(state3 && state3.routes[1].index).toEqual(0);
|
||||
});
|
||||
|
||||
test('popToTop targets StackRouter by key if specified', () => {
|
||||
const ChildNavigator = () => <div />;
|
||||
ChildNavigator.router = StackRouter({
|
||||
Baz: { screen: () => <div /> },
|
||||
Qux: { screen: () => <div /> },
|
||||
});
|
||||
const router = StackRouter({
|
||||
Foo: { screen: () => <div /> },
|
||||
Bar: { screen: ChildNavigator },
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
},
|
||||
state
|
||||
);
|
||||
const barKey = state2.routes[1].routes[0].key;
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.POP_TO_TOP,
|
||||
key: state2.key,
|
||||
},
|
||||
state2
|
||||
);
|
||||
expect(state3 && state3.index).toEqual(0);
|
||||
});
|
||||
|
||||
test('popToTop works as expected', () => {
|
||||
const TestRouter = StackRouter({
|
||||
foo: { screen: () => <div /> },
|
||||
@@ -1506,40 +1535,6 @@ describe('StackRouter', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('Maps old actions (uses "Handles the reset action" test)', () => {
|
||||
global.console.warn = jest.fn();
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
Bar: {
|
||||
screen: () => <div />,
|
||||
},
|
||||
});
|
||||
const initAction = NavigationActions.mapDeprecatedActionAndWarn({
|
||||
type: 'Init',
|
||||
});
|
||||
const state = router.getStateForAction(initAction);
|
||||
const resetAction = NavigationActions.mapDeprecatedActionAndWarn({
|
||||
type: 'Reset',
|
||||
actions: [
|
||||
{ type: 'Navigate', routeName: 'Foo', params: { bar: '42' } },
|
||||
{ type: 'Navigate', routeName: 'Bar' },
|
||||
],
|
||||
index: 1,
|
||||
});
|
||||
const state2 = router.getStateForAction(resetAction, state);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.routes[0].params).toEqual({ bar: '42' });
|
||||
expect(state2 && state2.routes[0].routeName).toEqual('Foo');
|
||||
expect(state2 && state2.routes[1].routeName).toEqual('Bar');
|
||||
expect(console.warn).toBeCalledWith(
|
||||
expect.stringContaining(
|
||||
"The action type 'Init' has been renamed to 'Navigation/INIT'"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('URI encoded string get passed to deep link', () => {
|
||||
const uri = 'people/2018%2F02%2F07';
|
||||
const action = TestStackRouter.getActionForPathAndParams(uri);
|
||||
|
||||
@@ -593,29 +593,6 @@ describe('TabRouter', () => {
|
||||
expect(path).toEqual('f/Baz');
|
||||
});
|
||||
|
||||
test('Maps old actions (uses "getStateForAction returns null when navigating to same tab" test)', () => {
|
||||
global.console.warn = jest.fn();
|
||||
const router = TabRouter(
|
||||
{ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig },
|
||||
{ initialRouteName: 'Bar' }
|
||||
);
|
||||
const initAction = NavigationActions.mapDeprecatedActionAndWarn({
|
||||
type: 'Init',
|
||||
});
|
||||
const state = router.getStateForAction(initAction);
|
||||
const navigateAction = NavigationActions.mapDeprecatedActionAndWarn({
|
||||
type: 'Navigate',
|
||||
routeName: 'Bar',
|
||||
});
|
||||
const state2 = router.getStateForAction(navigateAction, state);
|
||||
expect(state2).toEqual(null);
|
||||
expect(console.warn).toBeCalledWith(
|
||||
expect.stringContaining(
|
||||
"The action type 'Init' has been renamed to 'Navigation/INIT'"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('Can navigate to other tab (no router) with params', () => {
|
||||
const ScreenA = () => <div />;
|
||||
const ScreenB = () => <div />;
|
||||
|
||||
@@ -22,7 +22,7 @@ class Card extends React.Component {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
main: {
|
||||
backgroundColor: '#E9E9EF',
|
||||
backgroundColor: '#EFEFF4',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
|
||||
@@ -19,6 +19,7 @@ import getChildEventSubscriber from '../../getChildEventSubscriber';
|
||||
import SceneView from '../SceneView';
|
||||
|
||||
import TransitionConfigs from './TransitionConfigs';
|
||||
import * as ReactNativeFeatures from '../../utils/ReactNativeFeatures';
|
||||
|
||||
const emptyFunction = () => {};
|
||||
|
||||
@@ -166,12 +167,25 @@ class CardStack extends React.Component {
|
||||
}
|
||||
|
||||
_reset(resetToIndex, duration) {
|
||||
Animated.timing(this.props.transitionProps.position, {
|
||||
toValue: resetToIndex,
|
||||
duration,
|
||||
easing: EaseInOut,
|
||||
useNativeDriver: this.props.transitionProps.position.__isNative,
|
||||
}).start();
|
||||
if (
|
||||
Platform.OS === 'ios' &&
|
||||
ReactNativeFeatures.supportsImprovedSpringAnimation()
|
||||
) {
|
||||
Animated.spring(this.props.transitionProps.position, {
|
||||
toValue: resetToIndex,
|
||||
stiffness: 5000,
|
||||
damping: 600,
|
||||
mass: 3,
|
||||
useNativeDriver: this.props.transitionProps.position.__isNative,
|
||||
}).start();
|
||||
} else {
|
||||
Animated.timing(this.props.transitionProps.position, {
|
||||
toValue: resetToIndex,
|
||||
duration,
|
||||
easing: EaseInOut,
|
||||
useNativeDriver: this.props.transitionProps.position.__isNative,
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
_goBack(backFromIndex, duration) {
|
||||
@@ -182,12 +196,7 @@ class CardStack extends React.Component {
|
||||
// dispatched at the end of the transition.
|
||||
this._immediateIndex = toValue;
|
||||
|
||||
Animated.timing(position, {
|
||||
toValue,
|
||||
duration,
|
||||
easing: EaseInOut,
|
||||
useNativeDriver: position.__isNative,
|
||||
}).start(() => {
|
||||
const onCompleteAnimation = () => {
|
||||
this._immediateIndex = null;
|
||||
const backFromScene = scenes.find(s => s.index === toValue + 1);
|
||||
if (!this._isResponding && backFromScene) {
|
||||
@@ -198,7 +207,27 @@ class CardStack extends React.Component {
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (
|
||||
Platform.OS === 'ios' &&
|
||||
ReactNativeFeatures.supportsImprovedSpringAnimation()
|
||||
) {
|
||||
Animated.spring(position, {
|
||||
toValue,
|
||||
stiffness: 5000,
|
||||
damping: 600,
|
||||
mass: 3,
|
||||
useNativeDriver: position.__isNative,
|
||||
}).start(onCompleteAnimation);
|
||||
} else {
|
||||
Animated.timing(position, {
|
||||
toValue,
|
||||
duration,
|
||||
easing: EaseInOut,
|
||||
useNativeDriver: position.__isNative,
|
||||
}).start(onCompleteAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -373,14 +373,14 @@ class Header extends React.PureComponent {
|
||||
hasRightComponent: !!right,
|
||||
});
|
||||
|
||||
const wrapperProps = {
|
||||
style: [StyleSheet.absoluteFill, styles.header],
|
||||
key: `scene_${props.scene.key}`,
|
||||
};
|
||||
|
||||
const { isLandscape, transitionPreset } = this.props;
|
||||
const { options } = this.props.getScreenDetails(props.scene);
|
||||
|
||||
const wrapperProps = {
|
||||
style: styles.header,
|
||||
key: `scene_${props.scene.key}`,
|
||||
};
|
||||
|
||||
if (
|
||||
options.headerLeft ||
|
||||
options.headerBackImage ||
|
||||
@@ -418,8 +418,9 @@ class Header extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
let appBar;
|
||||
const { mode, scene, isLandscape } = this.props;
|
||||
|
||||
if (this.props.mode === 'float') {
|
||||
if (mode === 'float') {
|
||||
const scenesByIndex = {};
|
||||
this.props.scenes.forEach(scene => {
|
||||
scenesByIndex[scene.index] = scene;
|
||||
@@ -438,37 +439,62 @@ class Header extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {
|
||||
scenes,
|
||||
scene,
|
||||
position,
|
||||
screenProps,
|
||||
progress,
|
||||
isLandscape,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const { options } = this.props.getScreenDetails(scene);
|
||||
const { headerStyle } = options;
|
||||
const { headerStyle = {} } = options;
|
||||
const headerStyleObj =
|
||||
typeof headerStyle === 'number'
|
||||
? StyleSheet.flatten(headerStyle)
|
||||
: headerStyle;
|
||||
const appBarHeight = getAppBarHeight(isLandscape);
|
||||
|
||||
const {
|
||||
alignItems,
|
||||
justifyContent,
|
||||
flex,
|
||||
flexDirection,
|
||||
flexGrow,
|
||||
flexShrink,
|
||||
flexBasis,
|
||||
flexWrap,
|
||||
...safeHeaderStyle
|
||||
} = headerStyleObj;
|
||||
|
||||
if (__DEV__) {
|
||||
warnIfHeaderStyleDefined(alignItems, 'alignItems');
|
||||
warnIfHeaderStyleDefined(justifyContent, 'justifyContent');
|
||||
warnIfHeaderStyleDefined(flex, 'flex');
|
||||
warnIfHeaderStyleDefined(flexDirection, 'flexDirection');
|
||||
warnIfHeaderStyleDefined(flexGrow, 'flexGrow');
|
||||
warnIfHeaderStyleDefined(flexShrink, 'flexShrink');
|
||||
warnIfHeaderStyleDefined(flexBasis, 'flexBasis');
|
||||
warnIfHeaderStyleDefined(flexWrap, 'flexWrap');
|
||||
}
|
||||
|
||||
// TODO: warn if any unsafe styles are provided
|
||||
const containerStyles = [
|
||||
styles.container,
|
||||
{
|
||||
height: appBarHeight,
|
||||
},
|
||||
headerStyle,
|
||||
options.headerTransparent
|
||||
? styles.transparentContainer
|
||||
: styles.container,
|
||||
{ height: appBarHeight },
|
||||
safeHeaderStyle,
|
||||
];
|
||||
|
||||
return (
|
||||
<Animated.View {...rest}>
|
||||
<SafeAreaView
|
||||
style={containerStyles}
|
||||
forceInset={{ top: 'always', bottom: 'never' }}
|
||||
>
|
||||
<View style={styles.appBar}>{appBar}</View>
|
||||
</SafeAreaView>
|
||||
</Animated.View>
|
||||
<SafeAreaView
|
||||
forceInset={{ top: 'always', bottom: 'never' }}
|
||||
style={containerStyles}
|
||||
>
|
||||
<View style={StyleSheet.absoluteFill}>{options.headerBackground}</View>
|
||||
<View style={{ flex: 1 }}>{appBar}</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function warnIfHeaderStyleDefined(value, styleProp) {
|
||||
if (value !== undefined) {
|
||||
console.warn(
|
||||
`${styleProp} was given a value of ${value}, this has no effect on headerStyle.`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -477,7 +503,7 @@ let platformContainerStyles;
|
||||
if (Platform.OS === 'ios') {
|
||||
platformContainerStyles = {
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: 'rgba(0, 0, 0, .3)',
|
||||
borderBottomColor: '#A7A7AA',
|
||||
};
|
||||
} else {
|
||||
platformContainerStyles = {
|
||||
@@ -496,15 +522,18 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: Platform.OS === 'ios' ? '#F7F7F7' : '#FFF',
|
||||
...platformContainerStyles,
|
||||
},
|
||||
appBar: {
|
||||
flex: 1,
|
||||
transparentContainer: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
...platformContainerStyles,
|
||||
},
|
||||
header: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
item: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
iconMaskContainer: {
|
||||
@@ -528,23 +557,29 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
title: {
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
left: TITLE_OFFSET,
|
||||
right: TITLE_OFFSET,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
alignItems: Platform.OS === 'ios' ? 'center' : 'flex-start',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: Platform.OS === 'ios' ? 'center' : 'flex-start',
|
||||
},
|
||||
left: {
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
right: {
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ 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],
|
||||
inputRange: [first, index - 0.9, index - 0.2, index, last],
|
||||
outputRange: [0, 0, 0.3, 1, 0],
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import TabBarIcon from './TabBarIcon';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import withOrientation from '../withOrientation';
|
||||
|
||||
const majorVersion = parseInt(Platform.Version, 10);
|
||||
@@ -176,6 +177,24 @@ class TabBarBottom extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
_handleTabPress = index => {
|
||||
const { jumpToIndex, navigation } = this.props;
|
||||
const currentIndex = navigation.state.index;
|
||||
|
||||
if (currentIndex === index) {
|
||||
let childRoute = navigation.state.routes[index];
|
||||
if (childRoute.hasOwnProperty('index') && childRoute.index > 0) {
|
||||
navigation.dispatch(
|
||||
NavigationActions.popToTop({ key: childRoute.key })
|
||||
);
|
||||
} else {
|
||||
// TODO: do something to scroll to top
|
||||
}
|
||||
} else {
|
||||
jumpToIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
position,
|
||||
@@ -235,8 +254,13 @@ class TabBarBottom extends React.PureComponent {
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
onPress={() =>
|
||||
onPress
|
||||
? onPress({ previousScene, scene, jumpToIndex })
|
||||
: jumpToIndex(index)
|
||||
? onPress({
|
||||
previousScene,
|
||||
scene,
|
||||
jumpToIndex,
|
||||
defaultHandler: this._handleTabPress,
|
||||
})
|
||||
: this._handleTabPress(index)
|
||||
}
|
||||
>
|
||||
<Animated.View style={[styles.tab, { backgroundColor }]}>
|
||||
|
||||
@@ -91,7 +91,15 @@ export default class TabBarTop extends React.PureComponent {
|
||||
const onPress = getOnPress(previousScene, scene);
|
||||
|
||||
if (onPress) {
|
||||
onPress({ previousScene, scene, jumpToIndex });
|
||||
// To maintain the same API as `TabbarBottom`, we pass in a `defaultHandler`
|
||||
// even though I don't believe in this case it should be any different
|
||||
// than `jumpToIndex`.
|
||||
onPress({
|
||||
previousScene,
|
||||
scene,
|
||||
jumpToIndex,
|
||||
defaultHandler: jumpToIndex,
|
||||
});
|
||||
} else {
|
||||
jumpToIndex(scene.index);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user