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 https://github.com/facebook/react-native/pull/9505 Differential Revision: D3749880 Pulled By: ericvicenti fbshipit-source-id: dfa42ff8b6c8b41490ad1efc931b364e47058243
This commit is contained in:
parent
ea7b2ef36b
commit
0cf9505630
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -142,6 +144,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
|
||||
|
|
Loading…
Reference in New Issue