├── .gitignore ├── LICENSE ├── NavigationAbstractPanResponder.js ├── NavigationAnimatedValueSubscription.js ├── NavigationAnimatedView.js ├── NavigationCard.js ├── NavigationCardStack.js ├── NavigationCardStackPanResponder.js ├── NavigationCardStackStyleInterpolator.js ├── NavigationContainer.js ├── NavigationExperimental.js ├── NavigationFindReducer.js ├── NavigationHeader.js ├── NavigationHeaderBackButton.js ├── NavigationHeaderStyleInterpolator.js ├── NavigationHeaderTitle.js ├── NavigationLegacyNavigator.js ├── NavigationLegacyNavigatorRouteStack.js ├── NavigationPagerPanResponder.js ├── NavigationPagerStyleInterpolator.js ├── NavigationPointerEventsContainer.js ├── NavigationPropTypes.js ├── NavigationReducer.js ├── NavigationRootContainer.js ├── NavigationScenesReducer.js ├── NavigationStackReducer.js ├── NavigationStateUtils.js ├── NavigationTabsReducer.js ├── NavigationTypeDefinition.js ├── NavigationView.js ├── README.md ├── assets ├── back.png ├── back@1.5x.android.png ├── back@1.5x.ios.png ├── back@1.5x.windows.png ├── back@1x.android.png ├── back@1x.ios.png ├── back@1x.windows.png ├── back@2x.android.png ├── back@2x.ios.png ├── back@2x.windows.png ├── back@3x.android.png ├── back@3x.ios.png ├── back@3x.windows.png ├── back@4x.android.png ├── back@4x.ios.png └── back@4x.windows.png ├── back_chevron.png └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | install.sh 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For React Native software 4 | 5 | Copyright (c) 2015-present, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /NavigationAbstractPanResponder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationAbstractPanResponder 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const PanResponder = require('react-native').PanResponder; 15 | 16 | const invariant = require('fbjs/lib/invariant'); 17 | 18 | import type { 19 | NavigationPanPanHandlers, 20 | } from 'NavigationTypeDefinition'; 21 | 22 | const EmptyPanHandlers = { 23 | onMoveShouldSetPanResponder: null, 24 | onPanResponderGrant: null, 25 | onPanResponderMove: null, 26 | onPanResponderRelease: null, 27 | onPanResponderTerminate: null, 28 | }; 29 | 30 | /** 31 | * Abstract class that defines the common interface of PanResponder that handles 32 | * the gesture actions. 33 | */ 34 | class NavigationAbstractPanResponder { 35 | 36 | panHandlers: NavigationPanPanHandlers; 37 | 38 | constructor() { 39 | const config = {}; 40 | Object.keys(EmptyPanHandlers).forEach(name => { 41 | const fn: any = (this: any)[name]; 42 | 43 | invariant( 44 | typeof fn === 'function', 45 | 'subclass of `NavigationAbstractPanResponder` must implement method %s', 46 | name 47 | ); 48 | 49 | config[name] = fn.bind(this); 50 | }, this); 51 | 52 | this.panHandlers = PanResponder.create(config).panHandlers; 53 | } 54 | } 55 | 56 | module.exports = NavigationAbstractPanResponder; 57 | -------------------------------------------------------------------------------- /NavigationAnimatedValueSubscription.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Facebook, Inc. All rights reserved. 3 | * 4 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 5 | * all intellectual property and other proprietary rights, in and to the React 6 | * Native CustomComponents software (the "Software"). Subject to your 7 | * compliance with these terms, you are hereby granted a non-exclusive, 8 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 9 | * and (2) reproduce and distribute the Software as part of your own software 10 | * ("Your Software"). Facebook reserves all rights not expressly granted to 11 | * you in this license agreement. 12 | * 13 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 14 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 16 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 17 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 23 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * @providesModule NavigationAnimatedValueSubscription 26 | * @flow 27 | */ 28 | 'use strict'; 29 | 30 | import type { 31 | NavigationAnimatedValue 32 | } from 'NavigationTypeDefinition'; 33 | 34 | class NavigationAnimatedValueSubscription { 35 | _value: NavigationAnimatedValue; 36 | _token: string; 37 | 38 | constructor(value: NavigationAnimatedValue, callback: Function) { 39 | this._value = value; 40 | this._token = value.addListener(callback); 41 | } 42 | 43 | remove(): void { 44 | this._value.removeListener(this._token); 45 | } 46 | } 47 | 48 | module.exports = NavigationAnimatedValueSubscription; 49 | -------------------------------------------------------------------------------- /NavigationAnimatedView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationAnimatedView 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const Animated = require('react-native').Animated; 15 | const NavigationContainer = require('./NavigationContainer'); 16 | const NavigationPropTypes = require('./NavigationPropTypes'); 17 | const NavigationScenesReducer = require('./NavigationScenesReducer'); 18 | const React = require('react'); 19 | const StyleSheet = require('react-native').StyleSheet; 20 | const View = require('react-native').View; 21 | 22 | import type { 23 | NavigationActionCaller, 24 | NavigationAnimatedValue, 25 | NavigationAnimationSetter, 26 | NavigationLayout, 27 | NavigationParentState, 28 | NavigationScene, 29 | NavigationSceneRenderer, 30 | } from 'NavigationTypeDefinition'; 31 | 32 | type Props = { 33 | applyAnimation: NavigationAnimationSetter, 34 | navigationState: NavigationParentState, 35 | onNavigate: NavigationActionCaller, 36 | renderOverlay: ?NavigationSceneRenderer, 37 | renderScene: NavigationSceneRenderer, 38 | style: any, 39 | }; 40 | 41 | type State = { 42 | layout: NavigationLayout, 43 | position: NavigationAnimatedValue, 44 | scenes: Array, 45 | }; 46 | 47 | const PropTypes = require('prop-types'); 48 | 49 | function applyDefaultAnimation( 50 | position: NavigationAnimatedValue, 51 | navigationState: NavigationParentState, 52 | ): void { 53 | Animated.spring( 54 | position, 55 | { 56 | bounciness: 0, 57 | toValue: navigationState.index, 58 | } 59 | ).start(); 60 | } 61 | 62 | class NavigationAnimatedView 63 | extends React.Component { 64 | 65 | _onLayout: (event: any) => void; 66 | _onProgressChange: (data: {value: number}) => void; 67 | _positionListener: any; 68 | 69 | props: Props; 70 | state: State; 71 | 72 | static propTypes = { 73 | applyAnimation: PropTypes.func, 74 | navigationState: NavigationPropTypes.navigationState.isRequired, 75 | onNavigate: PropTypes.func.isRequired, 76 | renderOverlay: PropTypes.func, 77 | renderScene: PropTypes.func.isRequired, 78 | }; 79 | 80 | static defaultProps = { 81 | applyAnimation: applyDefaultAnimation, 82 | }; 83 | 84 | constructor(props: Props, context: any) { 85 | super(props, context); 86 | 87 | // The initial layout isn't measured. Measured layout will be only available 88 | // when the component is mounted. 89 | const layout = { 90 | height: new Animated.Value(0), 91 | initHeight: 0, 92 | initWidth: 0, 93 | isMeasured: false, 94 | width: new Animated.Value(0), 95 | }; 96 | 97 | this.state = { 98 | layout, 99 | position: new Animated.Value(this.props.navigationState.index), 100 | scenes: NavigationScenesReducer([], this.props.navigationState), 101 | }; 102 | } 103 | 104 | componentWillMount(): void { 105 | this._onLayout = this._onLayout.bind(this); 106 | this._onProgressChange = this._onProgressChange.bind(this); 107 | } 108 | 109 | componentDidMount(): void { 110 | this._positionListener = 111 | this.state.position.addListener(this._onProgressChange); 112 | } 113 | 114 | componentWillReceiveProps(nextProps: Props): void { 115 | if (nextProps.navigationState !== this.props.navigationState) { 116 | this.setState({ 117 | scenes: NavigationScenesReducer( 118 | this.state.scenes, 119 | nextProps.navigationState, 120 | this.props.navigationState 121 | ), 122 | }); 123 | } 124 | } 125 | 126 | componentDidUpdate(lastProps: Props): void { 127 | if (lastProps.navigationState.index !== this.props.navigationState.index) { 128 | this.props.applyAnimation( 129 | this.state.position, 130 | this.props.navigationState, 131 | lastProps.navigationState 132 | ); 133 | } 134 | } 135 | 136 | componentWillUnmount(): void { 137 | this.state.position.removeListener(this._positionListener); 138 | } 139 | 140 | _onProgressChange(data: Object): void { 141 | const delta = Math.abs(data.value - this.props.navigationState.index); 142 | if (delta > Number.EPSILON) { 143 | return; 144 | } 145 | 146 | const scenes = this.state.scenes.filter(scene => { 147 | return !scene.isStale; 148 | }); 149 | 150 | if (scenes.length !== this.state.scenes.length) { 151 | this.setState({ scenes }); 152 | } 153 | } 154 | 155 | render(): ReactElement { 156 | const overlay = this._renderOverlay(); 157 | const scenes = this._renderScenes(); 158 | return ( 159 | 162 | 163 | {scenes} 164 | 165 | {overlay} 166 | 167 | ); 168 | } 169 | 170 | _renderScenes(): Array { 171 | return this.state.scenes.map(this._renderScene, this); 172 | } 173 | 174 | _renderScene(scene: NavigationScene): ?ReactElement { 175 | const { 176 | navigationState, 177 | onNavigate, 178 | renderScene, 179 | } = this.props; 180 | 181 | const { 182 | position, 183 | scenes, 184 | } = this.state; 185 | 186 | return renderScene({ 187 | layout: this.state.layout, 188 | navigationState, 189 | onNavigate, 190 | position, 191 | scene, 192 | scenes, 193 | }); 194 | } 195 | 196 | _renderOverlay(): ?ReactElement { 197 | if (this.props.renderOverlay) { 198 | const { 199 | navigationState, 200 | onNavigate, 201 | renderOverlay, 202 | } = this.props; 203 | 204 | const { 205 | position, 206 | scenes, 207 | } = this.state; 208 | 209 | return renderOverlay({ 210 | layout: this.state.layout, 211 | navigationState, 212 | onNavigate, 213 | position, 214 | scene: scenes[navigationState.index], 215 | scenes, 216 | }); 217 | } 218 | return null; 219 | } 220 | 221 | _onLayout(event: any): void { 222 | const {height, width} = event.nativeEvent.layout; 223 | 224 | const layout = { 225 | ...this.state.layout, 226 | initHeight: height, 227 | initWidth: width, 228 | isMeasured: true, 229 | }; 230 | 231 | layout.height.setValue(height); 232 | layout.width.setValue(width); 233 | 234 | this.setState({ layout }); 235 | } 236 | } 237 | 238 | const styles = StyleSheet.create({ 239 | scenes: { 240 | flex: 1, 241 | }, 242 | }); 243 | 244 | NavigationAnimatedView = NavigationContainer.create(NavigationAnimatedView); 245 | 246 | module.exports = NavigationAnimatedView; 247 | -------------------------------------------------------------------------------- /NavigationCard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationCard 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | const Animated = require('react-native').Animated; 36 | const NavigationCardStackPanResponder = require('./NavigationCardStackPanResponder'); 37 | const NavigationCardStackStyleInterpolator = require('./NavigationCardStackStyleInterpolator'); 38 | const NavigationContainer = require('./NavigationContainer'); 39 | const NavigationPagerPanResponder = require('./NavigationPagerPanResponder'); 40 | const NavigationPagerStyleInterpolator = require('./NavigationPagerStyleInterpolator'); 41 | const NavigationPointerEventsContainer = require('./NavigationPointerEventsContainer'); 42 | const NavigationPropTypes = require('./NavigationPropTypes'); 43 | const React = require('react'); 44 | const StyleSheet = require('react-native').StyleSheet; 45 | const View = require('react-native').View; 46 | 47 | import type { 48 | NavigationPanPanHandlers, 49 | NavigationSceneRenderer, 50 | NavigationSceneRendererProps, 51 | } from 'NavigationTypeDefinition'; 52 | 53 | type SceneViewProps = { 54 | sceneRenderer: NavigationSceneRenderer, 55 | sceneRendererProps: NavigationSceneRendererProps, 56 | }; 57 | 58 | type Props = NavigationSceneRendererProps & { 59 | onComponentRef: (ref: any) => void, 60 | panHandlers: ?NavigationPanPanHandlers, 61 | pointerEvents: string, 62 | renderScene: NavigationSceneRenderer, 63 | style: any, 64 | }; 65 | 66 | const PropTypes = require('prop-types'); 67 | 68 | class SceneView extends React.Component { 69 | 70 | static propTypes = { 71 | sceneRenderer: PropTypes.func.isRequired, 72 | sceneRendererProps: NavigationPropTypes.SceneRenderer, 73 | }; 74 | 75 | shouldComponentUpdate(nextProps: SceneViewProps, nextState: any): boolean { 76 | return ( 77 | nextProps.sceneRendererProps.scene.navigationState !== 78 | this.props.sceneRendererProps.scene.navigationState 79 | ); 80 | } 81 | 82 | render(): ?ReactElement { 83 | return this.props.sceneRenderer(this.props.sceneRendererProps); 84 | } 85 | } 86 | 87 | /** 88 | * Component that renders the scene as card for the . 89 | */ 90 | class NavigationCard extends React.Component { 91 | props: Props; 92 | 93 | static propTypes = { 94 | ...NavigationPropTypes.SceneRendererProps, 95 | onComponentRef: PropTypes.func.isRequired, 96 | panHandlers: NavigationPropTypes.panHandlers, 97 | pointerEvents: PropTypes.string.isRequired, 98 | renderScene: PropTypes.func.isRequired, 99 | style: PropTypes.any, 100 | }; 101 | 102 | render(): ReactElement { 103 | const { 104 | panHandlers, 105 | pointerEvents, 106 | renderScene, 107 | style, 108 | ...props /* NavigationSceneRendererProps */ 109 | } = this.props; 110 | 111 | const viewStyle = style === undefined ? 112 | NavigationCardStackStyleInterpolator.forHorizontal(props) : 113 | style; 114 | 115 | const viewPanHandlers = panHandlers === undefined ? 116 | NavigationCardStackPanResponder.forHorizontal(props) : 117 | panHandlers; 118 | 119 | return ( 120 | 125 | 129 | 130 | ); 131 | } 132 | } 133 | 134 | const styles = StyleSheet.create({ 135 | main: { 136 | backgroundColor: 'white', 137 | bottom: 0, 138 | left: 0, 139 | position: 'absolute', 140 | right: 0, 141 | top: 0, 142 | }, 143 | }); 144 | 145 | NavigationCard = NavigationPointerEventsContainer.create(NavigationCard); 146 | NavigationCard = NavigationContainer.create(NavigationCard); 147 | 148 | // Export these buil-in interaction modules. 149 | NavigationCard.CardStackPanResponder = NavigationCardStackPanResponder; 150 | NavigationCard.CardStackStyleInterpolator = NavigationCardStackStyleInterpolator; 151 | NavigationCard.PagerPanResponder = NavigationPagerPanResponder; 152 | NavigationCard.PagerStyleInterpolator = NavigationPagerStyleInterpolator; 153 | 154 | module.exports = NavigationCard; 155 | -------------------------------------------------------------------------------- /NavigationCardStack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationCardStack 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | const NavigationAnimatedView = require('./NavigationAnimatedView'); 36 | const NavigationCard = require('./NavigationCard'); 37 | const NavigationCardStackStyleInterpolator = require('./NavigationCardStackStyleInterpolator'); 38 | const NavigationContainer = require('./NavigationContainer'); 39 | const NavigationCardStackPanResponder = require('./NavigationCardStackPanResponder'); 40 | const NavigationPropTypes = require('./NavigationPropTypes'); 41 | const React = require('react'); 42 | const StyleSheet = require('react-native').StyleSheet; 43 | 44 | const emptyFunction = require('fbjs/lib/emptyFunction'); 45 | 46 | const PropTypes = require('prop-types'); 47 | const {Directions} = NavigationCardStackPanResponder; 48 | 49 | import type { 50 | NavigationParentState, 51 | NavigationSceneRenderer, 52 | NavigationSceneRendererProps, 53 | } from 'NavigationTypeDefinition'; 54 | 55 | import type { 56 | NavigationGestureDirection, 57 | } from 'NavigationCardStackPanResponder'; 58 | 59 | type Props = { 60 | direction: NavigationGestureDirection, 61 | navigationState: NavigationParentState, 62 | renderOverlay: ?NavigationSceneRenderer, 63 | renderScene: NavigationSceneRenderer, 64 | }; 65 | 66 | type DefaultProps = { 67 | direction: NavigationGestureDirection, 68 | renderOverlay: ?NavigationSceneRenderer, 69 | }; 70 | 71 | /** 72 | * A controlled navigation view that renders a stack of cards. 73 | * 74 | * +------------+ 75 | * +-+ | 76 | * +-+ | | 77 | * | | | | 78 | * | | | Focused | 79 | * | | | Card | 80 | * | | | | 81 | * +-+ | | 82 | * +-+ | 83 | * +------------+ 84 | */ 85 | class NavigationCardStack extends React.PureComponent { 86 | _renderScene : NavigationSceneRenderer; 87 | 88 | static propTypes = { 89 | direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]), 90 | navigationState: NavigationPropTypes.navigationParentState.isRequired, 91 | renderOverlay: PropTypes.func, 92 | renderScene: PropTypes.func.isRequired, 93 | }; 94 | 95 | static defaultProps: DefaultProps = { 96 | direction: Directions.HORIZONTAL, 97 | renderOverlay: emptyFunction.thatReturnsNull, 98 | }; 99 | 100 | constructor(props: Props, context: any) { 101 | super(props, context); 102 | } 103 | 104 | componentWillMount(): void { 105 | this._renderScene = this._renderScene.bind(this); 106 | } 107 | 108 | render(): ReactElement { 109 | return ( 110 | 117 | ); 118 | } 119 | 120 | _renderScene(props: NavigationSceneRendererProps): ReactElement { 121 | const isVertical = this.props.direction === 'vertical'; 122 | 123 | const style = isVertical ? 124 | NavigationCardStackStyleInterpolator.forVertical(props) : 125 | NavigationCardStackStyleInterpolator.forHorizontal(props); 126 | 127 | const panHandlers = isVertical ? 128 | NavigationCardStackPanResponder.forVertical(props) : 129 | NavigationCardStackPanResponder.forHorizontal(props); 130 | 131 | return ( 132 | 139 | ); 140 | } 141 | } 142 | 143 | const styles = StyleSheet.create({ 144 | animatedView: { 145 | flex: 1, 146 | }, 147 | }); 148 | 149 | module.exports = NavigationContainer.create(NavigationCardStack); 150 | -------------------------------------------------------------------------------- /NavigationCardStackPanResponder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationCardStackPanResponder 10 | * @flow 11 | * @typechecks 12 | */ 13 | 'use strict'; 14 | 15 | const Animated = require('react-native').Animated; 16 | const NavigationAbstractPanResponder = require('./NavigationAbstractPanResponder'); 17 | 18 | const clamp = require('clamp'); 19 | 20 | import type { 21 | NavigationPanPanHandlers, 22 | NavigationSceneRendererProps, 23 | } from 'NavigationTypeDefinition'; 24 | 25 | /** 26 | * The duration of the card animation in milliseconds. 27 | */ 28 | const ANIMATION_DURATION = 250; 29 | 30 | /** 31 | * The threshold to invoke the `onNavigate` action. 32 | * For instance, `1 / 3` means that moving greater than 1 / 3 of the width of 33 | * the view will navigate. 34 | */ 35 | const POSITION_THRESHOLD = 1 / 3; 36 | 37 | /** 38 | * The threshold (in pixels) to start the gesture action. 39 | */ 40 | const RESPOND_THRESHOLD = 15; 41 | 42 | /** 43 | * The distance from the edge of the navigator which gesture response can start for. 44 | * For horizontal scroll views, a distance of 30 from the left of the screen is the 45 | * standard maximum position to start touch responsiveness. 46 | */ 47 | const RESPOND_POSITION_MAX_HORIZONTAL = 30; 48 | const RESPOND_POSITION_MAX_VERTICAL = null; 49 | 50 | /** 51 | * The threshold (in pixels) to finish the gesture action. 52 | */ 53 | const DISTANCE_THRESHOLD = 100; 54 | 55 | /** 56 | * Primitive gesture directions. 57 | */ 58 | const Directions = { 59 | 'HORIZONTAL': 'horizontal', 60 | 'VERTICAL': 'vertical', 61 | }; 62 | 63 | export type NavigationGestureDirection = 'horizontal' | 'vertical'; 64 | 65 | /** 66 | * Primitive gesture actions. 67 | */ 68 | const Actions = { 69 | // The gesture to navigate backward. 70 | // This is done by swiping from the left to the right or from the top to the 71 | // bottom. 72 | BACK: {type: 'back'}, 73 | }; 74 | 75 | /** 76 | * Pan responder that handles gesture for a card in the cards stack. 77 | * 78 | * +------------+ 79 | * +-+ | 80 | * +-+ | | 81 | * | | | | 82 | * | | | Focused | 83 | * | | | Card | 84 | * | | | | 85 | * +-+ | | 86 | * +-+ | 87 | * +------------+ 88 | */ 89 | class NavigationCardStackPanResponder extends NavigationAbstractPanResponder { 90 | 91 | _isResponding: boolean; 92 | _isVertical: boolean; 93 | _props: NavigationSceneRendererProps; 94 | _startValue: number; 95 | 96 | constructor( 97 | direction: NavigationGestureDirection, 98 | props: NavigationSceneRendererProps, 99 | ) { 100 | super(); 101 | this._isResponding = false; 102 | this._isVertical = direction === Directions.VERTICAL; 103 | this._props = props; 104 | this._startValue = 0; 105 | } 106 | 107 | onMoveShouldSetPanResponder(event: any, gesture: any): boolean { 108 | const props = this._props; 109 | 110 | if (props.navigationState.index !== props.scene.index) { 111 | return false; 112 | } 113 | 114 | const layout = props.layout; 115 | const isVertical = this._isVertical; 116 | const index = props.navigationState.index; 117 | const currentDragDistance = gesture[isVertical ? 'dy' : 'dx']; 118 | const currentDragPosition = gesture[isVertical ? 'moveY' : 'moveX']; 119 | const maxDragDistance = isVertical ? 120 | layout.height.__getValue() : 121 | layout.width.__getValue(); 122 | 123 | const positionMax = isVertical ? 124 | RESPOND_POSITION_MAX_VERTICAL : 125 | RESPOND_POSITION_MAX_HORIZONTAL; 126 | 127 | if (positionMax != null && currentDragPosition > positionMax) { 128 | return false; 129 | } 130 | 131 | return ( 132 | Math.abs(currentDragDistance) > RESPOND_THRESHOLD && 133 | maxDragDistance > 0 && 134 | index > 0 135 | ); 136 | } 137 | 138 | onPanResponderGrant(): void { 139 | this._isResponding = false; 140 | this._props.position.stopAnimation((value: number) => { 141 | this._isResponding = true; 142 | this._startValue = value; 143 | }); 144 | } 145 | 146 | onPanResponderMove(event: any, gesture: any): void { 147 | if (!this._isResponding) { 148 | return; 149 | } 150 | 151 | const props = this._props; 152 | const layout = props.layout; 153 | const isVertical = this._isVertical; 154 | const axis = isVertical ? 'dy' : 'dx'; 155 | const index = props.navigationState.index; 156 | const distance = isVertical ? 157 | layout.height.__getValue() : 158 | layout.width.__getValue(); 159 | 160 | const value = clamp( 161 | index - 1, 162 | this._startValue - (gesture[axis] / distance), 163 | index 164 | ); 165 | 166 | props.position.setValue(value); 167 | } 168 | 169 | onPanResponderRelease(event: any, gesture: any): void { 170 | if (!this._isResponding) { 171 | return; 172 | } 173 | 174 | this._isResponding = false; 175 | 176 | const props = this._props; 177 | const isVertical = this._isVertical; 178 | const axis = isVertical ? 'dy' : 'dx'; 179 | const index = props.navigationState.index; 180 | const distance = gesture[axis]; 181 | 182 | props.position.stopAnimation((value: number) => { 183 | this._reset(); 184 | if (distance > DISTANCE_THRESHOLD || value <= index - POSITION_THRESHOLD) { 185 | props.onNavigate(Actions.BACK); 186 | } 187 | }); 188 | } 189 | 190 | onPanResponderTerminate(): void { 191 | this._isResponding = false; 192 | this._reset(); 193 | } 194 | 195 | _reset(): void { 196 | const props = this._props; 197 | Animated.timing( 198 | props.position, 199 | { 200 | toValue: props.navigationState.index, 201 | duration: ANIMATION_DURATION, 202 | } 203 | ).start(); 204 | } 205 | } 206 | 207 | function createPanHandlers( 208 | direction: NavigationGestureDirection, 209 | props: NavigationSceneRendererProps, 210 | ): NavigationPanPanHandlers { 211 | const responder = new NavigationCardStackPanResponder(direction, props); 212 | return responder.panHandlers; 213 | } 214 | 215 | function forHorizontal( 216 | props: NavigationSceneRendererProps, 217 | ): NavigationPanPanHandlers { 218 | return createPanHandlers(Directions.HORIZONTAL, props); 219 | } 220 | 221 | function forVertical( 222 | props: NavigationSceneRendererProps, 223 | ): NavigationPanPanHandlers { 224 | return createPanHandlers(Directions.VERTICAL, props); 225 | } 226 | 227 | module.exports = { 228 | // constants 229 | ANIMATION_DURATION, 230 | DISTANCE_THRESHOLD, 231 | POSITION_THRESHOLD, 232 | RESPOND_THRESHOLD, 233 | 234 | // enums 235 | Actions, 236 | Directions, 237 | 238 | // methods. 239 | forHorizontal, 240 | forVertical, 241 | }; 242 | -------------------------------------------------------------------------------- /NavigationCardStackStyleInterpolator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationCardStackStyleInterpolator 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | import type { 36 | NavigationSceneRendererProps, 37 | } from 'NavigationTypeDefinition'; 38 | 39 | /** 40 | * Utility that builds the style for the card in the cards stack. 41 | * 42 | * +------------+ 43 | * +-+ | 44 | * +-+ | | 45 | * | | | | 46 | * | | | Focused | 47 | * | | | Card | 48 | * | | | | 49 | * +-+ | | 50 | * +-+ | 51 | * +------------+ 52 | */ 53 | 54 | /** 55 | * Render the initial style when the initial layout isn't measured yet. 56 | */ 57 | function forInitial(props: NavigationSceneRendererProps): Object { 58 | const { 59 | navigationState, 60 | scene, 61 | } = props; 62 | 63 | const focused = navigationState.index === scene.index; 64 | const opacity = focused ? 1 : 0; 65 | // If not focused, move the scene to the far away. 66 | const translate = focused ? 0 : 1000000; 67 | return { 68 | opacity, 69 | transform: [ 70 | { translateX: translate }, 71 | { translateY: translate }, 72 | ], 73 | }; 74 | } 75 | 76 | function forHorizontal(props: NavigationSceneRendererProps): Object { 77 | const { 78 | layout, 79 | position, 80 | scene, 81 | } = props; 82 | 83 | if (!layout.isMeasured) { 84 | return forInitial(props); 85 | } 86 | 87 | const index = scene.index; 88 | const inputRange = [index - 1, index, index + 1]; 89 | const width = layout.initWidth; 90 | 91 | const opacity = position.interpolate({ 92 | inputRange, 93 | outputRange: [1, 1, 0.3], 94 | }); 95 | 96 | const scale = position.interpolate({ 97 | inputRange, 98 | outputRange: [1, 1, 0.95], 99 | }); 100 | 101 | const translateY = 0; 102 | const translateX = position.interpolate({ 103 | inputRange, 104 | outputRange: [width, 0, -10], 105 | }); 106 | 107 | return { 108 | opacity, 109 | transform: [ 110 | { scale }, 111 | { translateX }, 112 | { translateY }, 113 | ], 114 | }; 115 | } 116 | 117 | function forVertical(props: NavigationSceneRendererProps): Object { 118 | const { 119 | layout, 120 | position, 121 | scene, 122 | } = props; 123 | 124 | if (!layout.isMeasured) { 125 | return forInitial(props); 126 | } 127 | 128 | const index = scene.index; 129 | const inputRange = [index - 1, index, index + 1]; 130 | const height = layout.initHeight; 131 | 132 | const opacity = position.interpolate({ 133 | inputRange, 134 | outputRange: [1, 1, 0.3], 135 | }); 136 | 137 | const scale = position.interpolate({ 138 | inputRange, 139 | outputRange: [1, 1, 0.95], 140 | }); 141 | 142 | const translateX = 0; 143 | const translateY = position.interpolate({ 144 | inputRange, 145 | outputRange: [height, 0, -10], 146 | }); 147 | 148 | return { 149 | opacity, 150 | transform: [ 151 | { scale }, 152 | { translateX }, 153 | { translateY }, 154 | ], 155 | }; 156 | } 157 | 158 | module.exports = { 159 | forHorizontal, 160 | forVertical, 161 | }; 162 | -------------------------------------------------------------------------------- /NavigationContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationContainer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | var React = require('react'); 15 | const PropTypes = require('prop-types'); 16 | var NavigationRootContainer = require('./NavigationRootContainer'); 17 | 18 | function createNavigationContainer( 19 | Component: ReactClass, 20 | ): ReactClass & Object { 21 | class NavigationComponent extends React.Component { 22 | render() { 23 | return ( 24 | 28 | ); 29 | } 30 | getNavigationHandler() { 31 | return this.props.onNavigate || this.context.onNavigate; 32 | } 33 | getChildContext() { 34 | return { 35 | onNavigate: this.getNavigationHandler(), 36 | }; 37 | } 38 | } 39 | NavigationComponent.contextTypes = { 40 | onNavigate: PropTypes.func, 41 | }; 42 | NavigationComponent.childContextTypes = { 43 | onNavigate: PropTypes.func, 44 | }; 45 | return NavigationComponent; 46 | } 47 | 48 | var NavigationContainer = { 49 | create: createNavigationContainer, 50 | RootContainer: NavigationRootContainer, 51 | }; 52 | 53 | 54 | module.exports = NavigationContainer; 55 | -------------------------------------------------------------------------------- /NavigationExperimental.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationExperimental 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const NavigationAnimatedView = require('./NavigationAnimatedView'); 15 | const NavigationCard = require('./NavigationCard'); 16 | const NavigationCardStack = require('./NavigationCardStack'); 17 | const NavigationContainer = require('./NavigationContainer'); 18 | const NavigationHeader = require('./NavigationHeader'); 19 | const NavigationLegacyNavigator = null; 20 | const NavigationReducer = require('./NavigationReducer'); 21 | const NavigationRootContainer = require('./NavigationRootContainer'); 22 | const NavigationStateUtils = require('./NavigationStateUtils'); 23 | const NavigationView = require('./NavigationView'); 24 | const NavigationPropTypes = require('./NavigationPropTypes'); 25 | 26 | const NavigationExperimental = { 27 | // Core 28 | StateUtils: NavigationStateUtils, 29 | Reducer: NavigationReducer, 30 | 31 | // Containers 32 | Container: NavigationContainer, 33 | RootContainer: NavigationRootContainer, 34 | 35 | // Views 36 | View: NavigationView, 37 | AnimatedView: NavigationAnimatedView, 38 | 39 | // CustomComponents: 40 | Card: NavigationCard, 41 | CardStack: NavigationCardStack, 42 | Header: NavigationHeader, 43 | LegacyNavigator: NavigationLegacyNavigator, 44 | 45 | PropTypes: NavigationPropTypes, 46 | }; 47 | 48 | module.exports = NavigationExperimental; 49 | -------------------------------------------------------------------------------- /NavigationFindReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationFindReducer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | /* 15 | * NavigationFindReducer takes an array of reducers, and returns a reducer that 16 | * iterates through all of the reducers and the result of the first reducer 17 | * that modifies the input 18 | */ 19 | 20 | import type { 21 | NavigationState, 22 | NavigationReducer 23 | } from 'NavigationTypeDefinition'; 24 | 25 | function NavigationFindReducer( 26 | reducers: Array, 27 | defaultState: NavigationState, 28 | ): NavigationReducer { 29 | return function(lastState: ?NavigationState, action: ?any): NavigationState { 30 | for (let i = 0; i < reducers.length; i++) { 31 | let reducer = reducers[i]; 32 | let newState = reducer(lastState, action); 33 | if (newState !== lastState) { 34 | return newState || defaultState; 35 | } 36 | } 37 | return lastState || defaultState; 38 | }; 39 | } 40 | 41 | module.exports = NavigationFindReducer; 42 | -------------------------------------------------------------------------------- /NavigationHeader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationHeader 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | const React = require('react'); 36 | const ReactNative = require('react-native'); 37 | const NavigationContainer = require('./NavigationContainer'); 38 | const NavigationHeaderTitle = require('./NavigationHeaderTitle'); 39 | const NavigationHeaderBackButton = require('./NavigationHeaderBackButton'); 40 | const NavigationPropTypes = require('./NavigationPropTypes'); 41 | const NavigationHeaderStyleInterpolator = require('./NavigationHeaderStyleInterpolator'); 42 | 43 | const { 44 | Animated, 45 | Platform, 46 | StyleSheet, 47 | View, 48 | ViewPropTypes, 49 | } = ReactNative; 50 | 51 | import type { 52 | NavigationSceneRenderer, 53 | NavigationSceneRendererProps, 54 | NavigationStyleInterpolator, 55 | } from 'NavigationTypeDefinition'; 56 | 57 | type DefaultProps = { 58 | renderLeftComponent: NavigationSceneRenderer, 59 | renderRightComponent: NavigationSceneRenderer, 60 | renderTitleComponent: NavigationSceneRenderer, 61 | }; 62 | 63 | type Props = NavigationSceneRendererProps & { 64 | renderLeftComponent: NavigationSceneRenderer, 65 | renderRightComponent: NavigationSceneRenderer, 66 | renderTitleComponent: NavigationSceneRenderer, 67 | style?: any; 68 | viewProps?: any; 69 | }; 70 | 71 | type SubViewName = 'left' | 'title' | 'right'; 72 | 73 | const APPBAR_HEIGHT = Platform.OS === 'ios' ? 44 : 56; 74 | const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 20 : 0; 75 | const PropTypes = require('prop-types'); 76 | 77 | class NavigationHeader extends React.PureComponent { 78 | props: Props; 79 | 80 | static defaultProps = { 81 | 82 | renderTitleComponent: (props: NavigationSceneRendererProps) => { 83 | const {navigationState} = props; 84 | const title = String(navigationState.title || ''); 85 | return {title}; 86 | }, 87 | 88 | renderLeftComponent: (props: NavigationSceneRendererProps) => { 89 | return props.scene.index > 0 ? : null; 90 | }, 91 | 92 | renderRightComponent: (props: NavigationSceneRendererProps) => { 93 | return null; 94 | }, 95 | }; 96 | 97 | static propTypes = { 98 | ...NavigationPropTypes.SceneRendererProps, 99 | renderLeftComponent: PropTypes.func, 100 | renderRightComponent: PropTypes.func, 101 | renderTitleComponent: PropTypes.func, 102 | style: ViewPropTypes.style, 103 | viewProps: PropTypes.shape(ViewPropTypes), 104 | }; 105 | 106 | render(): ReactElement { 107 | const { scenes, style, viewProps } = this.props; 108 | 109 | const scenesProps = scenes.map(scene => { 110 | const props = NavigationPropTypes.extractSceneRendererProps(this.props); 111 | props.scene = scene; 112 | return props; 113 | }); 114 | 115 | return ( 116 | 117 | {scenesProps.map(this._renderLeft, this)} 118 | {scenesProps.map(this._renderTitle, this)} 119 | {scenesProps.map(this._renderRight, this)} 120 | 121 | ); 122 | } 123 | 124 | _renderLeft(props: NavigationSceneRendererProps): ?ReactElement { 125 | return this._renderSubView( 126 | props, 127 | 'left', 128 | this.props.renderLeftComponent, 129 | NavigationHeaderStyleInterpolator.forLeft, 130 | ); 131 | } 132 | 133 | _renderTitle(props: NavigationSceneRendererProps): ?ReactElement { 134 | return this._renderSubView( 135 | props, 136 | 'title', 137 | this.props.renderTitleComponent, 138 | NavigationHeaderStyleInterpolator.forCenter, 139 | ); 140 | } 141 | 142 | _renderRight(props: NavigationSceneRendererProps): ?ReactElement { 143 | return this._renderSubView( 144 | props, 145 | 'right', 146 | this.props.renderRightComponent, 147 | NavigationHeaderStyleInterpolator.forRight, 148 | ); 149 | } 150 | 151 | _renderSubView( 152 | props: NavigationSceneRendererProps, 153 | name: SubViewName, 154 | renderer: NavigationSceneRenderer, 155 | styleInterpolator: NavigationStyleInterpolator, 156 | ): ?ReactElement { 157 | const { 158 | scene, 159 | navigationState, 160 | } = props; 161 | 162 | const { 163 | index, 164 | isStale, 165 | key, 166 | } = scene; 167 | 168 | const offset = navigationState.index - index; 169 | 170 | if (Math.abs(offset) > 2) { 171 | // Scene is far away from the active scene. Hides it to avoid unnecessary 172 | // rendering. 173 | return null; 174 | } 175 | 176 | const subView = renderer(props); 177 | if (subView === null) { 178 | return null; 179 | } 180 | 181 | const pointerEvents = offset !== 0 || isStale ? 'none' : 'box-none'; 182 | return ( 183 | 190 | {subView} 191 | 192 | ); 193 | } 194 | } 195 | 196 | const styles = StyleSheet.create({ 197 | appbar: { 198 | alignItems: 'center', 199 | backgroundColor: Platform.OS === 'ios' ? '#EFEFF2' : '#FFF', 200 | borderBottomColor: 'rgba(0, 0, 0, .15)', 201 | borderBottomWidth: Platform.OS === 'ios' ? StyleSheet.hairlineWidth : 0, 202 | elevation: 2, 203 | flexDirection: 'row', 204 | height: APPBAR_HEIGHT + STATUSBAR_HEIGHT, 205 | justifyContent: 'flex-start', 206 | left: 0, 207 | marginBottom: 16, // This is needed for elevation shadow 208 | position: 'absolute', 209 | right: 0, 210 | top: 0, 211 | }, 212 | 213 | title: { 214 | bottom: 0, 215 | left: APPBAR_HEIGHT, 216 | marginTop: STATUSBAR_HEIGHT, 217 | position: 'absolute', 218 | right: APPBAR_HEIGHT, 219 | top: 0, 220 | }, 221 | 222 | left: { 223 | bottom: 0, 224 | left: 0, 225 | marginTop: STATUSBAR_HEIGHT, 226 | position: 'absolute', 227 | top: 0, 228 | }, 229 | 230 | right: { 231 | bottom: 0, 232 | marginTop: STATUSBAR_HEIGHT, 233 | position: 'absolute', 234 | right: 0, 235 | top: 0, 236 | }, 237 | }); 238 | 239 | const NavigationHeaderContainer = NavigationContainer.create(NavigationHeader); 240 | 241 | NavigationHeaderContainer.HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT; 242 | NavigationHeaderContainer.Title = NavigationHeaderTitle; 243 | NavigationHeaderContainer.BackButton = NavigationHeaderBackButton; 244 | 245 | module.exports = NavigationHeaderContainer; 246 | -------------------------------------------------------------------------------- /NavigationHeaderBackButton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The examples provided by Facebook are for non-commercial testing and 3 | * evaluation purposes only. 4 | * 5 | * Facebook reserves all rights not expressly granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 8 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL 10 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 11 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | * 14 | * @providesModule NavigationHeaderBackButton 15 | * @flow 16 | */ 17 | 'use strict'; 18 | 19 | const React = require('react'); 20 | const PropTypes = require('prop-types'); 21 | const ReactNative = require('react-native'); 22 | const NavigationContainer = require('./NavigationContainer'); 23 | const NavigationRootContainer = require('./NavigationRootContainer'); 24 | 25 | const { 26 | Image, 27 | Platform, 28 | StyleSheet, 29 | TouchableOpacity, 30 | } = ReactNative; 31 | 32 | type Props = { 33 | onNavigate: Function 34 | } 35 | 36 | const NavigationHeaderBackButton = (props: Props) => ( 37 | props.onNavigate(NavigationRootContainer.getBackAction())}> 38 | 39 | 40 | ); 41 | 42 | NavigationHeaderBackButton.propTypes = { 43 | onNavigate: PropTypes.func.isRequired 44 | }; 45 | 46 | const styles = StyleSheet.create({ 47 | buttonContainer: { 48 | flex: 1, 49 | flexDirection: 'row', 50 | alignItems: 'center', 51 | justifyContent: 'center', 52 | }, 53 | button: { 54 | height: 24, 55 | width: 24, 56 | margin: Platform.OS === 'ios' ? 10 : 16, 57 | resizeMode: 'contain' 58 | } 59 | }); 60 | 61 | module.exports = NavigationContainer.create(NavigationHeaderBackButton); 62 | -------------------------------------------------------------------------------- /NavigationHeaderStyleInterpolator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationHeaderStyleInterpolator 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | 36 | import type { 37 | NavigationSceneRendererProps, 38 | } from 'NavigationTypeDefinition'; 39 | 40 | /** 41 | * Utility that builds the style for the navigation header. 42 | * 43 | * +-------------+-------------+-------------+ 44 | * | | | | 45 | * | Left | Title | Right | 46 | * | Component | Component | Component | 47 | * | | | | 48 | * +-------------+-------------+-------------+ 49 | */ 50 | 51 | function forLeft(props: NavigationSceneRendererProps): Object { 52 | const {position, scene} = props; 53 | const {index} = scene; 54 | return { 55 | opacity: position.interpolate({ 56 | inputRange: [ index - 1, index, index + 1 ], 57 | outputRange: [ 0, 1, 0 ], 58 | }), 59 | }; 60 | } 61 | 62 | function forCenter(props: NavigationSceneRendererProps): Object { 63 | const {position, scene} = props; 64 | const {index} = scene; 65 | return { 66 | opacity:position.interpolate({ 67 | inputRange: [ index - 1, index, index + 1 ], 68 | outputRange: [ 0, 1, 0 ], 69 | }), 70 | transform: [ 71 | { 72 | translateX: position.interpolate({ 73 | inputRange: [ index - 1, index + 1 ], 74 | outputRange: [ 200, -200 ], 75 | }), 76 | } 77 | ], 78 | }; 79 | } 80 | 81 | function forRight(props: NavigationSceneRendererProps): Object { 82 | const {position, scene} = props; 83 | const {index} = scene; 84 | return { 85 | opacity: position.interpolate({ 86 | inputRange: [ index - 1, index, index + 1 ], 87 | outputRange: [ 0, 1, 0 ], 88 | }), 89 | }; 90 | } 91 | 92 | module.exports = { 93 | forCenter, 94 | forLeft, 95 | forRight, 96 | }; 97 | -------------------------------------------------------------------------------- /NavigationHeaderTitle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Facebook, Inc. All rights reserved. 3 | * 4 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 5 | * all intellectual property and other proprietary rights, in and to the React 6 | * Native CustomComponents software (the "Software"). Subject to your 7 | * compliance with these terms, you are hereby granted a non-exclusive, 8 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 9 | * and (2) reproduce and distribute the Software as part of your own software 10 | * ("Your Software"). Facebook reserves all rights not expressly granted to 11 | * you in this license agreement. 12 | * 13 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 14 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 16 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 17 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 23 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * @providesModule NavigationHeaderTitle 26 | * @flow 27 | */ 28 | 'use strict'; 29 | 30 | const React = require('react'); 31 | const PropTypes = require('prop-types'); 32 | const ReactNative = require('react-native'); 33 | 34 | const { 35 | Platform, 36 | StyleSheet, 37 | View, 38 | Text, 39 | ViewPropTypes, 40 | } = ReactNative; 41 | 42 | type Props = { 43 | children: ReactElement; 44 | style: any; 45 | textStyle: any; 46 | viewProps: any; 47 | } 48 | 49 | const NavigationHeaderTitle = ({ children, style, textStyle, viewProps }: Props) => ( 50 | 51 | {children} 52 | 53 | ); 54 | 55 | const styles = StyleSheet.create({ 56 | title: { 57 | flex: 1, 58 | flexDirection: 'row', 59 | alignItems: 'center', 60 | marginHorizontal: 16 61 | }, 62 | 63 | titleText: { 64 | flex: 1, 65 | fontSize: 18, 66 | fontWeight: '500', 67 | color: 'rgba(0, 0, 0, .9)', 68 | textAlign: Platform.OS === 'ios' ? 'center' : 'left' 69 | } 70 | }); 71 | 72 | NavigationHeaderTitle.propTypes = { 73 | children: PropTypes.string.isRequired, 74 | style: ViewPropTypes.style, 75 | textStyle: Text.propTypes.style 76 | }; 77 | 78 | module.exports = NavigationHeaderTitle; 79 | -------------------------------------------------------------------------------- /NavigationLegacyNavigator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationLegacyNavigator 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | const Animated = require('react-native').Animated; 36 | const NavigationAnimatedValueSubscription = require('./NavigationAnimatedValueSubscription'); 37 | const NavigationAnimatedView = require('./NavigationAnimatedView'); 38 | const NavigationCard = require('./NavigationCard'); 39 | const NavigationCardStackStyleInterpolator = require('./NavigationCardStackStyleInterpolator'); 40 | const NavigationContext = require('./NavigationContext'); 41 | const NavigationLegacyNavigatorRouteStack = require('./NavigationLegacyNavigatorRouteStack'); 42 | const NavigationCardStackPanResponder = require('./NavigationCardStackPanResponder'); 43 | const NavigationPagerPanResponder = require('./NavigationPagerPanResponder'); 44 | const NavigationPagerStyleInterpolator = require('./NavigationPagerStyleInterpolator'); 45 | const NavigatorBreadcrumbNavigationBar = require('./NavigatorBreadcrumbNavigationBar'); 46 | const NavigatorNavigationBar = require('./NavigatorNavigationBar'); 47 | const NavigatorSceneConfigs = require('./NavigatorSceneConfigs'); 48 | const React = require('react'); 49 | 50 | import type { 51 | NavigationActionCaller, 52 | NavigationAnimatedValue, 53 | NavigationAnimationSetter, 54 | NavigationParentState, 55 | NavigationSceneRenderer, 56 | NavigationSceneRendererProps, 57 | } from 'NavigationTypeDefinition'; 58 | 59 | type Props = { 60 | configureScene: any, 61 | initialRoute: any, 62 | initialRouteStack: any, 63 | navigationBar: any, 64 | navigationBarNavigator: any, 65 | navigator: any, 66 | onDidFocus: any, 67 | onWillFocus: any, 68 | renderScene: any, 69 | renderScene: any, 70 | style: any, 71 | }; 72 | 73 | type State = { 74 | presentedIndex: number, 75 | routeStack: Array, 76 | }; 77 | 78 | const RouteStack = NavigationLegacyNavigatorRouteStack; 79 | 80 | /** 81 | * NavigationLegacyNavigator is meant to replace Navigator seemlessly with 82 | * minimum API changes. 83 | * 84 | * While the APIs remain compatible with Navigator, it is built with good 85 | * intention by using the new Navigation API such as 86 | * `NavigationAnimatedView`...etc. 87 | */ 88 | class NavigationLegacyNavigator extends React.PureComponent { 89 | static BreadcrumbNavigationBar: any; 90 | static NavigationBar: any; 91 | static SceneConfigs: any; 92 | 93 | _applyAnimation: NavigationAnimationSetter; 94 | _navigationBarRef: any; 95 | _onNavigationBarRef: (ref: any) => void; 96 | _onPositionChange: (data: {value: number}) => void; 97 | _positionListener: ?NavigationAnimatedValueSubscription; 98 | _previousStack: NavigationLegacyNavigatorRouteStack; 99 | _renderCard: NavigationSceneRenderer; 100 | _renderHeader: NavigationSceneRenderer; 101 | _renderScene: NavigationSceneRenderer; 102 | _routeFocused: any; 103 | _routeToFocus: any; 104 | _onNavigate: NavigationActionCaller; 105 | _stack: NavigationLegacyNavigatorRouteStack; 106 | _useAnimation: boolean; 107 | 108 | navigationContext: NavigationContext; 109 | parentNavigator: any; 110 | props: Props; 111 | state: State; 112 | 113 | constructor(props: Props, context: any) { 114 | super(props, context); 115 | 116 | const stack = this._getInitialRouteStack(); 117 | 118 | // Unfortunately, due to historical reasons, the `state` has been exposed 119 | // as public members of the navigator, therefore we'd keep private state 120 | // as private members. 121 | this._previousStack = stack; 122 | this._stack = stack; 123 | this._useAnimation = false; 124 | 125 | // Legacy members portred from `Navigator`. 126 | this.parentNavigator = props.navigator; 127 | this.navigationContext = new NavigationContext(); 128 | 129 | this.state = { 130 | routeStack: stack.toArray(), 131 | presentedIndex: stack.index, 132 | }; 133 | } 134 | 135 | jumpTo(route: any): void { 136 | this._applyStack(this._stack.jumpTo(route)); 137 | } 138 | 139 | jumpForward(): void { 140 | this._applyStack(this._stack.jumpForward()); 141 | } 142 | 143 | jumpBack(): void { 144 | this._applyStack(this._stack.jumpBack()); 145 | } 146 | 147 | push(route: any): void { 148 | this._applyStack(this._stack.push(route)); 149 | } 150 | 151 | pop(): void { 152 | this._applyStack(this._stack.pop()); 153 | } 154 | 155 | replaceAtIndex(route: any, index: number): void { 156 | this._applyStack(this._stack.replaceAtIndex(index, route)); 157 | } 158 | 159 | replace(route: any): void { 160 | this.replaceAtIndex(route, this._stack.index); 161 | } 162 | 163 | replacePrevious(route: any): void { 164 | this.replaceAtIndex(route, this._stack.index - 1); 165 | } 166 | 167 | popToTop(): void { 168 | this._applyStack(this._stack.slice(0, 1)); 169 | } 170 | 171 | popToRoute(route: any): void { 172 | this._applyStack(this._stack.popToRoute(route)); 173 | } 174 | 175 | replacePreviousAndPop(route: any): void { 176 | this._applyStack(this._stack.replacePreviousAndPop(route)); 177 | } 178 | 179 | resetTo(route: any): void { 180 | this._applyStack(this._stack.resetTo(route)); 181 | } 182 | 183 | immediatelyResetRouteStack(routes: Array): void { 184 | this._applyStack(this._stack.resetRoutes(routes), true); 185 | } 186 | 187 | getCurrentRoutes(): Array { 188 | return this._stack.toArray(); 189 | } 190 | 191 | // Life cycle and private methods below. 192 | 193 | componentWillMount(): void { 194 | this._applyAnimation = this._applyAnimation.bind(this); 195 | this._onNavigate = this._onNavigate.bind(this); 196 | this._onNavigationBarRef = this._onNavigationBarRef.bind(this); 197 | this._onPositionChange = this._onPositionChange.bind(this); 198 | this._renderCard = this._renderCard.bind(this); 199 | this._renderHeader = this._renderHeader.bind(this); 200 | this._renderScene = this._renderScene.bind(this); 201 | 202 | this._willFocus(); 203 | } 204 | 205 | componentDidMount(): void { 206 | this._didFocus(); 207 | } 208 | 209 | componentWillUnmount(): void { 210 | this._positionListener && this._positionListener.remove(); 211 | } 212 | 213 | componentWillUpdate(nextProps: Props, nextState: State): void { 214 | this._willFocus(); 215 | } 216 | 217 | componentDidUpdate(prevProps: Props, prevState: State): void { 218 | if (this._useAnimation) { 219 | // will play animation. 220 | return; 221 | } 222 | this._didFocus(); 223 | } 224 | 225 | render(): ReactElement { 226 | return ( 227 | 235 | ); 236 | } 237 | 238 | _getInitialRouteStack(): RouteStack { 239 | const {initialRouteStack, initialRoute} = this.props; 240 | const routes = initialRouteStack || [initialRoute]; 241 | const index = initialRoute ? 242 | routes.indexOf(initialRoute) : 243 | routes.length - 1; 244 | return new RouteStack(index, routes); 245 | } 246 | 247 | _renderHeader(props: NavigationSceneRendererProps): ?ReactElement { 248 | // `_renderHeader` is always called before `_renderCard`. We should 249 | // subscribe to the position here. 250 | this._positionListener && this._positionListener.remove(); 251 | this._positionListener = new NavigationAnimatedValueSubscription( 252 | props.position, 253 | this._onPositionChange, 254 | ); 255 | 256 | const {navigationBar, navigationBarNavigator} = this.props; 257 | if (!navigationBar) { 258 | return null; 259 | } 260 | 261 | return React.cloneElement( 262 | navigationBar, 263 | { 264 | key: 'header_' + props.scene.key, 265 | ref: this._onNavigationBarRef, 266 | navigator: navigationBarNavigator || this, 267 | navState: {...this.state}, 268 | } 269 | ); 270 | } 271 | 272 | _renderCard(props: NavigationSceneRendererProps): ReactElement { 273 | const {scene} = props; 274 | const {configureScene} = this.props; 275 | 276 | // Default getters for style and pan responders. 277 | let styleGetter = NavigationCardStackStyleInterpolator.forHorizontal; 278 | let panResponderGetter = NavigationCardStackPanResponder.forHorizontal; 279 | 280 | if (configureScene) { 281 | const route = RouteStack.getRouteByNavigationState(scene.navigationState); 282 | const config = configureScene(route, this.state.routeStack); 283 | 284 | if (config) { 285 | const gestures = config.gestures || {}; 286 | if (gestures.pop && gestures.pop.direction === 'left-to-right') { 287 | // pass, will use default getters. 288 | } else if (gestures.pop && gestures.pop.direction === 'top-to-bottom') { 289 | styleGetter = NavigationCardStackStyleInterpolator.forVertical; 290 | panResponderGetter = NavigationCardStackPanResponder.forVertical; 291 | } else if ( 292 | gestures.jumpBack && 293 | gestures.jumpForward && 294 | gestures.jumpBack.direction === 'left-to-right' && 295 | gestures.jumpForward.direction === 'right-to-left' 296 | ) { 297 | styleGetter = NavigationPagerStyleInterpolator.forHorizontal; 298 | panResponderGetter = NavigationPagerPanResponder.forHorizontal; 299 | } else if (__DEV__) { 300 | console.warn('unsupported scene configuration', config); 301 | } 302 | } 303 | } 304 | 305 | const style = styleGetter(props); 306 | const panHandlers = panResponderGetter(props); 307 | 308 | return ( 309 | 316 | ); 317 | } 318 | 319 | _renderScene(props: NavigationSceneRendererProps): ReactElement { 320 | const {navigationState} = props.scene; 321 | const route = RouteStack.getRouteByNavigationState(navigationState); 322 | return this.props.renderScene(route, this); 323 | } 324 | 325 | _applyStack( 326 | stack: NavigationLegacyNavigatorRouteStack, 327 | noAnimation: ?boolean, 328 | ): void { 329 | if (stack !== this._stack) { 330 | this._previousStack = this._stack; 331 | this._stack = stack; 332 | 333 | this._useAnimation = noAnimation || 334 | this._previousStack.index !== stack.index; 335 | 336 | this.setState({ 337 | presentedIndex: stack.index, 338 | routeStack: stack.toArray(), 339 | }); 340 | } 341 | } 342 | 343 | _onNavigationBarRef(navigationBarRef: any): void { 344 | this._navigationBarRef = navigationBarRef; 345 | const {navigationBar} = this.props; 346 | if (navigationBar && typeof navigationBar.ref === 'function') { 347 | navigationBar.ref(navigationBarRef); 348 | } 349 | } 350 | 351 | _onPositionChange(data: {value: number}): void { 352 | const fromIndex = this._previousStack.index; 353 | const toIndex = this._stack.index; 354 | 355 | if ( 356 | fromIndex !== toIndex && 357 | this._navigationBarRef && 358 | typeof this._navigationBarRef.updateProgress === 'function' 359 | ) { 360 | const progress = (data.value - fromIndex) / (toIndex - fromIndex); 361 | this._navigationBarRef.updateProgress(progress, fromIndex, toIndex); 362 | } 363 | 364 | const diff = this._stack.index - data.value; 365 | // When animation stops, the `diff` can still be very a small non-zero value 366 | // (e.g. 0.00000002). Call `willFocus` when `diff` is small enough. 367 | if (diff < 0.05) { 368 | this._didFocus(); 369 | } 370 | } 371 | 372 | _applyAnimation( 373 | position: NavigationAnimatedValue, 374 | nextState: NavigationParentState, 375 | prevState: NavigationParentState, 376 | ): void { 377 | const {index} = nextState; 378 | 379 | if (!this._useAnimation) { 380 | position.setValue(index); 381 | return; 382 | } 383 | 384 | Animated.timing( 385 | position, 386 | { 387 | duration: 500, 388 | toValue: index, 389 | } 390 | ).start(); 391 | } 392 | 393 | _willFocus(): void { 394 | const route = this._stack.get(this._stack.index); 395 | if (this._routeToFocus === route) { 396 | return; 397 | } 398 | this._routeToFocus = route; 399 | this.navigationContext.emit('willfocus', {route: route}); 400 | this.props.onWillFocus && this.props.onWillFocus(route); 401 | } 402 | 403 | _didFocus(): void { 404 | const route = this._stack.get(this._stack.index); 405 | if (this._routeFocused === route) { 406 | return; 407 | } 408 | this._routeFocused = route; 409 | this.navigationContext.emit('didfocus', {route: route}); 410 | this.props.onDidFocus && this.props.onDidFocus(route); 411 | } 412 | 413 | _onNavigate(action: any): void { 414 | switch (action) { 415 | case NavigationCardStackPanResponder.Actions.BACK: 416 | this.pop(); 417 | break; 418 | case NavigationPagerPanResponder.Actions.JUMP_BACK: 419 | this.jumpBack(); 420 | break; 421 | case NavigationPagerPanResponder.Actions.JUMP_FORWARD: 422 | this.jumpForward(); 423 | break; 424 | default: 425 | if (__DEV__) { 426 | console.warn('unsupported gesture action', action); 427 | } 428 | } 429 | } 430 | } 431 | 432 | // Legacy static members. 433 | NavigationLegacyNavigator.BreadcrumbNavigationBar = NavigatorBreadcrumbNavigationBar; 434 | NavigationLegacyNavigator.NavigationBar = NavigatorNavigationBar; 435 | NavigationLegacyNavigator.SceneConfigs = NavigatorSceneConfigs; 436 | 437 | module.exports = NavigationLegacyNavigator; 438 | -------------------------------------------------------------------------------- /NavigationLegacyNavigatorRouteStack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationLegacyNavigatorRouteStack 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const invariant = require('fbjs/lib/invariant'); 15 | 16 | import type { 17 | NavigationState, 18 | NavigationParentState, 19 | } from 'NavigationTypeDefinition'; 20 | 21 | type IterationCallback = (route: any, index: number, key: string) => void; 22 | 23 | function isRouteEmpty(route: any): boolean { 24 | return (route === undefined || route === null || route === '') || false; 25 | } 26 | 27 | function areRouteNodesEqual( 28 | one: Array, 29 | two: Array, 30 | ): boolean { 31 | if (one === two) { 32 | return true; 33 | } 34 | 35 | if (one.length !== two.length) { 36 | return false; 37 | } 38 | for (let ii = 0, jj = one.length; ii < jj; ii++) { 39 | if (one[ii] !== two[ii]) { 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | let _nextRouteNodeID = 0; 47 | 48 | /** 49 | * Private struct class that holds the key for a route. 50 | */ 51 | class RouteNode { 52 | key: string; 53 | route: any; 54 | 55 | /** 56 | * Cast `navigationState` as `RouteNode`. 57 | * Also see `RouteNode#toNavigationState`. 58 | */ 59 | static fromNavigationState(navigationState: NavigationState): RouteNode { 60 | invariant( 61 | navigationState instanceof RouteNode, 62 | 'navigationState should be an instacne of RouteNode' 63 | ); 64 | return navigationState; 65 | } 66 | 67 | constructor(route: any) { 68 | // Key value gets bigger incrementally. Developer can compare the 69 | // keys of two routes then know which route is added to the stack 70 | // earlier. 71 | const key = String(_nextRouteNodeID++); 72 | if (__DEV__ ) { 73 | // Ensure the immutability of the node. 74 | Object.defineProperty(this, 'key' , { 75 | enumerable: true, 76 | configurable: false, 77 | writable: false, 78 | value: key, 79 | }); 80 | Object.defineProperty(this, 'route' , { 81 | enumerable: true, 82 | configurable: false, 83 | writable: false, 84 | value: route, 85 | }); 86 | } else { 87 | this.key = key; 88 | this.route = route; 89 | } 90 | } 91 | 92 | toNavigationState(): NavigationState { 93 | return this; 94 | } 95 | } 96 | 97 | let _nextRouteStackID = 0; 98 | 99 | /** 100 | * The data structure that holds a list of routes and the focused index 101 | * of the routes. This data structure is implemented as immutable data 102 | * and mutation (e.g. push, pop...etc) will yields a new instance. 103 | */ 104 | class RouteStack { 105 | _index: number; 106 | _key: string; 107 | _routeNodes: Array; 108 | 109 | static getRouteByNavigationState(navigationState: NavigationState): any { 110 | return RouteNode.fromNavigationState(navigationState).route; 111 | } 112 | 113 | constructor(index: number, routes: Array) { 114 | invariant( 115 | routes.length > 0, 116 | 'routes must not be an empty array' 117 | ); 118 | 119 | invariant( 120 | index > -1 && index <= routes.length - 1, 121 | 'RouteStack: index out of bound' 122 | ); 123 | 124 | 125 | let routeNodes; 126 | if (routes[0] instanceof RouteNode) { 127 | // The array is already an array of . 128 | routeNodes = routes; 129 | } else { 130 | // Wrap the route with . 131 | routeNodes = routes.map((route) => { 132 | invariant(!isRouteEmpty(route), 'route must not be mepty'); 133 | return new RouteNode(route); 134 | }); 135 | } 136 | 137 | this._routeNodes = routeNodes; 138 | this._index = index; 139 | this._key = String(_nextRouteStackID++); 140 | } 141 | 142 | /* $FlowFixMe - get/set properties not yet supported */ 143 | get size(): number { 144 | return this._routeNodes.length; 145 | } 146 | 147 | /* $FlowFixMe - get/set properties not yet supported */ 148 | get index(): number { 149 | return this._index; 150 | } 151 | 152 | // Export as... 153 | toArray(): Array { 154 | return this._routeNodes.map(node => node.route); 155 | } 156 | 157 | toNavigationState(): NavigationParentState { 158 | return { 159 | index: this._index, 160 | key: this._key, 161 | children: this._routeNodes.map(node => node.toNavigationState()), 162 | }; 163 | } 164 | 165 | get(index: number): any { 166 | if (index < 0 || index > this._routeNodes.length - 1) { 167 | return null; 168 | } 169 | return this._routeNodes[index].route; 170 | } 171 | 172 | /** 173 | * Returns the key associated with the route. 174 | * When a route is added to a stack, the stack creates a key for this route. 175 | * The key will persist until the initial stack and its derived stack 176 | * no longer contains this route. 177 | */ 178 | keyOf(route: any): ?string { 179 | if (isRouteEmpty(route)) { 180 | return null; 181 | } 182 | const index = this.indexOf(route); 183 | return index > -1 ? 184 | this._routeNodes[index].key : 185 | null; 186 | } 187 | 188 | indexOf(route: any): number { 189 | if (isRouteEmpty(route)) { 190 | return -1; 191 | } 192 | 193 | for (let ii = 0, jj = this._routeNodes.length; ii < jj; ii++) { 194 | const node = this._routeNodes[ii]; 195 | if (node.route === route) { 196 | return ii; 197 | } 198 | } 199 | 200 | return -1; 201 | } 202 | 203 | slice(begin: ?number, end: ?number): RouteStack { 204 | // check `begin` and `end` first to keep @flow happy. 205 | const routeNodes = (end === undefined || end === null) ? 206 | this._routeNodes.slice(begin || 0) : 207 | this._routeNodes.slice(begin || 0, end || 0); 208 | 209 | const index = Math.min(this._index, routeNodes.length - 1); 210 | return this._update(index, routeNodes); 211 | } 212 | 213 | /** 214 | * Returns a new stack with the provided route appended, 215 | * starting at this stack size. 216 | */ 217 | push(route: any): RouteStack { 218 | 219 | invariant( 220 | !isRouteEmpty(route), 221 | 'Must supply route to push' 222 | ); 223 | 224 | invariant(this.indexOf(route) === -1, 'route must be unique'); 225 | 226 | // When pushing, removes the rest of the routes past the current index. 227 | const routeNodes = this._routeNodes.slice(0, this._index + 1); 228 | routeNodes.push(new RouteNode(route)); 229 | return this._update(routeNodes.length - 1, routeNodes); 230 | } 231 | 232 | /** 233 | * Returns a new stack a size ones less than this stack, 234 | * excluding the last index in this stack. 235 | */ 236 | pop(): RouteStack { 237 | if (this._routeNodes.length <= 1) { 238 | return this; 239 | } 240 | 241 | // When popping, removes the rest of the routes past the current index. 242 | const routeNodes = this._routeNodes.slice(0, this._index); 243 | return this._update(routeNodes.length - 1, routeNodes); 244 | } 245 | 246 | popToRoute(route: any): RouteStack { 247 | const index = this.indexOf(route); 248 | invariant( 249 | index > -1, 250 | 'Calling popToRoute for a route that doesn\'t exist!' 251 | ); 252 | return this.slice(0, index + 1); 253 | } 254 | 255 | jumpTo(route: any): RouteStack { 256 | const index = this.indexOf(route); 257 | return this.jumpToIndex(index); 258 | } 259 | 260 | jumpToIndex(index: number): RouteStack { 261 | invariant( 262 | index > -1 && index < this._routeNodes.length, 263 | 'jumpToIndex: index out of bound' 264 | ); 265 | 266 | return this._update(index, this._routeNodes); 267 | } 268 | 269 | jumpForward(): RouteStack { 270 | const index = this._index + 1; 271 | if (index >= this._routeNodes.length) { 272 | return this; 273 | } 274 | return this._update(index, this._routeNodes); 275 | } 276 | 277 | jumpBack(): RouteStack { 278 | const index = this._index - 1; 279 | if (index < 0) { 280 | return this; 281 | } 282 | return this._update(index, this._routeNodes); 283 | } 284 | 285 | /** 286 | * Replace a route in the navigation stack. 287 | * 288 | * `index` specifies the route in the stack that should be replaced. 289 | * If it's negative, it counts from the back. 290 | */ 291 | replaceAtIndex(index: number, route: any): RouteStack { 292 | invariant( 293 | !isRouteEmpty(route), 294 | 'Must supply route to replace' 295 | ); 296 | 297 | if (this.get(index) === route) { 298 | return this; 299 | } 300 | 301 | invariant(this.indexOf(route) === -1, 'route must be unique'); 302 | 303 | const size = this._routeNodes.length; 304 | if (index < 0) { 305 | index += size; 306 | } 307 | 308 | if (index < 0 || index >= size) { 309 | return this; 310 | } 311 | 312 | const routeNodes = this._routeNodes.slice(0); 313 | routeNodes[index] = new RouteNode(route); 314 | return this._update(index, routeNodes); 315 | } 316 | 317 | replacePreviousAndPop(route: any): RouteStack { 318 | if (this._index < 1) { 319 | // stack is too small. 320 | return this; 321 | } 322 | 323 | const index = this.indexOf(route); 324 | invariant( 325 | index === -1 || index === this._index - 1, 326 | 'route already exists in the stack' 327 | ); 328 | 329 | return this.replaceAtIndex(this._index - 1, route).popToRoute(route); 330 | } 331 | 332 | // Reset 333 | 334 | /** 335 | * Replace the current active route with a new route, and pops out 336 | * the rest routes after it. 337 | */ 338 | resetTo(route: any): RouteStack { 339 | invariant(!isRouteEmpty(route), 'Must supply route'); 340 | const index = this.indexOf(route); 341 | if (index === this._index) { 342 | // Already has this active route. 343 | return this; 344 | } 345 | invariant(index === -1, 'route already exists in the stack'); 346 | const routeNodes = this._routeNodes.slice(0, this._index); 347 | routeNodes.push(new RouteNode(route)); 348 | return this._update(routeNodes.length - 1, routeNodes); 349 | } 350 | 351 | resetRoutes(routes: Array): RouteStack { 352 | const index = routes.length - 1; 353 | return new RouteStack(index, routes); 354 | } 355 | 356 | // Iterations 357 | forEach(callback: IterationCallback, context: ?Object): void { 358 | this._routeNodes.forEach((node, index) => { 359 | callback.call(context, node.route, index, node.key); 360 | }); 361 | } 362 | 363 | mapToArray(callback: IterationCallback, context: ?Object): Array { 364 | return this._routeNodes.map((node, index) => { 365 | return callback.call(context, node.route, index, node.key); 366 | }); 367 | } 368 | 369 | _update(index: number, routeNodes: Array): RouteStack { 370 | if ( 371 | this._index === index && 372 | areRouteNodesEqual(this._routeNodes, routeNodes) 373 | ) { 374 | return this; 375 | } 376 | 377 | return new RouteStack(index, routeNodes); 378 | } 379 | } 380 | 381 | module.exports = RouteStack; 382 | -------------------------------------------------------------------------------- /NavigationPagerPanResponder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationPagerPanResponder 10 | * @flow 11 | * @typechecks 12 | */ 13 | 'use strict'; 14 | 15 | const Animated = require('react-native').Animated; 16 | const NavigationAbstractPanResponder = require('./NavigationAbstractPanResponder'); 17 | const NavigationCardStackPanResponder = require('./NavigationCardStackPanResponder'); 18 | 19 | const clamp = require('clamp'); 20 | 21 | import type { 22 | NavigationPanPanHandlers, 23 | NavigationSceneRendererProps, 24 | } from 'NavigationTypeDefinition'; 25 | 26 | import type { 27 | NavigationGestureDirection, 28 | } from 'NavigationCardStackPanResponder'; 29 | 30 | 31 | 32 | /** 33 | * Primitive gesture directions. 34 | */ 35 | const { 36 | ANIMATION_DURATION, 37 | DISTANCE_THRESHOLD, 38 | POSITION_THRESHOLD, 39 | RESPOND_THRESHOLD, 40 | Directions, 41 | } = NavigationCardStackPanResponder; 42 | 43 | /** 44 | * Primitive gesture actions. 45 | */ 46 | const Actions = { 47 | JUMP_BACK: {type: 'jump_back'}, 48 | JUMP_FORWARD: {type: 'jump_forward'}, 49 | }; 50 | 51 | /** 52 | * Pan responder that handles gesture for a card in the cards list. 53 | * 54 | * +-------------+-------------+-------------+ 55 | * | | | | 56 | * | | | | 57 | * | | | | 58 | * | Next | Focused | Previous | 59 | * | Card | Card | Card | 60 | * | | | | 61 | * | | | | 62 | * | | | | 63 | * +-------------+-------------+-------------+ 64 | */ 65 | class NavigationPagerPanResponder extends NavigationAbstractPanResponder { 66 | 67 | _isResponding: boolean; 68 | _isVertical: boolean; 69 | _props: NavigationSceneRendererProps; 70 | _startValue: number; 71 | 72 | constructor( 73 | direction: NavigationGestureDirection, 74 | props: NavigationSceneRendererProps, 75 | ) { 76 | super(); 77 | this._isResponding = false; 78 | this._isVertical = direction === Directions.VERTICAL; 79 | this._props = props; 80 | this._startValue = 0; 81 | } 82 | 83 | onMoveShouldSetPanResponder(event: any, gesture: any): boolean { 84 | const props = this._props; 85 | 86 | if (props.navigationState.index !== props.scene.index) { 87 | return false; 88 | } 89 | 90 | const layout = props.layout; 91 | const isVertical = this._isVertical; 92 | const axis = isVertical ? 'dy' : 'dx'; 93 | const index = props.navigationState.index; 94 | const distance = isVertical ? 95 | layout.height.__getValue() : 96 | layout.width.__getValue(); 97 | 98 | return ( 99 | Math.abs(gesture[axis]) > RESPOND_THRESHOLD && 100 | distance > 0 && 101 | index > 0 102 | ); 103 | } 104 | 105 | onPanResponderGrant(): void { 106 | this._isResponding = false; 107 | this._props.position.stopAnimation((value: number) => { 108 | this._isResponding = true; 109 | this._startValue = value; 110 | }); 111 | } 112 | 113 | onPanResponderMove(event: any, gesture: any): void { 114 | if (!this._isResponding) { 115 | return; 116 | } 117 | 118 | const { 119 | layout, 120 | navigationState, 121 | position, 122 | scenes, 123 | } = this._props; 124 | 125 | const isVertical = this._isVertical; 126 | const axis = isVertical ? 'dy' : 'dx'; 127 | const index = navigationState.index; 128 | const distance = isVertical ? 129 | layout.height.__getValue() : 130 | layout.width.__getValue(); 131 | 132 | const prevIndex = Math.max( 133 | 0, 134 | index - 1, 135 | ); 136 | 137 | const nextIndex = Math.min( 138 | index + 1, 139 | scenes.length - 1, 140 | ); 141 | 142 | const value = clamp( 143 | prevIndex, 144 | this._startValue - (gesture[axis] / distance), 145 | nextIndex, 146 | ); 147 | 148 | position.setValue(value); 149 | } 150 | 151 | onPanResponderRelease(event: any, gesture: any): void { 152 | if (!this._isResponding) { 153 | return; 154 | } 155 | 156 | this._isResponding = false; 157 | 158 | const { 159 | navigationState, 160 | onNavigate, 161 | position, 162 | } = this._props; 163 | 164 | const isVertical = this._isVertical; 165 | const axis = isVertical ? 'dy' : 'dx'; 166 | const index = navigationState.index; 167 | const distance = gesture[axis]; 168 | 169 | position.stopAnimation((value: number) => { 170 | this._reset(); 171 | if ( 172 | distance > DISTANCE_THRESHOLD || 173 | value <= index - POSITION_THRESHOLD 174 | ) { 175 | onNavigate(Actions.JUMP_BACK); 176 | return; 177 | } 178 | 179 | if ( 180 | distance < -DISTANCE_THRESHOLD || 181 | value >= index + POSITION_THRESHOLD 182 | ) { 183 | onNavigate(Actions.JUMP_FORWARD); 184 | } 185 | }); 186 | } 187 | 188 | onPanResponderTerminate(): void { 189 | this._isResponding = false; 190 | this._reset(); 191 | } 192 | 193 | _reset(): void { 194 | const props = this._props; 195 | Animated.timing( 196 | props.position, 197 | { 198 | toValue: props.navigationState.index, 199 | duration: ANIMATION_DURATION, 200 | } 201 | ).start(); 202 | } 203 | } 204 | 205 | function createPanHandlers( 206 | direction: NavigationGestureDirection, 207 | props: NavigationSceneRendererProps, 208 | ): NavigationPanPanHandlers { 209 | const responder = new NavigationPagerPanResponder(direction, props); 210 | return responder.panHandlers; 211 | } 212 | 213 | function forHorizontal( 214 | props: NavigationSceneRendererProps, 215 | ): NavigationPanPanHandlers { 216 | return createPanHandlers(Directions.HORIZONTAL, props); 217 | } 218 | 219 | module.exports = { 220 | Actions, 221 | forHorizontal, 222 | }; 223 | -------------------------------------------------------------------------------- /NavigationPagerStyleInterpolator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationPagerStyleInterpolator 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | import type { 36 | NavigationSceneRendererProps, 37 | } from 'NavigationTypeDefinition'; 38 | 39 | /** 40 | * Utility that builds the style for the card in the cards list. 41 | * 42 | * +-------------+-------------+-------------+ 43 | * | | | | 44 | * | | | | 45 | * | | | | 46 | * | Next | Focused | Previous | 47 | * | Card | Card | Card | 48 | * | | | | 49 | * | | | | 50 | * | | | | 51 | * +-------------+-------------+-------------+ 52 | */ 53 | 54 | /** 55 | * Render the initial style when the initial layout isn't measured yet. 56 | */ 57 | function forInitial(props: NavigationSceneRendererProps): Object { 58 | const { 59 | navigationState, 60 | scene, 61 | } = props; 62 | 63 | const focused = navigationState.index === scene.index; 64 | const opacity = focused ? 1 : 0; 65 | // If not focused, move the scene to the far away. 66 | const dir = scene.index > navigationState.index ? 1 : -1; 67 | const translate = focused ? 0 : (1000000 * dir); 68 | return { 69 | opacity, 70 | transform: [ 71 | { translateX: translate }, 72 | { translateY: translate }, 73 | ], 74 | }; 75 | } 76 | 77 | function forHorizontal(props: NavigationSceneRendererProps): Object { 78 | const { 79 | layout, 80 | position, 81 | scene, 82 | } = props; 83 | 84 | if (!layout.isMeasured) { 85 | return forInitial(props); 86 | } 87 | 88 | const index = scene.index; 89 | const inputRange = [index - 1, index, index + 1]; 90 | 91 | const width = layout.initWidth; 92 | const translateX = position.interpolate({ 93 | inputRange, 94 | outputRange: [width, 0, -width], 95 | }); 96 | 97 | return { 98 | opacity : 1, 99 | shadowColor: 'transparent', 100 | shadowRadius: 0, 101 | transform: [ 102 | { scale: 1 }, 103 | { translateX }, 104 | { translateY: 0 }, 105 | ], 106 | }; 107 | } 108 | 109 | module.exports = { 110 | forHorizontal, 111 | }; 112 | -------------------------------------------------------------------------------- /NavigationPointerEventsContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * Facebook, Inc. ("Facebook") owns all right, title and interest, including 10 | * all intellectual property and other proprietary rights, in and to the React 11 | * Native CustomComponents software (the "Software"). Subject to your 12 | * compliance with these terms, you are hereby granted a non-exclusive, 13 | * worldwide, royalty-free copyright license to (1) use and copy the Software; 14 | * and (2) reproduce and distribute the Software as part of your own software 15 | * ("Your Software"). Facebook reserves all rights not expressly granted to 16 | * you in this license agreement. 17 | * 18 | * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS 19 | * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. 21 | * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR 22 | * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF 28 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | * @providesModule NavigationPointerEventsContainer 31 | * @flow 32 | */ 33 | 'use strict'; 34 | 35 | const React = require('react'); 36 | const NavigationAnimatedValueSubscription = require('./NavigationAnimatedValueSubscription'); 37 | 38 | const invariant = require('fbjs/lib/invariant'); 39 | 40 | import type { 41 | NavigationSceneRendererProps, 42 | } from 'NavigationTypeDefinition'; 43 | 44 | type Props = NavigationSceneRendererProps; 45 | 46 | const MIN_POSITION_OFFSET = 0.01; 47 | 48 | /** 49 | * Create a higher-order component that automatically computes the 50 | * `pointerEvents` property for a component whenever navigation position 51 | * changes. 52 | */ 53 | function create( 54 | Component: ReactClass, 55 | ): ReactClass { 56 | 57 | class Container extends React.Component { 58 | 59 | _component: any; 60 | _onComponentRef: (view: any) => void; 61 | _onPositionChange: (data: {value: number}) => void; 62 | _pointerEvents: string; 63 | _positionListener: ?NavigationAnimatedValueSubscription; 64 | 65 | props: Props; 66 | 67 | constructor(props: Props, context: any) { 68 | super(props, context); 69 | this._pointerEvents = this._computePointerEvents(); 70 | } 71 | 72 | componentWillMount(): void { 73 | this._onPositionChange = this._onPositionChange.bind(this); 74 | this._onComponentRef = this._onComponentRef.bind(this); 75 | } 76 | 77 | componentDidMount(): void { 78 | this._bindPosition(this.props); 79 | } 80 | 81 | componentWillUnmount(): void { 82 | this._positionListener && this._positionListener.remove(); 83 | } 84 | 85 | componentWillReceiveProps(nextProps: Props): void { 86 | this._bindPosition(nextProps); 87 | } 88 | 89 | render(): ReactElement { 90 | this._pointerEvents = this._computePointerEvents(); 91 | return ( 92 | 97 | ); 98 | } 99 | 100 | _onComponentRef(component: any): void { 101 | this._component = component; 102 | if (component) { 103 | invariant( 104 | typeof component.setNativeProps === 'function', 105 | 'component must implement method `setNativeProps`', 106 | ); 107 | } 108 | } 109 | 110 | _bindPosition(props: NavigationSceneRendererProps): void { 111 | this._positionListener && this._positionListener.remove(); 112 | this._positionListener = new NavigationAnimatedValueSubscription( 113 | props.position, 114 | this._onPositionChange, 115 | ); 116 | } 117 | 118 | _onPositionChange(): void { 119 | if (this._component) { 120 | const pointerEvents = this._computePointerEvents(); 121 | if (this._pointerEvents !== pointerEvents) { 122 | this._pointerEvents = pointerEvents; 123 | this._component.setNativeProps({pointerEvents}); 124 | } 125 | } 126 | } 127 | 128 | _computePointerEvents(): string { 129 | const { 130 | navigationState, 131 | position, 132 | scene, 133 | } = this.props; 134 | 135 | if (scene.isStale || navigationState.index !== scene.index) { 136 | // The scene isn't focused. 137 | return scene.index > navigationState.index ? 138 | 'box-only' : 139 | 'none'; 140 | } 141 | 142 | const offset = position.__getAnimatedValue() - navigationState.index; 143 | if (Math.abs(offset) > MIN_POSITION_OFFSET) { 144 | // The positon is still away from scene's index. 145 | // Scene's children should not receive touches until the position 146 | // is close enough to scene's index. 147 | return 'box-only'; 148 | } 149 | 150 | return 'auto'; 151 | } 152 | } 153 | return Container; 154 | } 155 | 156 | module.exports = { 157 | create, 158 | }; 159 | -------------------------------------------------------------------------------- /NavigationPropTypes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationPropTypes 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | import type { 15 | NavigationSceneRendererProps, 16 | } from 'NavigationTypeDefinition'; 17 | 18 | /** 19 | * React component PropTypes Definitions. Consider using this as a supplementary 20 | * measure with `NavigationTypeDefinition`. This helps to capture the propType 21 | * error at run-time, where as `NavigationTypeDefinition` capture the flow 22 | * type check errors at build time. 23 | */ 24 | 25 | const Animated = require('react-native').Animated; 26 | const React = require('react'); 27 | 28 | const PropTypes = require('prop-types'); 29 | 30 | /* NavigationAction */ 31 | const action = PropTypes.shape({ 32 | type: PropTypes.string.isRequired, 33 | }); 34 | 35 | /* NavigationAnimatedValue */ 36 | const animatedValue = PropTypes.instanceOf(Animated.Value); 37 | 38 | /* NavigationState */ 39 | const navigationState = PropTypes.shape({ 40 | key: PropTypes.string.isRequired, 41 | }); 42 | 43 | /* NavigationParentState */ 44 | const navigationParentState = PropTypes.shape({ 45 | index: PropTypes.number.isRequired, 46 | key: PropTypes.string.isRequired, 47 | children: PropTypes.arrayOf(navigationState), 48 | }); 49 | 50 | /* NavigationLayout */ 51 | const layout = PropTypes.shape({ 52 | height: animatedValue, 53 | initHeight: PropTypes.number.isRequired, 54 | initWidth: PropTypes.number.isRequired, 55 | isMeasured: PropTypes.bool.isRequired, 56 | width: animatedValue, 57 | }); 58 | 59 | /* NavigationScene */ 60 | const scene = PropTypes.shape({ 61 | index: PropTypes.number.isRequired, 62 | isStale: PropTypes.bool.isRequired, 63 | key: PropTypes.string.isRequired, 64 | navigationState, 65 | }); 66 | 67 | /* NavigationSceneRendererProps */ 68 | const SceneRendererProps = { 69 | layout: layout.isRequired, 70 | navigationState: navigationParentState.isRequired, 71 | onNavigate: PropTypes.func.isRequired, 72 | position: animatedValue.isRequired, 73 | scene: scene.isRequired, 74 | scenes: PropTypes.arrayOf(scene).isRequired, 75 | }; 76 | 77 | const SceneRenderer = PropTypes.shape(SceneRendererProps); 78 | 79 | /* NavigationPanPanHandlers */ 80 | const panHandlers = PropTypes.shape({ 81 | onMoveShouldSetResponder: PropTypes.func.isRequired, 82 | onMoveShouldSetResponderCapture: PropTypes.func.isRequired, 83 | onResponderEnd: PropTypes.func.isRequired, 84 | onResponderGrant: PropTypes.func.isRequired, 85 | onResponderMove: PropTypes.func.isRequired, 86 | onResponderReject: PropTypes.func.isRequired, 87 | onResponderRelease: PropTypes.func.isRequired, 88 | onResponderStart: PropTypes.func.isRequired, 89 | onResponderTerminate: PropTypes.func.isRequired, 90 | onResponderTerminationRequest: PropTypes.func.isRequired, 91 | onStartShouldSetResponder: PropTypes.func.isRequired, 92 | onStartShouldSetResponderCapture: PropTypes.func.isRequired, 93 | }); 94 | 95 | /** 96 | * Helper function that extracts the props needed for scene renderer. 97 | */ 98 | function extractSceneRendererProps( 99 | props: NavigationSceneRendererProps, 100 | ): NavigationSceneRendererProps { 101 | return { 102 | layout: props.layout, 103 | navigationState: props.navigationState, 104 | onNavigate: props.onNavigate, 105 | position: props.position, 106 | scene: props.scene, 107 | scenes: props.scenes, 108 | }; 109 | } 110 | 111 | module.exports = { 112 | // helpers 113 | extractSceneRendererProps, 114 | 115 | // Bundled propTypes. 116 | SceneRendererProps, 117 | 118 | // propTypes 119 | action, 120 | navigationParentState, 121 | navigationState, 122 | panHandlers, 123 | SceneRenderer, 124 | }; 125 | -------------------------------------------------------------------------------- /NavigationReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationReducer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | var NavigationFindReducer = require('./NavigationFindReducer'); 15 | var NavigationStackReducer = require('./NavigationStackReducer'); 16 | var NavigationTabsReducer = require('./NavigationTabsReducer'); 17 | 18 | const NavigationReducer = { 19 | FindReducer: NavigationFindReducer, 20 | StackReducer: NavigationStackReducer, 21 | TabsReducer: NavigationTabsReducer, 22 | }; 23 | 24 | module.exports = NavigationReducer; 25 | -------------------------------------------------------------------------------- /NavigationRootContainer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationRootContainer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const AsyncStorage = require('react-native').AsyncStorage; 15 | const Linking = require('react-native').Linking; 16 | const NavigationPropTypes = require('./NavigationPropTypes'); 17 | const NavigationStateUtils = require('./NavigationStateUtils'); 18 | const Platform = require('react-native').Platform; 19 | const React = require('react'); 20 | 21 | import type { 22 | NavigationAction, 23 | NavigationParentState, 24 | NavigationReducer, 25 | NavigationRenderer, 26 | } from 'NavigationTypeDefinition'; 27 | 28 | export type BackAction = { 29 | type: 'BackAction', 30 | }; 31 | 32 | type Props = { 33 | /* 34 | * The default action to be passed into the reducer when getting the first 35 | * state. Defaults to {type: 'RootContainerInitialAction'} 36 | */ 37 | initialAction: NavigationAction, 38 | 39 | /* 40 | * Provide linkingActionMap to instruct the container to subscribe to linking 41 | * events, and use this mapper to convert URIs into actions that your app can 42 | * handle 43 | */ 44 | linkingActionMap: ?((uri: ?string) => NavigationAction), 45 | 46 | /* 47 | * Provide this key, and the container will store the navigation state in 48 | * AsyncStorage through refreshes, with the provided key 49 | */ 50 | persistenceKey: ?string, 51 | 52 | 53 | /* 54 | * A function that will output the latest navigation state as a function of 55 | * the (optional) previous state, and an action 56 | */ 57 | reducer: NavigationReducer, 58 | 59 | 60 | /* 61 | * Set up the rendering of the app for a given navigation state 62 | */ 63 | renderNavigation: NavigationRenderer, 64 | }; 65 | 66 | type State = { 67 | navState: ?NavigationParentState, 68 | }; 69 | 70 | function getBackAction(): BackAction { 71 | return { type: 'BackAction' }; 72 | } 73 | 74 | const PropTypes = require('prop-types'); 75 | 76 | class NavigationRootContainer extends React.Component { 77 | _handleOpenURLEvent: Function; 78 | 79 | props: Props; 80 | state: State; 81 | 82 | static propTypes = { 83 | initialAction: NavigationPropTypes.action.isRequired, 84 | linkingActionMap: PropTypes.func, 85 | persistenceKey: PropTypes.string, 86 | reducer: PropTypes.func.isRequired, 87 | renderNavigation: PropTypes.func.isRequired, 88 | }; 89 | 90 | static defaultProps = { 91 | initialAction: { type: 'RootContainerInitialAction' }, 92 | }; 93 | 94 | static childContextTypes = { 95 | onNavigate: PropTypes.func, 96 | }; 97 | 98 | 99 | static getBackAction = getBackAction; 100 | 101 | constructor(props: Props) { 102 | super(props); 103 | 104 | let navState = null; 105 | if (!this.props.persistenceKey) { 106 | navState = NavigationStateUtils.getParent( 107 | this.props.reducer(null, props.initialAction) 108 | ); 109 | } 110 | this.state = { navState }; 111 | } 112 | 113 | componentWillMount(): void { 114 | (this: any).handleNavigation = this.handleNavigation.bind(this); 115 | (this: any)._handleOpenURLEvent = this._handleOpenURLEvent.bind(this); 116 | } 117 | 118 | componentDidMount(): void { 119 | if (this.props.linkingActionMap) { 120 | Linking.getInitialURL().then(this._handleOpenURL.bind(this)); 121 | Platform.OS === 'ios' && Linking.addEventListener('url', this._handleOpenURLEvent); 122 | } 123 | if (this.props.persistenceKey) { 124 | AsyncStorage.getItem(this.props.persistenceKey, (err, storedString) => { 125 | if (err || !storedString) { 126 | this.setState({ 127 | // $FlowFixMe - navState is missing properties :( 128 | navState: this.props.reducer(null, this.props.initialAction), 129 | }); 130 | return; 131 | } 132 | this.setState({ 133 | navState: JSON.parse(storedString), 134 | }); 135 | }); 136 | } 137 | } 138 | 139 | componentWillUnmount(): void { 140 | if (Platform.OS === 'ios') { 141 | Linking.removeEventListener('url', this._handleOpenURLEvent); 142 | } 143 | } 144 | 145 | _handleOpenURLEvent(event: {url: string}): void { 146 | this._handleOpenURL(event.url); 147 | } 148 | 149 | _handleOpenURL(url: ?string): void { 150 | if (!this.props.linkingActionMap) { 151 | return; 152 | } 153 | const action = this.props.linkingActionMap(url); 154 | if (action) { 155 | this.handleNavigation(action); 156 | } 157 | } 158 | 159 | getChildContext(): Object { 160 | return { 161 | onNavigate: this.handleNavigation, 162 | }; 163 | } 164 | 165 | handleNavigation(action: Object): boolean { 166 | const navState = this.props.reducer(this.state.navState, action); 167 | if (navState === this.state.navState) { 168 | return false; 169 | } 170 | this.setState({ 171 | // $FlowFixMe - navState is missing properties :( 172 | navState, 173 | }); 174 | 175 | if (this.props.persistenceKey) { 176 | AsyncStorage.setItem(this.props.persistenceKey, JSON.stringify(navState)); 177 | } 178 | 179 | return true; 180 | } 181 | 182 | render(): ReactElement { 183 | const navigation = this.props.renderNavigation( 184 | this.state.navState, 185 | this.handleNavigation 186 | ); 187 | return navigation; 188 | } 189 | } 190 | 191 | module.exports = NavigationRootContainer; 192 | -------------------------------------------------------------------------------- /NavigationScenesReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationScenesReducer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const invariant = require('fbjs/lib/invariant'); 15 | 16 | import type { 17 | NavigationParentState, 18 | NavigationScene, 19 | } from 'NavigationTypeDefinition'; 20 | 21 | const SCENE_KEY_PREFIX = 'scene_'; 22 | 23 | /** 24 | * Helper function to compare route keys (e.g. "9", "11"). 25 | */ 26 | function compareKey(one: string, two: string): number { 27 | var delta = one.length - two.length; 28 | if (delta > 0) { 29 | return 1; 30 | } 31 | if (delta < 0) { 32 | return -1; 33 | } 34 | return one > two ? 1 : -1; 35 | } 36 | 37 | /** 38 | * Helper function to sort scenes based on their index and view key. 39 | */ 40 | function compareScenes( 41 | one: NavigationScene, 42 | two: NavigationScene, 43 | ): number { 44 | if (one.index > two.index) { 45 | return 1; 46 | } 47 | if (one.index < two.index) { 48 | return -1; 49 | } 50 | 51 | return compareKey( 52 | one.key, 53 | two.key, 54 | ); 55 | } 56 | 57 | function areScenesShallowEqual( 58 | one: NavigationScene, 59 | two: NavigationScene, 60 | ): boolean { 61 | return ( 62 | one.key === two.key && 63 | one.index === two.index && 64 | one.isStale === two.isStale && 65 | one.navigationState === two.navigationState && 66 | one.navigationState.key === two.navigationState.key 67 | ); 68 | } 69 | 70 | function NavigationScenesReducer( 71 | scenes: Array, 72 | nextState: NavigationParentState, 73 | prevState: ?NavigationParentState, 74 | ): Array { 75 | 76 | const prevScenes = new Map(); 77 | const freshScenes = new Map(); 78 | const staleScenes = new Map(); 79 | 80 | // Populate stale scenes from previous scenes marked as stale. 81 | scenes.forEach(scene => { 82 | const {key} = scene; 83 | if (scene.isStale) { 84 | staleScenes.set(key, scene); 85 | } 86 | prevScenes.set(key, scene); 87 | }); 88 | 89 | const nextKeys = new Set(); 90 | nextState.children.forEach((navigationState, index) => { 91 | const key = SCENE_KEY_PREFIX + navigationState.key; 92 | const scene = { 93 | index, 94 | isStale: false, 95 | key, 96 | navigationState, 97 | }; 98 | invariant( 99 | !nextKeys.has(key), 100 | `navigationState.children[${index}].key "${key}" conflicts with` + 101 | 'another child!' 102 | ); 103 | nextKeys.add(key); 104 | 105 | if (staleScenes.has(key)) { 106 | // A previously `stale` scene is now part of the nextState, so we 107 | // revive it by removing it from the stale scene map. 108 | staleScenes.delete(key); 109 | } 110 | freshScenes.set(key, scene); 111 | }); 112 | 113 | if (prevState) { 114 | // Look at the previous children and classify any removed scenes as `stale`. 115 | prevState.children.forEach((navigationState, index) => { 116 | const key = SCENE_KEY_PREFIX + navigationState.key; 117 | if (freshScenes.has(key)) { 118 | return; 119 | } 120 | staleScenes.set(key, { 121 | index, 122 | isStale: true, 123 | key, 124 | navigationState, 125 | }); 126 | }); 127 | } 128 | 129 | const nextScenes = []; 130 | 131 | const mergeScene = (nextScene => { 132 | const {key} = nextScene; 133 | const prevScene = prevScenes.has(key) ? prevScenes.get(key) : null; 134 | if (prevScene && areScenesShallowEqual(prevScene, nextScene)) { 135 | // Reuse `prevScene` as `scene` so view can avoid unnecessary re-render. 136 | // This assumes that the scene's navigation state is immutable. 137 | nextScenes.push(prevScene); 138 | } else { 139 | nextScenes.push(nextScene); 140 | } 141 | }); 142 | 143 | staleScenes.forEach(mergeScene); 144 | freshScenes.forEach(mergeScene); 145 | 146 | return nextScenes.sort(compareScenes); 147 | } 148 | 149 | module.exports = NavigationScenesReducer; 150 | -------------------------------------------------------------------------------- /NavigationStackReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationStackReducer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | var NavigationStateUtils = require('./NavigationStateUtils'); 15 | 16 | import type { 17 | NavigationState, 18 | NavigationParentState, 19 | NavigationReducer, 20 | } from 'NavigationTypeDefinition'; 21 | 22 | import type { 23 | BackAction, 24 | } from 'NavigationRootContainer'; 25 | 26 | export type NavigationStackReducerAction = BackAction | { 27 | type: string, 28 | }; 29 | 30 | export type ReducerForStateHandler = (state: NavigationState) => NavigationReducer; 31 | 32 | export type PushedReducerForActionHandler = (action: any, lastState: NavigationParentState) => ?NavigationReducer; 33 | 34 | export type StackReducerConfig = { 35 | /* 36 | * The initialState is that the reducer will use when there is no previous state. 37 | * Must be a NavigationParentState: 38 | * 39 | * { 40 | * children: [ 41 | * {key: 'subState0'}, 42 | * {key: 'subState1'}, 43 | * ], 44 | * index: 0, 45 | * key: 'navStackKey' 46 | * } 47 | */ 48 | initialState: NavigationParentState; 49 | 50 | /* 51 | * Returns the sub-reducer for a particular state to handle. This will be called 52 | * when we need to handle an action on a sub-state. If no reducer is returned, 53 | * no action will be taken 54 | */ 55 | getReducerForState?: ReducerForStateHandler; 56 | 57 | /* 58 | * Returns a sub-reducer that will be used when pushing a new route. If a reducer 59 | * is returned, it be called to get the new state that will be pushed 60 | */ 61 | getPushedReducerForAction: PushedReducerForActionHandler; 62 | }; 63 | 64 | const defaultGetReducerForState = (initialState) => (state) => state || initialState; 65 | 66 | function NavigationStackReducer({initialState, getReducerForState, getPushedReducerForAction}: StackReducerConfig): NavigationReducer { 67 | const getReducerForStateWithDefault = getReducerForState || defaultGetReducerForState; 68 | return function (lastState: ?NavigationState, action: any): NavigationState { 69 | if (!lastState) { 70 | return initialState; 71 | } 72 | const lastParentState = NavigationStateUtils.getParent(lastState); 73 | if (!lastParentState) { 74 | return lastState; 75 | } 76 | 77 | const activeSubState = lastParentState.children[lastParentState.index]; 78 | const activeSubReducer = getReducerForStateWithDefault(activeSubState); 79 | const nextActiveState = activeSubReducer(activeSubState, action); 80 | if (nextActiveState !== activeSubState) { 81 | const nextChildren = [...lastParentState.children]; 82 | nextChildren[lastParentState.index] = nextActiveState; 83 | return { 84 | ...lastParentState, 85 | children: nextChildren, 86 | }; 87 | } 88 | 89 | const subReducerToPush = getPushedReducerForAction(action, lastParentState); 90 | if (subReducerToPush) { 91 | return NavigationStateUtils.push( 92 | lastParentState, 93 | subReducerToPush(null, action) 94 | ); 95 | } 96 | 97 | switch (action.type) { 98 | case 'back': 99 | case 'BackAction': 100 | if (lastParentState.index === 0 || lastParentState.children.length === 1) { 101 | return lastParentState; 102 | } 103 | return NavigationStateUtils.pop(lastParentState); 104 | } 105 | 106 | return lastParentState; 107 | }; 108 | } 109 | 110 | module.exports = NavigationStackReducer; 111 | -------------------------------------------------------------------------------- /NavigationStateUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationStateUtils 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const invariant = require('fbjs/lib/invariant'); 15 | 16 | import type { 17 | NavigationState, 18 | NavigationParentState, 19 | } from 'NavigationTypeDefinition'; 20 | 21 | function getParent(state: NavigationState): ?NavigationParentState { 22 | if ( 23 | (state instanceof Object) && 24 | (state.children instanceof Array) && 25 | (state.children[0] !== undefined) && 26 | (typeof state.index === 'number') && 27 | (state.children[state.index] !== undefined) 28 | ) { 29 | return state; 30 | } 31 | return null; 32 | } 33 | 34 | function get(state: NavigationState, key: string): ?NavigationState { 35 | const parentState = getParent(state); 36 | if (!parentState) { 37 | return null; 38 | } 39 | const childState = parentState.children.find(child => child.key === key); 40 | return childState || null; 41 | } 42 | 43 | function indexOf(state: NavigationState, key: string): ?number { 44 | const parentState = getParent(state); 45 | if (!parentState) { 46 | return null; 47 | } 48 | const index = parentState.children.map(child => child.key).indexOf(key); 49 | if (index === -1) { 50 | return null; 51 | } 52 | return index; 53 | } 54 | 55 | function push(state: NavigationParentState, newChildState: NavigationState): NavigationParentState { 56 | var lastChildren: Array = state.children; 57 | return { 58 | ...state, 59 | children: [ 60 | ...lastChildren, 61 | newChildState, 62 | ], 63 | index: lastChildren.length, 64 | }; 65 | } 66 | 67 | function pop(state: NavigationParentState): NavigationParentState { 68 | const lastChildren = state.children; 69 | return { 70 | ...state, 71 | children: lastChildren.slice(0, lastChildren.length - 1), 72 | index: lastChildren.length - 2, 73 | }; 74 | } 75 | 76 | function reset(state: NavigationState, nextChildren: ?Array, nextIndex: ?number): NavigationState { 77 | const parentState = getParent(state); 78 | if (!parentState) { 79 | return state; 80 | } 81 | const children = nextChildren || parentState.children; 82 | const index = nextIndex == null ? parentState.index : nextIndex; 83 | if (children === parentState.children && index === parentState.index) { 84 | return state; 85 | } 86 | return { 87 | ...parentState, 88 | children, 89 | index, 90 | }; 91 | } 92 | 93 | function set(state: ?NavigationState, key: string, nextChildren: Array, nextIndex: number): NavigationState { 94 | if (!state) { 95 | return { 96 | children: nextChildren, 97 | index: nextIndex, 98 | key, 99 | }; 100 | } 101 | const parentState = getParent(state); 102 | if (!parentState) { 103 | return { 104 | children: nextChildren, 105 | index: nextIndex, 106 | key, 107 | }; 108 | } 109 | if (nextChildren === parentState.children && nextIndex === parentState.index && key === parentState.key) { 110 | return parentState; 111 | } 112 | return { 113 | ...parentState, 114 | children: nextChildren, 115 | index: nextIndex, 116 | key, 117 | }; 118 | } 119 | 120 | function jumpToIndex(state: NavigationState, index: number): NavigationState { 121 | const parentState = getParent(state); 122 | if (parentState && parentState.index === index) { 123 | return parentState; 124 | } 125 | return { 126 | ...parentState, 127 | index, 128 | }; 129 | } 130 | 131 | function jumpTo(state: NavigationState, key: string): NavigationState { 132 | const parentState = getParent(state); 133 | if (!parentState) { 134 | return state; 135 | } 136 | const index = parentState.children.indexOf(parentState.children.find(child => child.key === key)); 137 | invariant( 138 | index !== -1, 139 | 'Cannot find child with matching key in this NavigationState' 140 | ); 141 | return { 142 | ...parentState, 143 | index, 144 | }; 145 | } 146 | 147 | function replaceAt(state: NavigationState, key: string, newState: NavigationState): NavigationState { 148 | const parentState = getParent(state); 149 | if (!parentState) { 150 | return state; 151 | } 152 | const children = [...parentState.children]; 153 | const index = parentState.children.indexOf(parentState.children.find(child => child.key === key)); 154 | invariant( 155 | index !== -1, 156 | 'Cannot find child with matching key in this NavigationState' 157 | ); 158 | children[index] = newState; 159 | return { 160 | ...parentState, 161 | children, 162 | }; 163 | } 164 | 165 | function replaceAtIndex(state: NavigationState, index: number, newState: NavigationState): NavigationState { 166 | const parentState = getParent(state); 167 | if (!parentState) { 168 | return state; 169 | } 170 | const children = [...parentState.children]; 171 | children[index] = newState; 172 | return { 173 | ...parentState, 174 | children, 175 | }; 176 | } 177 | 178 | const NavigationStateUtils = { 179 | getParent, 180 | get: get, 181 | indexOf, 182 | push, 183 | pop, 184 | reset, 185 | set: set, 186 | jumpToIndex, 187 | jumpTo, 188 | replaceAt, 189 | replaceAtIndex, 190 | }; 191 | 192 | module.exports = NavigationStateUtils; 193 | -------------------------------------------------------------------------------- /NavigationTabsReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationTabsReducer 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const NavigationFindReducer = require('./NavigationFindReducer'); 15 | const NavigationStateUtils = require('./NavigationStateUtils'); 16 | 17 | import type { 18 | NavigationReducer, 19 | NavigationState, 20 | } from 'NavigationTypeDefinition'; 21 | 22 | const ActionTypes = { 23 | JUMP_TO: 'react-native/NavigationExperimental/tabs-jumpTo', 24 | }; 25 | 26 | export type JumpToAction = { 27 | type: typeof ActionTypes.JUMP_TO, 28 | index: number, 29 | }; 30 | function NavigationTabsJumpToAction(index: number): JumpToAction { 31 | return { 32 | type: ActionTypes.JUMP_TO, 33 | index, 34 | }; 35 | } 36 | 37 | type TabsReducerConfig = { 38 | key: string; 39 | initialIndex: number; 40 | tabReducers: Array; 41 | }; 42 | 43 | function NavigationTabsReducer({key, initialIndex, tabReducers}: TabsReducerConfig): NavigationReducer { 44 | return function(lastNavState: ?NavigationState, action: ?any): NavigationState { 45 | if (!lastNavState) { 46 | lastNavState = { 47 | children: tabReducers.map(reducer => reducer(null, null)), 48 | index: initialIndex || 0, 49 | key, 50 | }; 51 | } 52 | const lastParentNavState = NavigationStateUtils.getParent(lastNavState); 53 | if (!action || !lastParentNavState) { 54 | return lastNavState; 55 | } 56 | if ( 57 | action.type === ActionTypes.JUMP_TO && 58 | action.index !== lastParentNavState.index 59 | ) { 60 | return NavigationStateUtils.jumpToIndex( 61 | lastParentNavState, 62 | action.index, 63 | ); 64 | } 65 | const subReducers = tabReducers.map((tabReducer, tabIndex) => { 66 | return function(navState: ?NavigationState, tabAction: any): NavigationState { 67 | if (!navState) { 68 | return lastParentNavState; 69 | } 70 | const parentState = NavigationStateUtils.getParent(navState); 71 | const tabState = parentState && parentState.children[tabIndex]; 72 | const nextTabState = tabReducer(tabState, tabAction); 73 | if (nextTabState && tabState !== nextTabState) { 74 | const tabs = parentState && parentState.children || []; 75 | tabs[tabIndex] = nextTabState; 76 | return { 77 | ...lastParentNavState, 78 | tabs, 79 | index: tabIndex, 80 | }; 81 | } 82 | return lastParentNavState; 83 | }; 84 | }); 85 | let selectedTabReducer = subReducers.splice(lastParentNavState.index, 1)[0]; 86 | subReducers.unshift(function(navState: ?NavigationState, action: any): NavigationState { 87 | if (navState && action.type === 'BackAction') { 88 | return NavigationStateUtils.jumpToIndex( 89 | lastParentNavState, 90 | initialIndex || 0 91 | ); 92 | } 93 | return lastParentNavState; 94 | }); 95 | subReducers.unshift(selectedTabReducer); 96 | const findReducer = NavigationFindReducer(subReducers, lastParentNavState); 97 | return findReducer(lastParentNavState, action); 98 | }; 99 | } 100 | 101 | NavigationTabsReducer.JumpToAction = NavigationTabsJumpToAction; 102 | 103 | module.exports = NavigationTabsReducer; 104 | -------------------------------------------------------------------------------- /NavigationTypeDefinition.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationTypeDefinition 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const Animated = require('react-native').Animated; 15 | 16 | // Object Instances 17 | 18 | export type NavigationAnimatedValue = Animated.Value; 19 | 20 | // Value & Structs. 21 | 22 | export type NavigationGestureDirection = 'horizontal' | 'vertical'; 23 | 24 | export type NavigationState = { 25 | key: string, 26 | }; 27 | 28 | export type NavigationParentState = { 29 | index: number, 30 | key: string, 31 | children: Array, 32 | }; 33 | 34 | export type NavigationAction = any; 35 | 36 | export type NavigationLayout = { 37 | height: NavigationAnimatedValue, 38 | initHeight: number, 39 | initWidth: number, 40 | isMeasured: boolean, 41 | width: NavigationAnimatedValue, 42 | }; 43 | 44 | export type NavigationPosition = NavigationAnimatedValue; 45 | 46 | export type NavigationScene = { 47 | index: number, 48 | isStale: boolean, 49 | key: string, 50 | navigationState: NavigationState, 51 | }; 52 | 53 | export type NavigationSceneRendererProps = { 54 | // The layout of the containing view of the scenes. 55 | layout: NavigationLayout, 56 | 57 | // The navigation state of the containing view. 58 | navigationState: NavigationParentState, 59 | 60 | // Callback to navigation with an action. 61 | onNavigate: NavigationActionCaller, 62 | 63 | // The progressive index of the containing view's navigation state. 64 | position: NavigationPosition, 65 | 66 | // The scene to render. 67 | scene: NavigationScene, 68 | 69 | // All the scenes of the containing view's. 70 | scenes: Array, 71 | }; 72 | 73 | export type NavigationPanPanHandlers = { 74 | onMoveShouldSetResponder: Function, 75 | onMoveShouldSetResponderCapture: Function, 76 | onResponderEnd: Function, 77 | onResponderGrant: Function, 78 | onResponderMove: Function, 79 | onResponderReject: Function, 80 | onResponderRelease: Function, 81 | onResponderStart: Function, 82 | onResponderTerminate: Function, 83 | onResponderTerminationRequest: Function, 84 | onStartShouldSetResponder: Function, 85 | onStartShouldSetResponderCapture: Function, 86 | }; 87 | 88 | // Functions. 89 | 90 | export type NavigationActionCaller = Function; 91 | 92 | export type NavigationAnimationSetter = ( 93 | position: NavigationAnimatedValue, 94 | newState: NavigationParentState, 95 | lastState: NavigationParentState, 96 | ) => void; 97 | 98 | export type NavigationRenderer = ( 99 | navigationState: ?NavigationState, 100 | onNavigate: NavigationActionCaller, 101 | ) => ReactElement; 102 | 103 | export type NavigationReducer = ( 104 | state: ?NavigationState, 105 | action: ?NavigationAction, 106 | ) => NavigationState; 107 | 108 | export type NavigationSceneRenderer = ( 109 | props: NavigationSceneRendererProps, 110 | ) => ?ReactElement; 111 | 112 | export type NavigationStyleInterpolator = ( 113 | props: NavigationSceneRendererProps, 114 | ) => Object; 115 | -------------------------------------------------------------------------------- /NavigationView.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule NavigationView 10 | * @flow 11 | */ 12 | 'use strict'; 13 | 14 | const Animated = require('react-native').Animated; 15 | const NavigationContainer = require('./NavigationContainer'); 16 | const React = require('react'); 17 | const StyleSheet = require('react-native').StyleSheet; 18 | const View = require('react-native').View; 19 | const NavigationScenesReducer = require('./NavigationScenesReducer'); 20 | 21 | import type { 22 | NavigationActionCaller, 23 | NavigationAnimatedValue, 24 | NavigationLayout, 25 | NavigationParentState, 26 | NavigationScene, 27 | NavigationSceneRenderer, 28 | NavigationSceneRendererProps, 29 | } from 'NavigationTypeDefinition'; 30 | 31 | type Props = { 32 | navigationState: NavigationParentState, 33 | onNavigate: NavigationActionCaller, 34 | renderScene: NavigationSceneRenderer, 35 | style: any, 36 | }; 37 | 38 | type State = { 39 | layout: NavigationLayout, 40 | scenes: Array, 41 | }; 42 | 43 | const PropTypes = require('prop-types'); 44 | 45 | /** 46 | * A simple view that will render a scene for the currently focused sub-state. 47 | * The most common use-case is for tabs, where no transition is needed 48 | */ 49 | class NavigationView extends React.PureComponent { 50 | _onLayout: (event: any) => void; 51 | _position: NavigationAnimatedValue; 52 | 53 | props: Props; 54 | state: State; 55 | 56 | static propTypes = { 57 | navigationState: PropTypes.object.isRequired, 58 | onNavigate: PropTypes.func.isRequired, 59 | renderScene: PropTypes.func.isRequired, 60 | }; 61 | 62 | constructor(props: Props, context: any) { 63 | super(props, context); 64 | 65 | const layout = { 66 | initWidth: 0, 67 | initHeight: 0, 68 | isMeasured: false, 69 | width: new Animated.Value(0), 70 | height: new Animated.Value(0), 71 | }; 72 | 73 | const {navigationState} = this.props; 74 | 75 | this._position = new Animated.Value(navigationState.index); 76 | 77 | this.state = { 78 | layout, 79 | scenes: NavigationScenesReducer([], navigationState), 80 | }; 81 | } 82 | 83 | componentWillReceiveProps(nextProps: Props): void { 84 | if (nextProps.navigationState !== this.props.navigationState) { 85 | const {navigationState} = nextProps; 86 | this.setState( 87 | { 88 | scenes: NavigationScenesReducer( 89 | this.state.scenes, 90 | navigationState, 91 | null, // There will be no transtion. 92 | ), 93 | }, 94 | () => { 95 | this._position.setValue(navigationState.index); 96 | }, 97 | ); 98 | } 99 | } 100 | 101 | componentWillMount(): void { 102 | this._onLayout = this._onLayout.bind(this); 103 | } 104 | 105 | render(): ReactElement { 106 | const { 107 | navigationState, 108 | onNavigate 109 | } = this.props; 110 | 111 | const { 112 | layout, 113 | scenes, 114 | } = this.state; 115 | 116 | const sceneProps = { 117 | layout, 118 | navigationState: navigationState, 119 | onNavigate: onNavigate, 120 | position: this._position, 121 | scene: scenes[navigationState.index], 122 | scenes, 123 | }; 124 | 125 | return ( 126 | 129 | {this._renderScene(sceneProps)} 130 | 131 | ); 132 | } 133 | 134 | _renderScene(props: NavigationSceneRendererProps): ?ReactElement { 135 | 136 | const child = this.props.renderScene(props); 137 | if (child === null) { 138 | return null; 139 | } 140 | return {child}; 141 | } 142 | 143 | _onLayout(event: any): void { 144 | const {height, width} = event.nativeEvent.layout; 145 | 146 | const layout = { 147 | ...this.state.layout, 148 | initHeight: height, 149 | initWidth: width, 150 | isMeasured: true, 151 | }; 152 | 153 | layout.height.setValue(height); 154 | layout.width.setValue(width); 155 | 156 | this.setState({ layout }); 157 | } 158 | } 159 | 160 | const styles = StyleSheet.create({ 161 | scene: { 162 | bottom: 0, 163 | left: 0, 164 | position: 'absolute', 165 | right: 0, 166 | top: 0, 167 | }, 168 | }); 169 | 170 | module.exports = NavigationContainer.create(NavigationView); 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-experimental-navigation 2 | React Native ExperimentalNavigation clone 3 | 4 | ## This package is obsolete and no longer supported 5 | Please use v4 version (based on ReactNavigation.org) of react-native-router-flux instead 6 | -------------------------------------------------------------------------------- /assets/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back.png -------------------------------------------------------------------------------- /assets/back@1.5x.android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@1.5x.android.png -------------------------------------------------------------------------------- /assets/back@1.5x.ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@1.5x.ios.png -------------------------------------------------------------------------------- /assets/back@1.5x.windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@1.5x.windows.png -------------------------------------------------------------------------------- /assets/back@1x.android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@1x.android.png -------------------------------------------------------------------------------- /assets/back@1x.ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@1x.ios.png -------------------------------------------------------------------------------- /assets/back@1x.windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@1x.windows.png -------------------------------------------------------------------------------- /assets/back@2x.android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@2x.android.png -------------------------------------------------------------------------------- /assets/back@2x.ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@2x.ios.png -------------------------------------------------------------------------------- /assets/back@2x.windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@2x.windows.png -------------------------------------------------------------------------------- /assets/back@3x.android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@3x.android.png -------------------------------------------------------------------------------- /assets/back@3x.ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@3x.ios.png -------------------------------------------------------------------------------- /assets/back@3x.windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@3x.windows.png -------------------------------------------------------------------------------- /assets/back@4x.android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@4x.android.png -------------------------------------------------------------------------------- /assets/back@4x.ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@4x.ios.png -------------------------------------------------------------------------------- /assets/back@4x.windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/assets/back@4x.windows.png -------------------------------------------------------------------------------- /back_chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aksonov/react-native-experimental-navigation/b8a9ea78b2df038d5088bcf88ce85a689cd191ca/back_chevron.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-experimental-navigation", 3 | "version": "0.29.2", 4 | "description": "React Native ExperimentalNavigation clone", 5 | "main": "NavigationExperimental", 6 | "scripts": { 7 | "build": "babel src -d lib" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/aksonov/react-native-experimental-navigation.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "ExperimentalNavigation", 16 | "navigation" 17 | ], 18 | "author": "Pavlo Aksonov", 19 | "license": "BSD 3-clause \"New\" or \"Revised\" License", 20 | "bugs": { 21 | "url": "https://github.com/aksonov/react-native-experimental-navigation/issues" 22 | }, 23 | "homepage": "https://github.com/aksonov/react-native-experimental-navigation#readme", 24 | "devDependencies": { 25 | "babel-cli": "^6.8.0", 26 | "clamp": "^1.0.1", 27 | "fbjs": "^0.8.1", 28 | "babel-plugin-syntax-flow": "^6.8.0", 29 | "babel-plugin-transform-flow-strip-types": "^6.8.0", 30 | "babel-preset-react-native": "^1.7.0" 31 | }, 32 | "babel": { 33 | "plugins": [ 34 | "syntax-flow", 35 | "transform-flow-strip-types" 36 | ], 37 | "presets": [ 38 | "react-native" 39 | ] 40 | }, 41 | "dependencies": { 42 | "babel-plugin-syntax-flow": "^6.8.0", 43 | "babel-plugin-transform-flow-strip-types": "^6.8.0", 44 | "babel-preset-react-native": "^1.9.0", 45 | "clamp": "^1.0.1", 46 | "fbjs": "^0.8.1", 47 | "prop-types": "^15.5.10" 48 | } 49 | } 50 | --------------------------------------------------------------------------------