feat: add swipe handler to entire bottom sheet

This commit is contained in:
Bettenbuk Zoltan 2019-12-12 14:52:41 +01:00 committed by Saúl Ibarra Corretgé
parent 7b9abd34a0
commit 9b60537e0f
5 changed files with 108 additions and 36 deletions

5
package-lock.json generated
View File

@ -14873,11 +14873,6 @@
}
}
},
"react-native-swipe-gestures": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.4.tgz",
"integrity": "sha512-C/vz0KPHNyqHk3uF4Cz/jzd/0N8z34ZgsjAZUh/RsXPH2FtJJf3Fw73pQDWJSoCMtvVadlztb8xQ+/aEQrll7w=="
},
"react-native-swipeout": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz",

View File

@ -77,7 +77,6 @@
"react-native-sound": "0.11.0",
"react-native-svg": "9.7.1",
"react-native-svg-transformer": "0.13.0",
"react-native-swipe-gestures": "1.0.4",
"react-native-swipeout": "2.3.6",
"react-native-watch-connectivity": "0.2.0",
"react-native-webrtc": "1.75.2",

View File

@ -1,7 +1,7 @@
// @flow
import React, { PureComponent, type Node } from 'react';
import { SafeAreaView, ScrollView, View } from 'react-native';
import { PanResponder, SafeAreaView, ScrollView, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../color-scheme';
import { SlidingView } from '../../../react';
@ -10,6 +10,16 @@ import { StyleType } from '../../../styles';
import { bottomSheetStyles as styles } from './styles';
/**
* Minimal distance that needs to be moved by the finger to consider it a swipe.
*/
const GESTURE_DISTANCE_THRESHOLD = 5;
/**
* The minimal speed needed to be achieved by the finger to consider it as a swipe.
*/
const GESTURE_SPEED_THRESHOLD = 0.2;
/**
* The type of {@code BottomSheet}'s React {@code Component} prop types.
*/
@ -31,6 +41,11 @@ type Props = {
*/
onCancel: ?Function,
/**
* Callback to be attached to the custom swipe event of the BottomSheet.
*/
onSwipe?: Function,
/**
* Function to render a bottom sheet header element, if necessary.
*/
@ -41,6 +56,23 @@ type Props = {
* A component emulating Android's BottomSheet.
*/
class BottomSheet extends PureComponent<Props> {
panResponder: Object;
/**
* Instantiates a new component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: this._onShouldSetResponder.bind(this),
onMoveShouldSetPanResponder: this._onShouldSetResponder.bind(this),
onPanResponderRelease: this._onGestureEnd.bind(this)
});
}
/**
* Implements React's {@link Component#render()}.
*
@ -66,7 +98,8 @@ class BottomSheet extends PureComponent<Props> {
style = { [
styles.sheetItemContainer,
_styles.sheet
] }>
] }
{ ...this.panResponder.panHandlers }>
<ScrollView
bounces = { false }
showsVerticalScrollIndicator = { false }
@ -78,6 +111,48 @@ class BottomSheet extends PureComponent<Props> {
</SlidingView>
);
}
/**
* Callback to handle a gesture end event.
*
* @param {Object} evt - The native gesture event.
* @param {Object} gestureState - The gesture state.
* @returns {void}
*/
_onGestureEnd(evt, gestureState) {
const verticalSwipe = Math.abs(gestureState.vy) > Math.abs(gestureState.vx)
&& Math.abs(gestureState.vy) > GESTURE_SPEED_THRESHOLD;
if (verticalSwipe) {
const direction = gestureState.vy > 0 ? 'down' : 'up';
const { onCancel, onSwipe } = this.props;
let isSwipeHandled = false;
if (onSwipe) {
isSwipeHandled = onSwipe(direction);
}
if (direction === 'down' && !isSwipeHandled) {
// Swipe down is a special gesture that can be used to close the
// BottomSheet, so if the swipe is not handled by the parent
// component, we consider it as a request to close.
onCancel && onCancel();
}
}
}
/**
* Returns true if the pan responder should activate, false otherwise.
*
* @param {Object} evt - The native gesture event.
* @param {Object} gestureState - The gesture state.
* @returns {boolean}
*/
_onShouldSetResponder({ nativeEvent }, gestureState) {
return nativeEvent.touches.length === 1
&& Math.abs(gestureState.dx) > GESTURE_DISTANCE_THRESHOLD
&& Math.abs(gestureState.dy) > GESTURE_DISTANCE_THRESHOLD;
}
}
/**

View File

@ -1,9 +1,8 @@
// @flow
import React, { PureComponent } from 'react';
import { Platform, TouchableOpacity } from 'react-native';
import { Platform, TouchableOpacity, View } from 'react-native';
import Collapsible from 'react-native-collapsible';
import GestureRecognizer, { swipeDirections } from 'react-native-swipe-gestures';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
@ -59,6 +58,11 @@ type Props = {
type State = {
/**
* True if the bottom scheet is scrolled to the top.
*/
scrolledToTop: boolean,
/**
* True if the 'more' button set needas to be rendered.
*/
@ -88,6 +92,7 @@ class OverflowMenu extends PureComponent<Props, State> {
super(props);
this.state = {
scrolledToTop: true,
showMore: false
};
@ -117,6 +122,7 @@ class OverflowMenu extends PureComponent<Props, State> {
return (
<BottomSheet
onCancel = { this._onCancel }
onSwipe = { this._onSwipe }
renderHeader = { this._renderMenuExpandToggle }>
<AudioRouteButton { ...buttonProps } />
<ToggleCameraButton { ...buttonProps } />
@ -152,12 +158,7 @@ class OverflowMenu extends PureComponent<Props, State> {
*/
_renderMenuExpandToggle() {
return (
<GestureRecognizer
config = {{
velocityThreshold: 0.1,
directionalOffsetThreshold: 30
}}
onSwipe = { this._onSwipe }
<View
style = { [
this.props._bottomSheetStyles.sheet,
styles.expandMenuContainer
@ -166,7 +167,7 @@ class OverflowMenu extends PureComponent<Props, State> {
{ /* $FlowFixMeProps */ }
<IconDragHandle style = { this.props._bottomSheetStyles.expandIcon } />
</TouchableOpacity>
</GestureRecognizer>
</View>
);
}
@ -188,34 +189,31 @@ class OverflowMenu extends PureComponent<Props, State> {
return false;
}
_onSwipe: (string) => void;
_onSwipe: string => void;
/**
* Callback to be invoked when a swipe gesture is detected on the menu.
* Callback to be invoked when swipe gesture is detected on the menu. Returns true
* if the swipe gesture is handled by the menu, false otherwise.
*
* @param {string} gestureName - The name of the swipe gesture.
* @returns {void}
* @param {string} direction - Direction of 'up' or 'down'.
* @returns {boolean}
*/
_onSwipe(gestureName) {
_onSwipe(direction) {
const { showMore } = this.state;
switch (gestureName) {
case swipeDirections.SWIPE_UP:
switch (direction) {
case 'up':
!showMore && this.setState({
showMore: true
});
break;
case swipeDirections.SWIPE_DOWN:
if (showMore) {
// If the menu is expanded, we collapse it.
this.setState({
showMore: false
});
} else {
// If the menu is not expanded, we close the menu
this._onCancel();
}
break;
return !showMore;
case 'down':
showMore && this.setState({
showMore: false
});
return showMore;
}
}

View File

@ -60,6 +60,11 @@ const styles = {
flexDirection: 'column'
},
sheetGestureRecognizer: {
alignItems: 'stretch',
flexDirection: 'column'
},
/**
* The style of the toolbar.
*/