diff --git a/packages/drawer/src/views/DrawerLayout.js b/packages/drawer/src/views/DrawerLayout.js deleted file mode 100644 index 7839d4ab..00000000 --- a/packages/drawer/src/views/DrawerLayout.js +++ /dev/null @@ -1,508 +0,0 @@ -/* eslint-disable */ - -// ######################################################### -// This is vendored from react-native-gesture-handler! -// ######################################################### - -// This component is based on RN's DrawerLayoutAndroid API -// -// It perhaps deserves to be put in a separate repo, but since it relies -// on react-native-gesture-handler library which isn't very popular at the -// moment I decided to keep it here for the time being. It will allow us -// to move faster and fix issues that may arise in gesture handler library -// that could be found when using the drawer component - -import React, { Component } from 'react'; -import { - Animated, - StyleSheet, - View, - Keyboard, - StatusBar, - I18nManager, -} from 'react-native'; -import invariant from '../utils/invariant'; - -import { - PanGestureHandler, - TapGestureHandler, - State, -} from 'react-native-gesture-handler'; - -const DRAG_TOSS = 0.05; - -const IDLE = 'Idle'; -const DRAGGING = 'Dragging'; -const SETTLING = 'Settling'; - -export type PropType = { - children: any, - drawerBackgroundColor?: string, - drawerPosition: 'left' | 'right', - drawerWidth: number, - keyboardDismissMode?: 'none' | 'on-drag', - onDrawerClose?: Function, - onDrawerOpen?: Function, - onDrawerStateChanged?: Function, - renderNavigationView: (progressAnimatedValue: any) => any, - useNativeAnimations: boolean, - - // brand new properties - drawerType: 'front' | 'back' | 'slide', - edgeWidth: number, - minSwipeDistance: number, - hideStatusBar?: boolean, - statusBarAnimation?: 'slide' | 'none' | 'fade', - overlayColor: string, - contentContainerStyle?: any, - - // Added in this fork, needs to be upstreamed - drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open', - - // Properties not yet supported - // onDrawerSlide?: Function -}; - -export type StateType = { - drawerShown: boolean, - dragX: any, - touchX: any, - drawerTranslation: any, - containerWidth: number, -}; - -export type EventType = { - stopPropagation: Function, -}; - -export type DrawerMovementOptionType = { - velocity?: number, -}; - -export default class DrawerLayout extends Component { - static defaultProps = { - drawerWidth: 200, - drawerPosition: 'left', - useNativeAnimations: true, - drawerType: 'front', - edgeWidth: 20, - minSwipeDistance: 3, - overlayColor: 'black', - drawerLockMode: 'unlocked', - }; - - static positions = { - Left: 'left', - Right: 'right', - }; - _openValue: ?Animated.Interpolation; - _onGestureEvent: any; - - constructor(props: PropType, context: any) { - super(props, context); - - const dragX = new Animated.Value(0); - const touchX = new Animated.Value(0); - const drawerTranslation = new Animated.Value(0); - - this.state = { - dragX, - touchX, - drawerTranslation, - drawerShown: false, - containerWidth: 0, - }; - - this._updateAnimatedEvent(props, this.state); - } - - componentWillUpdate(props: PropType, state: StateType) { - if ( - this.props.drawerPosition !== props.drawerPosition || - this.props.drawerWidth !== props.drawerWidth || - this.props.drawerType !== props.drawerType || - this.state.containerWidth !== state.containerWidth - ) { - this._updateAnimatedEvent(props, state); - } - } - - _updateAnimatedEvent = (props: PropType, state: StateType) => { - // Event definition is based on - const { drawerPosition, drawerWidth, drawerType } = props; - const { - dragX: dragXValue, - touchX: touchXValue, - drawerTranslation, - containerWidth, - } = state; - - let dragX = dragXValue; - let touchX = touchXValue; - - if (drawerPosition !== 'left') { - // Most of the code is written in a way to handle left-side drawer. - // In order to handle right-side drawer the only thing we need to - // do is to reverse events coming from gesture handler in a way they - // emulate left-side drawer gestures. E.g. dragX is simply -dragX, and - // touchX is calulcated by subtracing real touchX from the width of the - // container (such that when touch happens at the right edge the value - // is simply 0) - dragX = Animated.multiply(new Animated.Value(-1), dragXValue); - touchX = Animated.add( - new Animated.Value(containerWidth), - Animated.multiply(new Animated.Value(-1), touchXValue) - ); - touchXValue.setValue(containerWidth); - } else { - touchXValue.setValue(0); - } - - // While closing the drawer when user starts gesture outside of its area (in greyed - // out part of the window), we want the drawer to follow only once finger reaches the - // edge of the drawer. - // E.g. on the diagram below drawer is illustrate by X signs and the greyed out area by - // dots. The touch gesture starts at '*' and moves left, touch path is indicated by - // an arrow pointing left - // 1) +---------------+ 2) +---------------+ 3) +---------------+ 4) +---------------+ - // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........| - // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........| - // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........| - // |XXXXXXXX|......| |XXXXXXXX|.<-*..| |XXXXXXXX|<--*..| |XXXXX|<-----*..| - // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........| - // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........| - // |XXXXXXXX|......| |XXXXXXXX|......| |XXXXXXXX|......| |XXXXX|.........| - // +---------------+ +---------------+ +---------------+ +---------------+ - // - // For the above to work properly we define animated value that will keep start position - // of the gesture. Then we use that value to calculate how much we need to subtract from - // the dragX. If the gesture started on the greyed out area we take the distance from the - // edge of the drawer to the start position. Otherwise we don't subtract at all and the - // drawer be pulled back as soon as you start the pan. - // - // This is used only when drawerType is "front" - // - let translationX = dragX; - if (drawerType === 'front') { - const startPositionX = Animated.add( - touchX, - Animated.multiply(new Animated.Value(-1), dragX) - ); - - const dragOffsetFromOnStartPosition = startPositionX.interpolate({ - inputRange: [drawerWidth - 1, drawerWidth, drawerWidth + 1], - outputRange: [0, 0, 1], - }); - translationX = Animated.add(dragX, dragOffsetFromOnStartPosition); - } - - this._openValue = Animated.add(translationX, drawerTranslation).interpolate( - { - inputRange: [0, drawerWidth], - outputRange: [0, 1], - extrapolate: 'clamp', - } - ); - - this._onGestureEvent = Animated.event( - [{ nativeEvent: { translationX: dragXValue, x: touchXValue } }], - { useNativeDriver: props.useNativeAnimations } - ); - }; - - _handleContainerLayout = ({ nativeEvent }) => { - this.setState({ containerWidth: nativeEvent.layout.width }); - }; - - _emitStateChanged = (newState: string, drawerWillShow: boolean) => { - this.props.onDrawerStateChanged && - this.props.onDrawerStateChanged(newState, drawerWillShow); - }; - - _openingHandlerStateChange = ({ nativeEvent }) => { - if (nativeEvent.oldState === State.ACTIVE) { - this._handleRelease(nativeEvent); - } else if (nativeEvent.state === State.ACTIVE) { - this._emitStateChanged(DRAGGING, false); - if (this.props.keyboardDismissMode === 'on-drag') { - Keyboard.dismiss(); - } - if (this.props.hideStatusBar) { - StatusBar.setHidden(true, this.props.statusBarAnimation || 'slide'); - } - } - }; - - _onTapHandlerStateChange = ({ nativeEvent }) => { - if ( - this.state.drawerShown && - nativeEvent.oldState === State.ACTIVE && - this.props.drawerLockMode !== 'locked-open' - ) { - this.closeDrawer(); - } - }; - - _handleRelease = nativeEvent => { - const { drawerWidth, drawerPosition, drawerType } = this.props; - const { drawerShown, containerWidth } = this.state; - let { translationX: dragX, velocityX, x: touchX } = nativeEvent; - - if (drawerPosition !== 'left') { - // See description in _updateAnimatedEvent about why events are flipped - // for right-side drawer - dragX = -dragX; - touchX = containerWidth - touchX; - velocityX = -velocityX; - } - - const gestureStartX = touchX - dragX; - let dragOffsetBasedOnStart = 0; - - if (drawerType === 'front') { - dragOffsetBasedOnStart = - gestureStartX > drawerWidth ? gestureStartX - drawerWidth : 0; - } - - const startOffsetX = - dragX + dragOffsetBasedOnStart + (drawerShown ? drawerWidth : 0); - const projOffsetX = startOffsetX + DRAG_TOSS * velocityX; - - const shouldOpen = projOffsetX > drawerWidth / 2; - - if (shouldOpen) { - this._animateDrawer({ - fromValue: startOffsetX, - toValue: drawerWidth, - velocity: velocityX, - }); - } else { - this._animateDrawer({ - fromValue: startOffsetX, - toValue: 0, - velocity: velocityX, - }); - } - }; - - _animateDrawer = ({ - fromValue, - toValue, - velocity, - }: { - fromValue: number, - toValue: number, - velocity: number, - }) => { - this.state.dragX.setValue(0); - this.state.touchX.setValue( - this.props.drawerPosition === 'left' ? 0 : this.state.containerWidth - ); - - if (typeof fromValue === 'number') { - this.state.drawerTranslation.setValue(fromValue); - } - - const willShow = toValue !== 0; - this.setState({ drawerShown: willShow }); - this._emitStateChanged(SETTLING, willShow); - if (this.props.hideStatusBar) { - StatusBar.setHidden(willShow, this.props.statusBarAnimation || 'slide'); - } - Animated.spring(this.state.drawerTranslation, { - velocity, - bounciness: 0, - toValue, - useNativeDriver: this.props.useNativeAnimations, - }).start(({ finished }) => { - if (finished) { - this._emitStateChanged(IDLE, willShow); - if (willShow) { - this.props.onDrawerOpen && this.props.onDrawerOpen(); - } else { - this.props.onDrawerClose && this.props.onDrawerClose(); - } - } - }); - }; - - openDrawer = (options: DrawerMovementOptionType = {}) => { - this._animateDrawer({ - toValue: this.props.drawerWidth, - velocity: options.velocity ? options.velocity : 0, - }); - }; - - closeDrawer = (options: DrawerMovementOptionType = {}) => { - this._animateDrawer({ - toValue: 0, - velocity: options.velocity ? options.velocity : 0, - }); - }; - - _renderOverlay = () => { - /* Overlay styles */ - invariant(this._openValue, 'should be set'); - const overlayOpacity = this._openValue.interpolate({ - inputRange: [0, 1], - outputRange: [0, 0.7], - extrapolate: 'clamp', - }); - const dynamicOverlayStyles = { - opacity: overlayOpacity, - backgroundColor: this.props.overlayColor, - }; - return ( - - - - ); - }; - - _renderDrawer = () => { - const { drawerShown } = this.state; - const { - drawerBackgroundColor, - drawerWidth, - drawerPosition, - drawerType, - contentContainerStyle, - } = this.props; - - const fromLeft = drawerPosition === 'left'; - const drawerSlide = drawerType !== 'back'; - const containerSlide = drawerType !== 'front'; - - const dynamicDrawerStyles = { - backgroundColor: drawerBackgroundColor, - width: drawerWidth, - }; - const openValue = this._openValue; - invariant(openValue, 'should be set'); - - let containerStyles; - if (containerSlide) { - const containerTranslateX = openValue.interpolate({ - inputRange: [0, 1], - outputRange: fromLeft ? [0, drawerWidth] : [0, -drawerWidth], - extrapolate: 'clamp', - }); - containerStyles = { - transform: [{ translateX: containerTranslateX }], - }; - } - - let drawerTranslateX = 0; - if (drawerSlide) { - const closedDrawerOffset = fromLeft ? -drawerWidth : drawerWidth; - drawerTranslateX = openValue.interpolate({ - inputRange: [0, 1], - outputRange: [closedDrawerOffset, 0], - extrapolate: 'clamp', - }); - } - const drawerStyles = { - transform: [{ translateX: drawerTranslateX }], - flexDirection: (I18nManager.isRTL - ? !fromLeft - : fromLeft) - ? 'row' - : 'row-reverse', - }; - - return ( - - - {typeof this.props.children === 'function' - ? this.props.children(this._openValue) - : this.props.children} - {this._renderOverlay()} - - - - {this.props.renderNavigationView(this._openValue)} - - - - ); - }; - - render() { - const { drawerShown, containerWidth } = this.state; - - const { - drawerPosition, - drawerType, - drawerLockMode, - edgeWidth, - minSwipeDistance, - } = this.props; - - const fromLeft = drawerPosition === 'left'; - - // gestureOrientation is 1 if the expected gesture is from left to right and -1 otherwise - // e.g. when drawer is on the left and is closed we expect left to right gesture, thus - // orientation will be 1. - const gestureOrientation = (fromLeft ? 1 : -1) * (drawerShown ? -1 : 1); - - // When drawer is closed we want the hitSlop to be horizontally shorter - // than the container size by the value of SLOP. This will make it only - // activate when gesture happens not further than SLOP away from the edge - const hitSlop = fromLeft - ? { right: drawerShown ? 0 : edgeWidth - containerWidth } - : { left: drawerShown ? 0 : edgeWidth - containerWidth }; - - return ( - - {this._renderDrawer()} - - ); - } -} - -const styles = StyleSheet.create({ - drawer: { flex: 0 }, - drawerContainer: { - ...StyleSheet.absoluteFillObject, - zIndex: 1001, - flexDirection: 'row', - }, - containerInFront: { - ...StyleSheet.absoluteFillObject, - zIndex: 1002, - }, - containerOnBack: { - ...StyleSheet.absoluteFillObject, - }, - main: { - flex: 1, - zIndex: 0, - overflow: 'hidden', - }, - overlay: { - ...StyleSheet.absoluteFillObject, - zIndex: 1000, - }, -}); diff --git a/packages/drawer/src/views/DrawerView.js b/packages/drawer/src/views/DrawerView.js index be4a4981..8d319a1e 100644 --- a/packages/drawer/src/views/DrawerView.js +++ b/packages/drawer/src/views/DrawerView.js @@ -1,10 +1,10 @@ import React from 'react'; import { Dimensions, StyleSheet } from 'react-native'; import { SceneView } from '@react-navigation/core'; +import DrawerLayout from 'react-native-gesture-handler/DrawerLayout'; import { ScreenContainer } from 'react-native-screens'; import DrawerActions from '../routers/DrawerActions'; -import DrawerLayout from './DrawerLayout'; import DrawerSidebar from './DrawerSidebar'; import DrawerGestureContext from '../utils/DrawerGestureContext'; import ResourceSavingScene from '../views/ResourceSavingScene'; @@ -195,6 +195,10 @@ export default class DrawerView extends React.PureComponent { } }; + _setDrawerGestureRef = ref => { + this.drawerGestureRef.current = ref; + }; + render() { const { navigation } = this.props; const activeKey = navigation.state.routes[navigation.state.index].key; @@ -205,7 +209,7 @@ export default class DrawerView extends React.PureComponent { ref={c => { this._drawer = c; }} - gestureRef={this.drawerGestureRef} + onGestureRef={this._setDrawerGestureRef} drawerLockMode={ drawerLockMode || (this.props.screenProps && this.props.screenProps.drawerLockMode) ||