feat: add swipe handler to entire bottom sheet
This commit is contained in:
parent
7b9abd34a0
commit
9b60537e0f
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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({
|
||||
|
||||
return !showMore;
|
||||
case 'down':
|
||||
showMore && this.setState({
|
||||
showMore: false
|
||||
});
|
||||
} else {
|
||||
// If the menu is not expanded, we close the menu
|
||||
this._onCancel();
|
||||
}
|
||||
break;
|
||||
|
||||
return showMore;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,11 @@ const styles = {
|
|||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
sheetGestureRecognizer: {
|
||||
alignItems: 'stretch',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
/**
|
||||
* The style of the toolbar.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue