Permalink
Browse files

Allow the disabling of navigation gestures.

Summary:
During development of an app I decided upon NavigationExperimental as I found it easily configurable with my Redux stores and make it work the way I wanted. One thing I found missing was the ability to decide if gestures were on or not for the card stack. In my case I need the gestures off as they conflict with what I am trying to do. This PR simply opens up the ability to turn the gestures for a CardStack off.

Testing was completed via UI Explorer. An additional example replicating the existing one with the new setting set to false was created. To ensure nothing broke I tested both the original example and new example to ensure gestures worked (and didn't work) when expected.

I did not see any unit tests around NavigationExperimental but if I simply missed them I would be more then happy to update/add any.
Closes #9505

Differential Revision: D3749880

Pulled By: ericvicenti

fbshipit-source-id: dfa42ff8b6c8b41490ad1efc931b364e47058243
  • Loading branch information...
1 parent ea7b2ef commit 0cf95056303b8690077ae8aa842f4d810bf3cbc9 @rmevans9 rmevans9 committed with Facebook Github Bot 0 Aug 21, 2016
@@ -0,0 +1,196 @@
+/**
+ * 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.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+'use strict';
+
+const NavigationExampleRow = require('./NavigationExampleRow');
+const React = require('react');
+const ReactNative = require('react-native');
+
+/**
+ * Basic example that shows how to use <NavigationCardStack /> to build
+ * an app with controlled navigation system but without gestures.
+ */
+const {
+ NavigationExperimental,
+ ScrollView,
+ StyleSheet,
+} = ReactNative;
+
+const {
+ CardStack: NavigationCardStack,
+ StateUtils: NavigationStateUtils,
+} = NavigationExperimental;
+
+// Step 1:
+// Define a component for your application.
+class YourApplication extends React.Component {
+
+ // This sets up the initial navigation state.
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = {
+ // This defines the initial navigation state.
+ navigationState: {
+ index: 0, // starts with first route focused.
+ routes: [{key: 'Welcome'}], // starts with only one route.
+ },
+ };
+
+ this._exit = this._exit.bind(this);
+ this._onNavigationChange = this._onNavigationChange.bind(this);
+ }
+
+ // User your own navigator (see Step 2).
+ render(): ReactElement {
+ return (
+ <YourNavigator
+ navigationState={this.state.navigationState}
+ onNavigationChange={this._onNavigationChange}
+ onExit={this._exit}
+ />
+ );
+ }
+
+ // This handles the navigation state changes. You're free and responsible
+ // to define the API that changes that navigation state. In this exmaple,
+ // we'd simply use a `function(type: string)` to update the navigation state.
+ _onNavigationChange(type: string): void {
+ let {navigationState} = this.state;
+ switch (type) {
+ case 'push':
+ // push a new route.
+ const route = {key: 'route-' + Date.now()};
+ navigationState = NavigationStateUtils.push(navigationState, route);
+ break;
+
+ case 'pop':
+ navigationState = NavigationStateUtils.pop(navigationState);
+ break;
+ }
+
+ // NavigationStateUtils gives you back the same `navigationState` if nothing
+ // has changed. You could use that to avoid redundant re-rendering.
+ if (this.state.navigationState !== navigationState) {
+ this.setState({navigationState});
+ }
+ }
+
+ // Exits the example. `this.props.onExampleExit` is provided
+ // by the UI Explorer.
+ _exit(): void {
+ this.props.onExampleExit && this.props.onExampleExit();
+ }
+
+ // This public method is optional. If exists, the UI explorer will call it
+ // the "back button" is pressed. Normally this is the cases for Android only.
+ handleBackAction(): boolean {
+ return this._onNavigationChange('pop');
+ }
+}
+
+// Step 2:
+// Define your own controlled navigator.
+//
+// +------------+
+// +-+ |
+// +-+ | |
+// | | | |
+// | | | Active |
+// | | | Scene |
+// | | | |
+// +-+ | |
+// +-+ |
+// +------------+
+//
+class YourNavigator extends React.Component {
+
+ // This sets up the methods (e.g. Pop, Push) for navigation.
+ constructor(props: any, context: any) {
+ super(props, context);
+
+ this._onPushRoute = this.props.onNavigationChange.bind(null, 'push');
+ this._onPopRoute = this.props.onNavigationChange.bind(null, 'pop');
+
+ this._renderScene = this._renderScene.bind(this);
+ }
+
+ // Now use the `NavigationCardStack` to render the scenes.
+ render(): ReactElement {
+ return (
+ <NavigationCardStack
+ onNavigateBack={this._onPopRoute}
+ navigationState={this.props.navigationState}
+ renderScene={this._renderScene}
+ style={styles.navigator}
+ enableGestures={false}
+ />
+ );
+ }
+
+ // Render a scene for route.
+ // The detailed spec of `sceneProps` is defined at `NavigationTypeDefinition`
+ // as type `NavigationSceneRendererProps`.
+ _renderScene(sceneProps: Object): ReactElement {
+ return (
+ <YourScene
+ route={sceneProps.scene.route}
+ onPushRoute={this._onPushRoute}
+ onPopRoute={this._onPopRoute}
+ onExit={this.props.onExit}
+ />
+ );
+ }
+}
+
+// Step 3:
+// Define your own scene.
+class YourScene extends React.Component {
+ render() {
+ return (
+ <ScrollView>
+ <NavigationExampleRow
+ text={'route = ' + this.props.route.key}
+ />
+ <NavigationExampleRow
+ text="Push Route"
+ onPress={this.props.onPushRoute}
+ />
+ <NavigationExampleRow
+ text="Pop Route"
+ onPress={this.props.onPopRoute}
+ />
+ <NavigationExampleRow
+ text="Exit Card Stack Example"
+ onPress={this.props.onExit}
+ />
+ </ScrollView>
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ navigator: {
+ flex: 1,
+ },
+});
+
+module.exports = YourApplication;
@@ -35,6 +35,7 @@ const View = require('View');
const EXAMPLES = {
'CardStack + Header + Tabs Example': require('./NavigationCardStack-NavigationHeader-Tabs-example'),
'CardStack Example': require('./NavigationCardStack-example'),
+ 'CardStack Without Gestures Example': require('./NavigationCardStack-NoGesture-example'),
'Transitioner + Animated View Example': require('./NavigationTransitioner-AnimatedView-example'),
'Transitioner + Animated View Pager Example': require('./NavigationTransitioner-AnimatedView-pager-example'),
};
@@ -64,10 +64,12 @@ type Props = {
cardStyle?: any,
style: any,
gestureResponseDistance?: ?number,
+ enableGestures: ?boolean
};
type DefaultProps = {
direction: NavigationGestureDirection,
+ enableGestures: boolean
};
/**
@@ -143,6 +145,11 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
gestureResponseDistance: PropTypes.number,
/**
+ * Enable gestures. Default value is true
+ */
+ enableGestures: PropTypes.bool,
+
+ /**
* The controlled navigation state. Typically, the navigation state
* look like this:
*
@@ -183,6 +190,7 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
static defaultProps: DefaultProps = {
direction: Directions.HORIZONTAL,
+ enableGestures: true,
};
constructor(props: Props, context: any) {
@@ -236,14 +244,18 @@ class NavigationCardStack extends React.Component<DefaultProps, Props, void> {
NavigationCardStackStyleInterpolator.forVertical(props) :
NavigationCardStackStyleInterpolator.forHorizontal(props);
- const panHandlersProps = {
- ...props,
- onNavigateBack: this.props.onNavigateBack,
- gestureResponseDistance: this.props.gestureResponseDistance,
- };
- const panHandlers = isVertical ?
- NavigationCardStackPanResponder.forVertical(panHandlersProps) :
- NavigationCardStackPanResponder.forHorizontal(panHandlersProps);
+ let panHandlers = null;
+
+ if (this.props.enableGestures) {
+ const panHandlersProps = {
+ ...props,
+ onNavigateBack: this.props.onNavigateBack,
+ gestureResponseDistance: this.props.gestureResponseDistance,
+ };
+ panHandlers = isVertical ?
+ NavigationCardStackPanResponder.forVertical(panHandlersProps) :
+ NavigationCardStackPanResponder.forHorizontal(panHandlersProps);
+ }
return (
<NavigationCard

0 comments on commit 0cf9505

Please sign in to comment.