From db7ac89f3c429947278c8715521e19e1da7be586 Mon Sep 17 00:00:00 2001 From: Gava97 Date: Thu, 6 Jun 2019 14:30:03 +0200 Subject: [PATCH 1/2] Backdrop enhancements Add support for custom actions on backdrop (onTouchStart, onTouchEnd) Add option for preventing default behaviour on actions onTouchStart and onTouchEnd --- SlidingUpPanel.js | 66 ++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/SlidingUpPanel.js b/SlidingUpPanel.js index 422833f..e548c18 100644 --- a/SlidingUpPanel.js +++ b/SlidingUpPanel.js @@ -14,7 +14,7 @@ import { import closest from './libs/closest' import measureElement from './libs/measureElement' import FlickAnimation from './libs/FlickAnimation' -import {statusBarHeight, visibleHeight} from './libs/layout' +import { statusBarHeight, visibleHeight } from './libs/layout' import * as Constants from './libs/constants' import styles from './libs/styles' @@ -47,9 +47,13 @@ class SlidingUpPanel extends React.PureComponent { onDragEnd: PropTypes.func, onMomentumDragStart: PropTypes.func, onMomentumDragEnd: PropTypes.func, + onBackdropTouchStart: PropTypes.func, + onBackdropTouchEnd: PropTypes.func, allowMomentum: PropTypes.bool, allowDragging: PropTypes.bool, showBackdrop: PropTypes.bool, + hideOnBackdropTap: PropTypes.bool, + stopFlickOnBackdropTap: PropTypes.bool, backdropOpacity: PropTypes.number, friction: PropTypes.number, containerStyle: PropTypes.any, @@ -60,19 +64,21 @@ class SlidingUpPanel extends React.PureComponent { static defaultProps = { height: usableHeight, animatedValue: new Animated.Value(0), - draggableRange: {top: usableHeight, bottom: 0}, + draggableRange: { top: usableHeight, bottom: 0 }, snappingPoints: [], minimumVelocityThreshold: Constants.DEFAULT_MINIMUM_VELOCITY_THRESHOLD, minimumDistanceThreshold: Constants.DEFAULT_MINIMUM_DISTANCE_THRESHOLD, avoidKeyboard: true, onBackButtonPress: null, - onDragStart: () => {}, - onDragEnd: () => {}, - onMomentumDragStart: () => {}, - onMomentumDragEnd: () => {}, + onDragStart: () => { }, + onDragEnd: () => { }, + onMomentumDragStart: () => { }, + onMomentumDragEnd: () => { }, allowMomentum: true, allowDragging: true, showBackdrop: true, + hideOnBackdropTap: true, + stopFlickOnBackdropTap: true, backdropOpacity: 0.75, friction: Constants.DEFAULT_FRICTION } @@ -117,7 +123,7 @@ class SlidingUpPanel extends React.PureComponent { this.hide = this.hide.bind(this) this.scrollIntoView = this.scrollIntoView.bind(this) - const {top, bottom} = this.props.draggableRange + const { top, bottom } = this.props.draggableRange const animatedValue = this.props.animatedValue.__getValue() const initialValue = clamp(animatedValue, bottom, top) @@ -126,7 +132,7 @@ class SlidingUpPanel extends React.PureComponent { this._initialDragPosition = initialValue this._backdropPointerEvents = this._isAtBottom(initialValue) ? 'none' : 'box-only' // prettier-ignore - this._flick = new FlickAnimation({max: top, min: bottom}) + this._flick = new FlickAnimation({ max: top, min: bottom }) this._flickAnimationListener = this._flick.onUpdate(value => { this.props.animatedValue.setValue(value) @@ -142,7 +148,7 @@ class SlidingUpPanel extends React.PureComponent { prevProps.draggableRange.top !== this.props.draggableRange.top || prevProps.draggableRange.bottom !== this.props.draggableRange.bottom ) { - const {top, bottom} = this.props.draggableRange + const { top, bottom } = this.props.draggableRange const animatedValue = this.props.animatedValue.__getValue() this._flick.setMax(top) @@ -204,7 +210,7 @@ class SlidingUpPanel extends React.PureComponent { } _onPanResponderMove(evt, gestureState) { - const {top, bottom} = this.props.draggableRange + const { top, bottom } = this.props.draggableRange const delta = this._initialDragPosition - gestureState.dy const newValue = clamp(delta, top, bottom) @@ -229,7 +235,7 @@ class SlidingUpPanel extends React.PureComponent { if (this.props.snappingPoints.length > 0) { this.props.onMomentumDragStart(animatedValue) - const {top, bottom} = this.props.draggableRange + const { top, bottom } = this.props.draggableRange const nextPoint = this._flick.predictNextPosition({ fromValue: animatedValue, velocity: gestureState.vy, @@ -278,7 +284,7 @@ class SlidingUpPanel extends React.PureComponent { this.props.onDragEnd(animatedValue, gestureState) } - _onAnimatedValueChange({value}) { + _onAnimatedValueChange({ value }) { const isAtBottom = this._isAtBottom(value) if (isAtBottom) { @@ -293,12 +299,12 @@ class SlidingUpPanel extends React.PureComponent { if (isAtBottom && this._backdropPointerEvents === 'box-only') { this._backdropPointerEvents = 'none' - this._backdrop.setNativeProps({pointerEvents: 'none'}) + this._backdrop.setNativeProps({ pointerEvents: 'none' }) } if (!isAtBottom && this._backdropPointerEvents === 'none') { this._backdropPointerEvents = 'box-only' - this._backdrop.setNativeProps({pointerEvents: 'box-only'}) + this._backdrop.setNativeProps({ pointerEvents: 'box-only' }) } } @@ -349,7 +355,7 @@ class SlidingUpPanel extends React.PureComponent { } _isInsideDraggableRange(value, gestureState) { - const {top, bottom} = this.props.draggableRange + const { top, bottom } = this.props.draggableRange if (gestureState.dy > 0) { return value > bottom @@ -359,7 +365,7 @@ class SlidingUpPanel extends React.PureComponent { } _isAtBottom(value) { - const {bottom} = this.props.draggableRange + const { bottom } = this.props.draggableRange return value <= bottom } @@ -385,8 +391,8 @@ class SlidingUpPanel extends React.PureComponent { return null } - const {top, bottom} = this.props.draggableRange - const { backdropStyle } = this.props + const { top, bottom } = this.props.draggableRange + const { backdropStyle, onBackdropTouchStart, onBackdropTouchEnd, stopFlickOnBackdropTap, hideOnBackdropTap } = this.props const backdropOpacity = this.props.animatedValue.interpolate({ inputRange: [bottom, top], @@ -399,8 +405,14 @@ class SlidingUpPanel extends React.PureComponent { key="backdrop" pointerEvents={this._backdropPointerEvents} ref={c => (this._backdrop = c)} - onTouchStart={() => this._flick.stop()} - onTouchEnd={() => this.hide()} + onTouchStart={() => { + if (onBackdropTouchStart) onBackdropTouchStart() + if (stopFlickOnBackdropTap) this._flick.stop() + }} + onTouchEnd={() => { + if (onBackdropTouchEnd) onBackdropTouchEnd() + if (hideOnBackdropTap) this.hide() + }} style={[styles.backdrop, backdropStyle, {opacity: backdropOpacity}]} /> ) @@ -419,7 +431,7 @@ class SlidingUpPanel extends React.PureComponent { extrapolate: 'clamp' }) - const transform = {transform: [{translateY}]} + const transform = { transform: [{ translateY }] } const animatedContainerStyles = [ styles.animatedContainer, @@ -456,20 +468,20 @@ class SlidingUpPanel extends React.PureComponent { show(mayBeValueOrOptions) { if (!mayBeValueOrOptions) { - const {top} = this.props.draggableRange - return this._triggerAnimation({toValue: top}) + const { top } = this.props.draggableRange + return this._triggerAnimation({ toValue: top }) } if (typeof mayBeValueOrOptions === 'object') { return this._triggerAnimation(mayBeValueOrOptions) } - return this._triggerAnimation({toValue: mayBeValueOrOptions}) + return this._triggerAnimation({ toValue: mayBeValueOrOptions }) } hide() { - const {bottom} = this.props.draggableRange - this._triggerAnimation({toValue: bottom}) + const { bottom } = this.props.draggableRange + this._triggerAnimation({ toValue: bottom }) } async scrollIntoView(node, options = {}) { @@ -480,7 +492,7 @@ class SlidingUpPanel extends React.PureComponent { // Stop any animation when the keyboard starts showing this._flick.stop() - const {y} = await measureElement(node) + const { y } = await measureElement(node) const extraMargin = options.keyboardExtraMargin || Constants.KEYBOARD_EXTRA_MARGIN // prettier-ignore const keyboardActualPos = this._keyboardYPosition - extraMargin From ae018da59a3a29940e4735ebfab3831bd966da46 Mon Sep 17 00:00:00 2001 From: Gava97 Date: Thu, 6 Jun 2019 14:31:06 +0200 Subject: [PATCH 2/2] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d8a71ef..66e1e83 100644 --- a/README.md +++ b/README.md @@ -77,11 +77,15 @@ animatedValue|Animated.Value|An **Animated.Value** number between the top and bo |showBackdrop|boolean|Controls the visibility of backdrop. Default `true`. |allowMomentum|boolean|If `false`, panel will not continue to move when you release your finger. |allowDragging|boolean|Default `true`. Setting this to `false` to disable dragging. +|stopFlickOnBackdropTap|boolean|Default `true`. Setting this to `false` to prevent the user to stop the flicking action while the container is sliding up. +|hideOnBackdropTap|boolean|Default `true`. Setting this to `false` to prevent the hide of the container when the user taps the backdrop. |onBackButtonPress|() => boolean|By default when you press back button (Android) the panel will be closed (Move to `bottom` position of `draggableRange`). Implement this function if you want to custom the behavior. Returning `true` means the event has been handled. |onDragStart|(position: number, gestureState: GestureState) => void|Called when the panel is about to start dragging. |onDragEnd|(position: number: gestureState: GestureState) => void|Called when you release your finger. |onMomentumDragStart|(position: number) => void|Called when the momentum drag starts. Works exactly the same way of [ScrollView#onMomentumScrollBegin](https://facebook.github.io/react-native/docs/scrollview#onmomentumscrollbegin). |onMomentumDragEnd|(position: number) => void|Called when the momentum drag ends. Works exactly the same way of [ScrollView#onMomentumScrollEnd](https://facebook.github.io/react-native/docs/scrollview#onmomentumscrollend). +|onBackdropTouchStart|() => void|Called when user taps the backdrop when the container is sliding up. +|onBackdropTouchEnd|() => void|Called when user taps the backdrop when the container is fully opened. |children|React.Element \| Function|Accepts passing a function as component. Invoked with `dragHandlers` (that can be passed into another View like this ``) when the panel is mounted. Useful when you want only a part of your content becomes the drag handler. A `gestureState` (is forwarded from `PanResponder'`s callbacks) object has the following: