diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js index c27f3170d613b5..705f7d6be432eb 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -13,20 +20,23 @@ */ 'use strict'; -var React = require('react-native'); -var { +const React = require('react-native'); + +const { Animated, NavigationExperimental, StyleSheet, ScrollView, } = React; -var NavigationExampleRow = require('./NavigationExampleRow'); -var { + +const NavigationExampleRow = require('./NavigationExampleRow'); + +const { AnimatedView: NavigationAnimatedView, Card: NavigationCard, - RootContainer: NavigationRootContainer, - Reducer: NavigationReducer, Header: NavigationHeader, + Reducer: NavigationReducer, + RootContainer: NavigationRootContainer, } = NavigationExperimental; const NavigationBasicReducer = NavigationReducer.StackReducer({ @@ -79,7 +89,7 @@ class NavigationAnimatedExample extends React.Component { style={styles.animatedView} renderOverlay={this._renderHeader} applyAnimation={(pos, navState) => { - Animated.timing(pos, {toValue: navState.index, duration: 1000}).start(); + Animated.timing(pos, {toValue: navState.index, duration: 500}).start(); }} renderScene={this._renderCard} /> diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js index b6aa01a7993937..2ac20280a70f8e 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -14,7 +21,6 @@ 'use strict'; const NavigationExampleRow = require('./NavigationExampleRow'); -const NavigationRootContainer = require('NavigationRootContainer'); const React = require('react-native'); const { @@ -23,10 +29,13 @@ const { ScrollView, } = React; -const NavigationCardStack = NavigationExperimental.CardStack; -const NavigationStateUtils = NavigationExperimental.StateUtils; +const { + CardStack: NavigationCardStack, + StateUtils: NavigationStateUtils, + RootContainer: NavigationRootContainer, +} = NavigationExperimental; -function reduceNavigationState(initialState) { +function createReducer(initialState) { return (currentState, action) => { switch (action.type) { case 'RootContainerInitialAction': @@ -47,7 +56,7 @@ function reduceNavigationState(initialState) { }; } -const ExampleReducer = reduceNavigationState({ +const ExampleReducer = createReducer({ index: 0, key: 'exmaple', children: [{key: 'First Route'}], diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js index 258ce5bc8394bb..6e3d597f27c0e9 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js @@ -1,5 +1,10 @@ /** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * Facebook, Inc. ("Facebook") owns all right, title and interest, including * all intellectual property and other proprietary rights, in and to the React @@ -28,36 +33,24 @@ 'use strict'; const Animated = require('Animated'); +const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); const NavigationContainer = require('NavigationContainer'); const NavigationLinearPanResponder = require('NavigationLinearPanResponder'); const NavigationPropTypes = require('NavigationPropTypes'); -const React = require('React'); +const React = require('react-native'); const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin'); const StyleSheet = require('StyleSheet'); const View = require('View'); -const {Directions} = NavigationLinearPanResponder; - import type { - NavigationAnimatedValue, - NavigationLayout, - NavigationPosition, + NavigationPanPanHandlers, NavigationSceneRenderer, NavigationSceneRendererProps, } from 'NavigationTypeDefinition'; -import type { - NavigationGestureDirection -} from 'NavigationLinearPanResponder'; - -type State = { - hash: string, - height: number, - width: number, -}; - type Props = NavigationSceneRendererProps & { - direction: NavigationGestureDirection, + style: any, + panHandlers: ?NavigationPanPanHandlers, renderScene: NavigationSceneRenderer, }; @@ -65,78 +58,18 @@ const {PropTypes} = React; const propTypes = { ...NavigationPropTypes.SceneRenderer, - direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]), + style: PropTypes.any, + panHandlers: NavigationPropTypes.panHandlers, renderScene: PropTypes.func.isRequired, }; -const defaultProps = { - direction: Directions.HORIZONTAL, -}; - -class AmimatedValueSubscription { - _value: NavigationAnimatedValue; - _token: string; - - constructor(value: NavigationAnimatedValue, callback: Function) { - this._value = value; - this._token = value.addListener(callback); - } - - remove() { - this._value.removeListener(this._token); - } -} - -/** - * Class that provides the required information for the - * `NavigationLinearPanResponder`. This class must implement - * the interface `NavigationLinearPanResponderDelegate`. - */ -class PanResponderDelegate { - _props : Props; - - constructor(props: Props) { - this._props = props; - } - - getDirection(): NavigationGestureDirection { - return this._props.direction; - } - - getIndex(): number { - return this._props.navigationState.index; - } - - getLayout(): NavigationLayout { - return this._props.layout; - } - - getPosition(): NavigationPosition { - return this._props.position; - } - - onNavigate(action: {type: string}): void { - this._props.onNavigate && this._props.onNavigate(action); - } -} - /** * Component that renders the scene as card for the . */ -class NavigationCard extends React.Component { +class NavigationCard extends React.Component { props: Props; - state: State; - _calculateState: (t: NavigationLayout) => State; - _layoutListeners: Array; - - constructor(props: Props, context: any) { - super(props, context); - - this.state = this._calculateState(props.layout); - this._layoutListeners = []; - } - shouldComponentUpdate(nextProps: Object, nextState: Object): boolean { + shouldComponentUpdate(nextProps: Props, nextState: any): boolean { return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call( this, nextProps, @@ -144,130 +77,32 @@ class NavigationCard extends React.Component { ); } - componentWillMount(): void { - this._calculateState = this._calculateState.bind(this); - } - - componentDidMount(): void { - this._applyLayout(this.props.layout); - } - - componentWillUnmount(): void { - this._layoutListeners.forEach(subscription => subscription.remove); - } - - componentWillReceiveProps(nextProps: Props): void { - this._applyLayout(nextProps.layout); - } - render(): ReactElement { - const { - direction, - layout, - navigationState, - onNavigate, - position, - scene, - scenes, + let { + style, + panHandlers, + renderScene, + ...props, } = this.props; - const { - height, - width, - } = this.state; - - const index = scene.index; - const isVertical = direction === 'vertical'; - const inputRange = [index - 1, index, index + 1]; - const animatedStyle = { - - opacity: position.interpolate({ - inputRange, - outputRange: [1, 1, 0.3], - }), - - transform: [ - { - scale: position.interpolate({ - inputRange, - outputRange: [1, 1, 0.95], - }), - }, - { - translateX: isVertical ? 0 : - position.interpolate({ - inputRange, - outputRange: [width, 0, -10], - }), - }, - { - translateY: !isVertical ? 0 : - position.interpolate({ - inputRange, - outputRange: [height, 0, -10], - }), - }, - ], - }; - - let panHandlers = null; - if (navigationState.index === index) { - const delegate = new PanResponderDelegate(this.props); - const panResponder = new NavigationLinearPanResponder(delegate); - panHandlers = panResponder.panHandlers; + if (style === undefined) { + // fall back to default style. + style = NavigationCardStackStyleInterpolator.forHorizontal(props); + } + if (panHandlers === undefined) { + // fall back to default pan handlers. + panHandlers = NavigationLinearPanResponder.forHorizontal(props); } - - const sceneProps = { - layout, - navigationState, - onNavigate, - position, - scene, - scenes, - }; return ( - - {this.props.renderScene(sceneProps)} + + {renderScene(props)} ); } - - _calculateState(layout: NavigationLayout): State { - const width = layout.width.__getValue(); - const height = layout.height.__getValue(); - const hash = 'layout-' + width + '-' + height; - const state = { - height, - width, - hash, - }; - return state; - } - - _applyLayout(layout: NavigationLayout) { - this._layoutListeners.forEach(subscription => subscription.remove); - - this._layoutListeners.length = 0; - - const callback = this._applyLayout.bind(this, layout); - - this._layoutListeners.push( - new AmimatedValueSubscription(layout.width, callback), - new AmimatedValueSubscription(layout.height, callback), - ); - - const nextState = this._calculateState(layout); - if (nextState.hash !== this.state.hash) { - this.setState(nextState); - } - } } NavigationCard.propTypes = propTypes; -NavigationCard.defaultProps = defaultProps; const styles = StyleSheet.create({ main: { diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js index 8b6cc316d4fbae..319d8d721b9545 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js @@ -1,5 +1,10 @@ /** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * Facebook, Inc. ("Facebook") owns all right, title and interest, including * all intellectual property and other proprietary rights, in and to the React @@ -30,6 +35,7 @@ const Animated = require('Animated'); const NavigationAnimatedView = require('NavigationAnimatedView'); const NavigationCard = require('NavigationCard'); +const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); const NavigationContainer = require('NavigationContainer'); const NavigationLinearPanResponder = require('NavigationLinearPanResponder'); const NavigationPropTypes = require('NavigationPropTypes'); @@ -110,12 +116,23 @@ class NavigationCardStack extends React.Component { } _renderScene(props: NavigationSceneRendererProps): ReactElement { + const isVertical = this.props.direction === 'vertical'; + + const style = isVertical ? + NavigationCardStackStyleInterpolator.forVertical(props) : + NavigationCardStackStyleInterpolator.forHorizontal(props); + + const panHandlers = isVertical ? + NavigationLinearPanResponder.forVertical(props) : + NavigationLinearPanResponder.forHorizontal(props); + return ( ); } diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js new file mode 100644 index 00000000000000..d4b65644ac7030 --- /dev/null +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStackStyleInterpolator.js @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * Facebook, Inc. ("Facebook") owns all right, title and interest, including + * all intellectual property and other proprietary rights, in and to the React + * Native CustomComponents software (the "Software"). Subject to your + * compliance with these terms, you are hereby granted a non-exclusive, + * worldwide, royalty-free copyright license to (1) use and copy the Software; + * and (2) reproduce and distribute the Software as part of your own software + * ("Your Software"). Facebook reserves all rights not expressly granted to + * you in this license agreement. + * + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @providesModule NavigationCardStackStyleInterpolator + * @flow + */ +'use strict'; + +/** + * Predefined interpolator that renders the animated style for NavigationCard. + * + */ + +import type { + NavigationSceneRendererProps, +} from 'NavigationTypeDefinition'; + +function forHorizontal(props: NavigationSceneRendererProps): Object { + const { + layout, + position, + scene, + } = props; + + const index = scene.index; + const inputRange = [index - 1, index, index + 1]; + const width = layout.initWidth; + + const opacity = position.interpolate({ + inputRange, + outputRange: [1, 1, 0.3], + }); + + const scale = position.interpolate({ + inputRange, + outputRange: [1, 1, 0.95], + }); + + const translateY = 0; + const translateX = position.interpolate({ + inputRange, + outputRange: [width, 0, -10], + }); + + return { + opacity, + transform: [ + { scale }, + { translateX }, + { translateY }, + ], + }; +} + +function forVertical(props: NavigationSceneRendererProps): Object { + const { + layout, + position, + scene, + } = props; + + const index = scene.index; + const inputRange = [index - 1, index, index + 1]; + const height = layout.initHeight; + + const opacity = position.interpolate({ + inputRange, + outputRange: [1, 1, 0.3], + }); + + const scale = position.interpolate({ + inputRange, + outputRange: [1, 1, 0.95], + }); + + const translateX = 0; + const translateY = position.interpolate({ + inputRange, + outputRange: [height, 0, -10], + }); + + return { + opacity, + transform: [ + { scale }, + { translateX }, + { translateY }, + ], + }; +} + +module.exports = { + forHorizontal, + forVertical, +}; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigator.js b/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigator.js index 6f1909db89e59f..bbb0221a8d2095 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigator.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationLegacyNavigator.js @@ -1,5 +1,10 @@ /** - * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * Facebook, Inc. ("Facebook") owns all right, title and interest, including * all intellectual property and other proprietary rights, in and to the React @@ -30,8 +35,10 @@ const NavigationAnimatedValueSubscription = require('NavigationAnimatedValueSubscription'); const NavigationAnimatedView = require('NavigationAnimatedView'); const NavigationCard = require('NavigationCard'); +const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); const NavigationContext = require('NavigationContext'); const NavigationLegacyNavigatorRouteStack = require('NavigationLegacyNavigatorRouteStack'); +const NavigationLinearPanResponder = require('NavigationLinearPanResponder'); const NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar'); const NavigatorNavigationBar = require('NavigatorNavigationBar'); const NavigatorSceneConfigs = require('NavigatorSceneConfigs'); @@ -287,38 +294,48 @@ class NavigationLegacyNavigator extends React.Component { } _renderCard(props: NavigationSceneRendererProps): ReactElement { - let direction = 'horizontal'; - - const {navigationState} = props.scene; + const {scene} = props; const {configureScene} = this.props; + let isVertical = false; + if (configureScene) { - const route = RouteStack.getRouteByNavigationState(navigationState); + const route = RouteStack.getRouteByNavigationState(scene.navigationState); const config = configureScene(route, this.state.routeStack); + const direction = getConfigPopDirection(config); - switch (getConfigPopDirection(config)) { + switch (direction) { case 'left-to-right': - direction = 'horizontal'; + // default. break; case 'top-to-bottom': - direction = 'vertical'; + isVertical = true; break; default: // unsupported config. if (__DEV__) { - console.warn('unsupported scene configuration'); + console.warn('unsupported scene configuration %s', direction); } } } + const style = isVertical ? + NavigationCardStackStyleInterpolator.forVertical(props) : + NavigationCardStackStyleInterpolator.forHorizontal(props); + + const panHandlers = isVertical ? + NavigationLinearPanResponder.forVertical(props) : + NavigationLinearPanResponder.forHorizontal(props); + return ( ); } diff --git a/Libraries/NavigationExperimental/NavigationAbstractPanResponder.js b/Libraries/NavigationExperimental/NavigationAbstractPanResponder.js index 98fdbf32d0055f..cd31dccdbde158 100644 --- a/Libraries/NavigationExperimental/NavigationAbstractPanResponder.js +++ b/Libraries/NavigationExperimental/NavigationAbstractPanResponder.js @@ -1,5 +1,10 @@ /** - * Copyright 2004-present Facebook. All Rights Reserved. + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule NavigationAbstractPanResponder * @flow @@ -10,6 +15,10 @@ const PanResponder = require('PanResponder'); const invariant = require('fbjs/lib/invariant'); +import type { + NavigationPanPanHandlers, +} from 'NavigationTypeDefinition'; + const EmptyPanHandlers = { onMoveShouldSetPanResponder: null, onPanResponderGrant: null, @@ -24,7 +33,7 @@ const EmptyPanHandlers = { */ class NavigationAbstractPanResponder { - panHandlers: Object; + panHandlers: NavigationPanPanHandlers; constructor() { const config = {}; diff --git a/Libraries/NavigationExperimental/NavigationExperimental.js b/Libraries/NavigationExperimental/NavigationExperimental.js index ca6bb627d5597b..e30fa3a8380fe2 100644 --- a/Libraries/NavigationExperimental/NavigationExperimental.js +++ b/Libraries/NavigationExperimental/NavigationExperimental.js @@ -14,9 +14,11 @@ const NavigationAnimatedView = require('NavigationAnimatedView'); const NavigationCard = require('NavigationCard'); const NavigationCardStack = require('NavigationCardStack'); +const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); const NavigationContainer = require('NavigationContainer'); const NavigationHeader = require('NavigationHeader'); const NavigationLegacyNavigator = require('NavigationLegacyNavigator'); +const NavigationLinearPanResponder = require('NavigationLinearPanResponder'); const NavigationReducer = require('NavigationReducer'); const NavigationRootContainer = require('NavigationRootContainer'); const NavigationStateUtils = require('NavigationStateUtils'); @@ -40,6 +42,12 @@ const NavigationExperimental = { CardStack: NavigationCardStack, Header: NavigationHeader, LegacyNavigator: NavigationLegacyNavigator, + + // Animations Style Interpolators: + CardStackStyleInterpolator: NavigationCardStackStyleInterpolator, + + // Interactions: + LinearPanResponder: NavigationLinearPanResponder, }; module.exports = NavigationExperimental; diff --git a/Libraries/NavigationExperimental/NavigationLinearPanResponder.js b/Libraries/NavigationExperimental/NavigationLinearPanResponder.js index 2318a70c6ca30c..94b23ea9e291ee 100644 --- a/Libraries/NavigationExperimental/NavigationLinearPanResponder.js +++ b/Libraries/NavigationExperimental/NavigationLinearPanResponder.js @@ -1,5 +1,10 @@ /** - * Copyright 2004-present Facebook. All Rights Reserved. + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule NavigationLinearPanResponder * @flow @@ -13,9 +18,8 @@ const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder') const clamp = require('clamp'); import type { - NavigationActionCaller, - NavigationLayout, - NavigationPosition, + NavigationPanPanHandlers, + NavigationSceneRendererProps, } from 'NavigationTypeDefinition'; /** @@ -36,9 +40,9 @@ const POSITION_THRESHOLD = 1 / 3; const RESPOND_THRESHOLD = 15; /** - * The threshold (in speed) to finish the gesture action. + * The threshold (in pixels) to finish the gesture action. */ -const VELOCITY_THRESHOLD = 100; +const DISTANCE_THRESHOLD = 100; /** * Primitive gesture directions. @@ -48,7 +52,7 @@ const Directions = { 'VERTICAL': 'vertical', }; -export type NavigationGestureDirection = $Enum; +export type NavigationGestureDirection = 'horizontal' | 'vertical'; /** * Primitive gesture actions. @@ -60,43 +64,39 @@ const Actions = { BACK: {type: 'back'}, }; -/** - * The type interface of the object that provides the information required by - * NavigationLinearPanResponder. - */ -export type NavigationLinearPanResponderDelegate = { - getDirection: () => NavigationGestureDirection; - getIndex: () => number, - getLayout: () => NavigationLayout, - getPosition: () => NavigationPosition, - onNavigate: NavigationActionCaller, -}; - /** * Pan responder that handles the One-dimensional gesture (horizontal or * vertical). */ class NavigationLinearPanResponder extends NavigationAbstractPanResponder { - static Actions: Object; - static Directions: Object; _isResponding: boolean; + _isVertical: boolean; + _props: NavigationSceneRendererProps; _startValue: number; - _delegate: NavigationLinearPanResponderDelegate; - constructor(delegate: NavigationLinearPanResponderDelegate) { + constructor( + direction: NavigationGestureDirection, + props: NavigationSceneRendererProps, + ) { super(); this._isResponding = false; + this._isVertical = direction === Directions.VERTICAL; + this._props = props; this._startValue = 0; - this._delegate = delegate; } onMoveShouldSetPanResponder(event: any, gesture: any): boolean { - const delegate = this._delegate; - const layout = delegate.getLayout(); - const isVertical = delegate.getDirection() === Directions.VERTICAL; + const props = this._props; + + if (props.navigationState.index !== props.scene.index) { + return false; + } + + const layout = props.layout; + const isVertical = this._isVertical; const axis = isVertical ? 'dy' : 'dx'; - const index = delegate.getIndex(); + const index = props.navigationState.index; const distance = isVertical ? layout.height.__getValue() : layout.width.__getValue(); @@ -110,7 +110,7 @@ class NavigationLinearPanResponder extends NavigationAbstractPanResponder { onPanResponderGrant(): void { this._isResponding = false; - this._delegate.getPosition().stopAnimation((value: number) => { + this._props.position.stopAnimation((value: number) => { this._isResponding = true; this._startValue = value; }); @@ -121,11 +121,11 @@ class NavigationLinearPanResponder extends NavigationAbstractPanResponder { return; } - const delegate = this._delegate; - const layout = delegate.getLayout(); - const isVertical = delegate.getDirection() === Directions.VERTICAL; + const props = this._props; + const layout = props.layout; + const isVertical = this._isVertical; const axis = isVertical ? 'dy' : 'dx'; - const index = delegate.getIndex(); + const index = props.navigationState.index; const distance = isVertical ? layout.height.__getValue() : layout.width.__getValue(); @@ -136,7 +136,7 @@ class NavigationLinearPanResponder extends NavigationAbstractPanResponder { index ); - this._delegate.getPosition().setValue(value); + props.position.setValue(value); } onPanResponderRelease(event: any, gesture: any): void { @@ -146,16 +146,16 @@ class NavigationLinearPanResponder extends NavigationAbstractPanResponder { this._isResponding = false; - const delegate = this._delegate; - const isVertical = delegate.getDirection() === Directions.VERTICAL; + const props = this._props; + const isVertical = this._isVertical; const axis = isVertical ? 'dy' : 'dx'; - const index = delegate.getIndex(); - const velocity = gesture[axis]; + const index = props.navigationState.index; + const distance = gesture[axis]; - delegate.getPosition().stopAnimation((value: number) => { + props.position.stopAnimation((value: number) => { this._reset(); - if (velocity > VELOCITY_THRESHOLD || value <= index - POSITION_THRESHOLD) { - delegate.onNavigate(Actions.BACK); + if (distance > DISTANCE_THRESHOLD || value <= index - POSITION_THRESHOLD) { + props.onNavigate(Actions.BACK); } }); } @@ -166,17 +166,40 @@ class NavigationLinearPanResponder extends NavigationAbstractPanResponder { } _reset(): void { + const props = this._props; Animated.timing( - this._delegate.getPosition(), + props.position, { - toValue: this._delegate.getIndex(), + toValue: props.navigationState.index, duration: ANIMATION_DURATION, } ).start(); } } -NavigationLinearPanResponder.Actions = Actions; -NavigationLinearPanResponder.Directions = Directions; +function createPanHandlers( + direction: NavigationGestureDirection, + props: NavigationSceneRendererProps, +): NavigationPanPanHandlers { + const responder = new NavigationLinearPanResponder(direction, props); + return responder.panHandlers; +} + +function forHorizontal( + props: NavigationSceneRendererProps, +): NavigationPanPanHandlers { + return createPanHandlers(Directions.HORIZONTAL, props); +} -module.exports = NavigationLinearPanResponder; +function forVertical( + props: NavigationSceneRendererProps, +): NavigationPanPanHandlers { + return createPanHandlers(Directions.VERTICAL, props); +} + +module.exports = { + Actions, + Directions, + forHorizontal, + forVertical, +}; diff --git a/Libraries/NavigationExperimental/NavigationPropTypes.js b/Libraries/NavigationExperimental/NavigationPropTypes.js index b5d87834199644..841895f51014f9 100644 --- a/Libraries/NavigationExperimental/NavigationPropTypes.js +++ b/Libraries/NavigationExperimental/NavigationPropTypes.js @@ -68,9 +68,26 @@ const SceneRenderer = { scenes: PropTypes.arrayOf(scene).isRequired, }; +/* NavigationPanPanHandlers */ +const panHandlers = PropTypes.shape({ + onMoveShouldSetResponder: PropTypes.func.isRequired, + onMoveShouldSetResponderCapture: PropTypes.func.isRequired, + onResponderEnd: PropTypes.func.isRequired, + onResponderGrant: PropTypes.func.isRequired, + onResponderMove: PropTypes.func.isRequired, + onResponderReject: PropTypes.func.isRequired, + onResponderRelease: PropTypes.func.isRequired, + onResponderStart: PropTypes.func.isRequired, + onResponderTerminate: PropTypes.func.isRequired, + onResponderTerminationRequest: PropTypes.func.isRequired, + onStartShouldSetResponder: PropTypes.func.isRequired, + onStartShouldSetResponderCapture: PropTypes.func.isRequired, +}); + module.exports = { SceneRenderer, action, navigationParentState, navigationState, + panHandlers, }; diff --git a/Libraries/NavigationExperimental/NavigationTypeDefinition.js b/Libraries/NavigationExperimental/NavigationTypeDefinition.js index 3f6f6e43922af1..6ecd2774bf7f7f 100644 --- a/Libraries/NavigationExperimental/NavigationTypeDefinition.js +++ b/Libraries/NavigationExperimental/NavigationTypeDefinition.js @@ -68,6 +68,21 @@ export type NavigationSceneRendererProps = { scenes: Array, }; +export type NavigationPanPanHandlers = { + onMoveShouldSetResponder: Function, + onMoveShouldSetResponderCapture: Function, + onResponderEnd: Function, + onResponderGrant: Function, + onResponderMove: Function, + onResponderReject: Function, + onResponderRelease: Function, + onResponderStart: Function, + onResponderTerminate: Function, + onResponderTerminationRequest: Function, + onStartShouldSetResponder: Function, + onStartShouldSetResponderCapture: Function, +}; + // Functions. export type NavigationActionCaller = Function;