From 4bdd06a71a06e71dceebbd920d176caec0e265e5 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 1 May 2019 23:58:06 +0200 Subject: [PATCH] refactor: rewrite drawer layout with reanimated (#60) --- packages/drawer/src/index.tsx | 10 +- .../src/navigators/createDrawerNavigator.tsx | 6 +- packages/drawer/src/types.tsx | 4 - packages/drawer/src/views/Drawer.tsx | 586 ++++++++++++++++++ packages/drawer/src/views/DrawerView.tsx | 229 +++---- 5 files changed, 678 insertions(+), 157 deletions(-) create mode 100644 packages/drawer/src/views/Drawer.tsx diff --git a/packages/drawer/src/index.tsx b/packages/drawer/src/index.tsx index 1f4f091c..0537142a 100644 --- a/packages/drawer/src/index.tsx +++ b/packages/drawer/src/index.tsx @@ -1,15 +1,15 @@ +import * as DrawerAcions from './routers/DrawerActions'; + /** * Navigators */ -/** - * Router - */ -import * as DrawerAcions from './routers/DrawerActions'; - export { default as createDrawerNavigator, } from './navigators/createDrawerNavigator'; +/** + * Router + */ export { DrawerAcions }; export { default as DrawerRouter } from './routers/DrawerRouter'; diff --git a/packages/drawer/src/navigators/createDrawerNavigator.tsx b/packages/drawer/src/navigators/createDrawerNavigator.tsx index 383e6cdc..a318d6ec 100644 --- a/packages/drawer/src/navigators/createDrawerNavigator.tsx +++ b/packages/drawer/src/navigators/createDrawerNavigator.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Dimensions, Platform, ScrollView } from 'react-native'; +import { Dimensions, Platform, ScrollView, I18nManager } from 'react-native'; import { createNavigator } from '@react-navigation/core'; import { SafeAreaView } from '@react-navigation/native'; import DrawerRouter from '../routers/DrawerRouter'; @@ -35,14 +35,12 @@ const DefaultDrawerConfig = { return Math.min(smallerAxisSize - appBarHeight, maxWidth); }, contentComponent: defaultContentComponent, - drawerPosition: 'left', + drawerPosition: I18nManager.isRTL ? 'right' : 'left', keyboardDismissMode: 'on-drag', drawerBackgroundColor: 'white', - useNativeAnimations: true, drawerType: 'front', hideStatusBar: false, statusBarAnimation: 'slide', - overlayColor: 'black', }; const DrawerNavigator = (routeConfigs: object, config: any = {}) => { diff --git a/packages/drawer/src/types.tsx b/packages/drawer/src/types.tsx index a04d64a7..a077deb2 100644 --- a/packages/drawer/src/types.tsx +++ b/packages/drawer/src/types.tsx @@ -17,10 +17,6 @@ export type Navigation = { key: string; index: number; routes: Route[]; - openId: string; - closeId: string; - toggleId: string; - isDrawerIdle: boolean; isDrawerOpen: boolean; }; openDrawer: () => void; diff --git a/packages/drawer/src/views/Drawer.tsx b/packages/drawer/src/views/Drawer.tsx new file mode 100644 index 00000000..a41a44f5 --- /dev/null +++ b/packages/drawer/src/views/Drawer.tsx @@ -0,0 +1,586 @@ +import * as React from 'react'; +import { + StyleSheet, + ViewStyle, + LayoutChangeEvent, + I18nManager, + Platform, + Keyboard, + StatusBar, +} from 'react-native'; +import { + PanGestureHandler, + TapGestureHandler, + State, + TapGestureHandlerStateChangeEvent, +} from 'react-native-gesture-handler'; +import Animated from 'react-native-reanimated'; + +const { + Clock, + Value, + onChange, + clockRunning, + startClock, + stopClock, + interpolate, + spring, + abs, + add, + and, + block, + call, + cond, + divide, + eq, + event, + greaterThan, + lessThan, + max, + min, + multiply, + neq, + or, + set, + sub, +} = Animated; + +const TRUE = 1; +const FALSE = 0; +const NOOP = 0; +const UNSET = -1; + +const PROGRESS_EPSILON = 0.05; + +const DIRECTION_LEFT = 1; +const DIRECTION_RIGHT = -1; + +const SWIPE_DISTANCE_THRESHOLD_DEFAULT = 60; + +const SWIPE_DISTANCE_MINIMUM = 5; + +const SPRING_CONFIG = { + damping: 30, + mass: 0.5, + stiffness: 150, + overshootClamping: true, + restSpeedThreshold: 0.001, + restDisplacementThreshold: 0.001, +}; + +type Binary = 0 | 1; + +type Renderer = (props: { progress: Animated.Node }) => React.ReactNode; + +type Props = { + open: boolean; + onOpen: () => void; + onClose: () => void; + onGestureRef?: (ref: PanGestureHandler | null) => void; + locked: boolean; + drawerPosition: 'left' | 'right'; + drawerType: 'front' | 'back' | 'slide'; + keyboardDismissMode: 'none' | 'on-drag'; + swipeEdgeWidth: number; + swipeDistanceThreshold?: number; + swipeVelocityThreshold: number; + hideStatusBar: boolean; + statusBarAnimation: 'slide' | 'none' | 'fade'; + overlayStyle?: ViewStyle; + drawerStyle?: ViewStyle; + contentContainerStyle?: ViewStyle; + renderDrawerContent: Renderer; + renderSceneContent: Renderer; +}; + +export default class DrawerView extends React.PureComponent { + static defaultProps = { + locked: false, + drawerPostion: I18nManager.isRTL ? 'left' : 'right', + drawerType: 'front', + swipeEdgeWidth: 32, + swipeVelocityThreshold: 500, + keyboardDismissMode: 'on-drag', + hideStatusBar: false, + statusBarAnimation: 'slide', + }; + + componentDidUpdate(prevProps: Props) { + const { + open, + drawerPosition, + drawerType, + swipeDistanceThreshold, + swipeVelocityThreshold, + hideStatusBar, + } = this.props; + + if ( + // If we're not in the middle of a transition, sync the drawer's open state + typeof this.pendingOpenValue !== 'boolean' || + open !== this.pendingOpenValue + ) { + this.toggleDrawer(open); + } + + this.pendingOpenValue = undefined; + + if (open !== prevProps.open && hideStatusBar) { + this.toggleStatusBar(open); + } + + if (prevProps.drawerPosition !== drawerPosition) { + this.drawerPosition.setValue( + drawerPosition === 'right' ? DIRECTION_RIGHT : DIRECTION_LEFT + ); + } + + if (prevProps.drawerType !== drawerType) { + this.isDrawerTypeFront.setValue(drawerType === 'front' ? TRUE : FALSE); + } + + if (prevProps.swipeDistanceThreshold !== swipeDistanceThreshold) { + this.swipeDistanceThreshold.setValue( + swipeDistanceThreshold !== undefined + ? swipeDistanceThreshold + : SWIPE_DISTANCE_THRESHOLD_DEFAULT + ); + } + + if (prevProps.swipeVelocityThreshold !== swipeVelocityThreshold) { + this.swipeVelocityThreshold.setValue(swipeVelocityThreshold); + } + } + + componentWillUnmount() { + this.toggleStatusBar(false); + } + + private clock = new Clock(); + + private isDrawerTypeFront = new Value( + this.props.drawerType === 'front' ? TRUE : FALSE + ); + + private isOpen = new Value(this.props.open ? TRUE : FALSE); + private nextIsOpen = new Value(UNSET); + private isSwiping = new Value(FALSE); + + private gestureState = new Value(State.UNDETERMINED); + private touchX = new Value(0); + private velocityX = new Value(0); + private gestureX = new Value(0); + private offsetX = new Value(0); + private position = new Value(0); + + private containerWidth = new Value(0); + private drawerWidth = new Value(0); + private drawerOpacity = new Value(0); + private drawerPosition = new Value( + this.props.drawerPosition === 'right' ? DIRECTION_RIGHT : DIRECTION_LEFT + ); + + // Comment stolen from react-native-gesture-handler/DrawerLayout + // + // 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" + private touchDistanceFromDrawer = cond( + this.isDrawerTypeFront, + cond( + eq(this.drawerPosition, DIRECTION_LEFT), + max( + // Distance of touch start from left screen edge - Drawer width + sub(sub(this.touchX, this.gestureX), this.drawerWidth), + 0 + ), + min( + multiply( + // Distance of drawer from left screen edge - Touch start point + sub( + sub(this.containerWidth, this.drawerWidth), + sub(this.touchX, this.gestureX) + ), + DIRECTION_RIGHT + ), + 0 + ) + ), + 0 + ); + + private swipeDistanceThreshold = new Value( + this.props.swipeDistanceThreshold !== undefined + ? this.props.swipeDistanceThreshold + : SWIPE_DISTANCE_THRESHOLD_DEFAULT + ); + private swipeVelocityThreshold = new Value( + this.props.swipeVelocityThreshold + ); + + private currentOpenValue: boolean = this.props.open; + private pendingOpenValue: boolean | undefined; + + private isStatusBarHidden: boolean = false; + + private transitionTo = (isOpen: number | Animated.Node) => { + const toValue = new Value(0); + const frameTime = new Value(0); + + const state = { + position: this.position, + time: new Value(0), + finished: new Value(FALSE), + }; + + return block([ + cond(clockRunning(this.clock), NOOP, [ + // Animation wasn't running before + // Set the initial values and start the clock + set(toValue, multiply(isOpen, this.drawerWidth, this.drawerPosition)), + set(frameTime, 0), + set(state.time, 0), + set(state.finished, FALSE), + set(this.isOpen, isOpen), + startClock(this.clock), + ]), + spring( + this.clock, + { ...state, velocity: this.velocityX }, + { ...SPRING_CONFIG, toValue } + ), + cond(state.finished, [ + // Reset gesture and velocity from previous gesture + set(this.touchX, 0), + set(this.gestureX, 0), + set(this.velocityX, 0), + set(this.offsetX, 0), + // When the animation finishes, stop the clock + stopClock(this.clock), + call([this.isOpen], ([value]: ReadonlyArray) => { + const open = Boolean(value); + + if (open !== this.props.open) { + // Sync drawer's state after animation finished + // This shouldn't be necessary, but there seems to be an issue on iOS + this.toggleDrawer(this.props.open); + } + }), + ]), + ]); + }; + + private dragX = block([ + onChange( + this.isOpen, + call([this.isOpen], ([value]: ReadonlyArray) => { + const open = Boolean(value); + + this.currentOpenValue = open; + + // Without this check, the drawer can go to an infinite update <-> animate loop for sync updates + if (open !== this.props.open) { + // If the mode changed, update state + if (open) { + this.props.onOpen(); + } else { + this.props.onClose(); + } + + this.pendingOpenValue = open; + + // Force componentDidUpdate to fire, whether user does a setState or not + // This allows us to detect when the user drops the update and revert back + // It's necessary to make sure that the state stays in sync + this.forceUpdate(); + } + }) + ), + onChange( + this.nextIsOpen, + cond(neq(this.nextIsOpen, UNSET), [ + // Stop any running animations + cond(clockRunning(this.clock), stopClock(this.clock)), + // Update the open value to trigger the transition + set(this.isOpen, this.nextIsOpen), + set(this.nextIsOpen, UNSET), + ]) + ), + // This block must be after the this.isOpen listener since we check for current value + onChange( + this.isSwiping, + // Listen to updates for this value only when it changes + // Without `onChange`, this will fire even if the value didn't change + // We don't want to call the listeners if the value didn't change + call([this.isSwiping], ([value]: ReadonlyArray) => { + const { keyboardDismissMode } = this.props; + + if (value === TRUE) { + if (keyboardDismissMode === 'on-drag') { + Keyboard.dismiss(); + } + + this.toggleStatusBar(true); + } else { + this.toggleStatusBar(this.currentOpenValue); + } + }) + ), + cond( + eq(this.gestureState, State.ACTIVE), + [ + cond(this.isSwiping, NOOP, [ + // We weren't dragging before, set it to true + set(this.isSwiping, TRUE), + // Also update the drag offset to the last position + set(this.offsetX, this.position), + ]), + // Update position with previous offset + gesture distance + set( + this.position, + add(this.offsetX, this.gestureX, this.touchDistanceFromDrawer) + ), + // Stop animations while we're dragging + stopClock(this.clock), + ], + [ + set(this.isSwiping, FALSE), + set(this.touchX, 0), + this.transitionTo( + cond( + or( + and( + greaterThan(abs(this.gestureX), SWIPE_DISTANCE_MINIMUM), + greaterThan(abs(this.velocityX), this.swipeVelocityThreshold) + ), + greaterThan(abs(this.gestureX), this.swipeDistanceThreshold) + ), + cond( + eq(this.drawerPosition, DIRECTION_LEFT), + // If swiped to right, open the drawer, otherwise close it + greaterThan( + cond(eq(this.velocityX, 0), this.gestureX, this.velocityX), + 0 + ), + // If swiped to left, open the drawer, otherwise close it + lessThan( + cond(eq(this.velocityX, 0), this.gestureX, this.velocityX), + 0 + ) + ), + this.isOpen + ) + ), + ] + ), + this.position, + ]); + + private translateX = cond( + eq(this.drawerPosition, DIRECTION_RIGHT), + min(max(multiply(this.drawerWidth, -1), this.dragX), 0), + max(min(this.drawerWidth, this.dragX), 0) + ); + + private progress = cond( + // Check if the drawer width is available to avoid division by zero + eq(this.drawerWidth, 0), + 0, + abs(divide(this.translateX, this.drawerWidth)) + ); + + private handleGestureEvent = event([ + { + nativeEvent: { + x: this.touchX, + translationX: this.gestureX, + velocityX: this.velocityX, + state: this.gestureState, + }, + }, + ]); + + private handleTapStateChange = ({ + nativeEvent, + }: TapGestureHandlerStateChangeEvent) => { + if (nativeEvent.oldState === State.ACTIVE && !this.props.locked) { + this.toggleDrawer(false); + } + }; + + private handleContainerLayout = (e: LayoutChangeEvent) => + this.containerWidth.setValue(e.nativeEvent.layout.width); + + private handleDrawerLayout = (e: LayoutChangeEvent) => { + this.drawerWidth.setValue(e.nativeEvent.layout.width); + this.toggleDrawer(this.props.open); + + // Until layout is available, drawer is hidden with opacity: 0 by default + // Show it in the next frame when layout is available + // If we don't delay it until the next frame, there's a visible flicker + requestAnimationFrame(() => this.drawerOpacity.setValue(1)); + }; + + private toggleDrawer = (open: boolean) => { + this.nextIsOpen.setValue(open ? TRUE : FALSE); + + // This value will also be set shortly after as changing this.nextIsOpen changes this.isOpen + // However, there's a race condition on Android, so we need to set a bit earlier + this.currentOpenValue = open; + }; + + private toggleStatusBar = (hidden: boolean) => { + const { hideStatusBar, statusBarAnimation } = this.props; + + if (hideStatusBar && this.isStatusBarHidden !== hidden) { + this.isStatusBarHidden = hidden; + StatusBar.setHidden(hidden, statusBarAnimation); + } + }; + + render() { + const { + open, + locked, + drawerPosition, + drawerType, + swipeEdgeWidth, + contentContainerStyle, + drawerStyle, + overlayStyle, + onGestureRef, + renderDrawerContent, + renderSceneContent, + } = this.props; + + const right = drawerPosition === 'right'; + + const contentTranslateX = drawerType === 'front' ? 0 : this.translateX; + const drawerTranslateX = + drawerType === 'back' + ? I18nManager.isRTL + ? multiply(this.drawerWidth, DIRECTION_RIGHT) + : this.drawerWidth + : this.translateX; + + const offset = I18nManager.isRTL ? '100%' : multiply(this.drawerWidth, -1); + + // FIXME: Currently hitSlop is broken when on Android when drawer is on right + // https://github.com/kmagiera/react-native-gesture-handler/issues/569 + const hitSlop = right + ? // Extend hitSlop to the side of the screen when drawer is closed + // This lets the user drag the drawer from the side of the screen + { right: 0, width: open ? undefined : swipeEdgeWidth } + : { left: 0, width: open ? undefined : swipeEdgeWidth }; + + return ( + + + + {renderSceneContent({ progress: this.progress })} + + + + + + {renderDrawerContent({ progress: this.progress })} + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + backgroundColor: 'white', + position: 'absolute', + top: 0, + bottom: 0, + width: '80%', + maxWidth: '100%', + }, + overlay: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + }, + content: { + flex: 1, + }, + main: { + flex: 1, + overflow: 'hidden', + }, +}); diff --git a/packages/drawer/src/views/DrawerView.tsx b/packages/drawer/src/views/DrawerView.tsx index 56610325..c00732c2 100644 --- a/packages/drawer/src/views/DrawerView.tsx +++ b/packages/drawer/src/views/DrawerView.tsx @@ -1,30 +1,28 @@ import * as React from 'react'; -import { Dimensions, StyleSheet, ViewStyle, Animated } from 'react-native'; +import { Dimensions, StyleSheet, ViewStyle } from 'react-native'; import { SceneView } from '@react-navigation/core'; -import DrawerLayout from 'react-native-gesture-handler/DrawerLayout'; import { ScreenContainer } from 'react-native-screens'; import * as DrawerActions from '../routers/DrawerActions'; import DrawerSidebar, { ContentComponentProps } from './DrawerSidebar'; import DrawerGestureContext from '../utils/DrawerGestureContext'; -import ResourceSavingScene from '../views/ResourceSavingScene'; +import ResourceSavingScene from './ResourceSavingScene'; +import Drawer from './Drawer'; import { Navigation } from '../types'; +import { PanGestureHandler } from 'react-native-gesture-handler'; type DrawerOptions = { drawerBackgroundColor?: string; overlayColor?: string; minSwipeDistance?: number; drawerPosition: 'left' | 'right'; + drawerType: 'front' | 'back' | 'slide'; drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open'; keyboardDismissMode?: 'on-drag' | 'none'; - drawerType: 'front' | 'back' | 'slide'; drawerWidth: number | (() => number); statusBarAnimation: 'slide' | 'none' | 'fade'; - useNativeAnimations?: boolean; onDrawerClose?: () => void; onDrawerOpen?: () => void; - onDrawerStateChanged?: () => void; - drawerContainerStyle?: ViewStyle; contentContainerStyle?: ViewStyle; edgeWidth: number; hideStatusBar?: boolean; @@ -82,90 +80,36 @@ export default class DrawerView extends React.PureComponent { }; componentDidMount() { - Dimensions.addEventListener('change', this._updateWidth); - } - - componentDidUpdate(prevProps: Props) { - const { - openId, - closeId, - toggleId, - isDrawerOpen, - } = this.props.navigation.state; - const { - openId: prevOpenId, - closeId: prevCloseId, - toggleId: prevToggleId, - } = prevProps.navigation.state; - - let prevIds = [prevOpenId, prevCloseId, prevToggleId]; - let changedIds = [openId, closeId, toggleId] - .filter(id => !prevIds.includes(id)) - // @ts-ignore - .sort((a, b) => a > b); - - changedIds.forEach(id => { - if (id === openId) { - this._drawer.openDrawer(); - } else if (id === closeId) { - this._drawer.closeDrawer(); - } else if (id === toggleId) { - if (isDrawerOpen) { - this._drawer.closeDrawer(); - } else { - this._drawer.openDrawer(); - } - } - }); + Dimensions.addEventListener('change', this.updateWidth); } componentWillUnmount() { - Dimensions.removeEventListener('change', this._updateWidth); + Dimensions.removeEventListener('change', this.updateWidth); } - _drawer: typeof DrawerLayout; + private drawerGestureRef = React.createRef(); - drawerGestureRef = React.createRef(); + private handleDrawerOpen = () => { + const { navigation } = this.props; - _handleDrawerStateChange = (newState: string, willShow: boolean) => { - if (newState === 'Idle') { - if (!this.props.navigation.state.isDrawerIdle) { - this.props.navigation.dispatch({ - type: DrawerActions.MARK_DRAWER_IDLE, - key: this.props.navigation.state.key, - }); - } - } else if (newState === 'Settling') { - this.props.navigation.dispatch({ - type: DrawerActions.MARK_DRAWER_SETTLING, - key: this.props.navigation.state.key, - willShow, - }); - } else { - if (this.props.navigation.state.isDrawerIdle) { - this.props.navigation.dispatch({ - type: DrawerActions.MARK_DRAWER_ACTIVE, - key: this.props.navigation.state.key, - }); - } - } + navigation.dispatch( + DrawerActions.openDrawer({ + key: navigation.state.key, + }) + ); }; - _handleDrawerOpen = () => { - this.props.navigation.dispatch({ - type: DrawerActions.DRAWER_OPENED, - key: this.props.navigation.state.key, - }); - }; + private handleDrawerClose = () => { + const { navigation } = this.props; - _handleDrawerClose = () => { - this.props.navigation.dispatch({ - type: DrawerActions.DRAWER_CLOSED, - key: this.props.navigation.state.key, - }); + navigation.dispatch( + DrawerActions.closeDrawer({ + key: navigation.state.key, + }) + ); }; - _updateWidth = () => { + private updateWidth = () => { const drawerWidth = typeof this.props.navigationConfig.drawerWidth === 'function' ? this.props.navigationConfig.drawerWidth() @@ -176,27 +120,23 @@ export default class DrawerView extends React.PureComponent { } }; - _renderNavigationView = ( - drawerOpenProgress: Animated.AnimatedInterpolation - ) => { + private renderNavigationView = ({ progress }: any) => { return ( - - - + ); }; - _renderContent = () => { + private renderContent = () => { let { lazy, navigation } = this.props; let { loaded } = this.state; let { routes } = navigation.state; @@ -214,7 +154,7 @@ export default class DrawerView extends React.PureComponent { ); } else { return ( - + {routes.map((route, index) => { if (lazy && !loaded.includes(index)) { // Don't render a screen if we've never navigated to it @@ -246,67 +186,68 @@ export default class DrawerView extends React.PureComponent { } }; - _setDrawerGestureRef = (ref: any) => { + private setDrawerGestureRef = (ref: PanGestureHandler | null) => { // @ts-ignore this.drawerGestureRef.current = ref; }; render() { - const { navigation, screenProps } = this.props; + const { navigation } = this.props; + const { + drawerType, + drawerBackgroundColor, + overlayColor, + contentContainerStyle, + edgeWidth, + minSwipeDistance, + hideStatusBar, + statusBarAnimation, + } = this.props.navigationConfig; const activeKey = navigation.state.routes[navigation.state.index].key; const { drawerLockMode } = this.props.descriptors[activeKey].options; + const isOpen = + drawerLockMode === 'locked-closed' + ? false + : drawerLockMode === 'locked-open' + ? true + : this.props.navigation.state.isDrawerOpen; + return ( - { - this._drawer = c; - }} - onGestureRef={this._setDrawerGestureRef} - drawerLockMode={ - drawerLockMode || - (typeof screenProps === 'object' && - screenProps != null && - // @ts-ignore - screenProps.drawerLockMode) || - this.props.navigationConfig.drawerLockMode - } - drawerBackgroundColor={ - this.props.navigationConfig.drawerBackgroundColor - } - keyboardDismissMode={this.props.navigationConfig.keyboardDismissMode} - drawerWidth={this.state.drawerWidth} - onDrawerOpen={this._handleDrawerOpen} - onDrawerClose={this._handleDrawerClose} - onDrawerStateChanged={this._handleDrawerStateChange} - useNativeAnimations={this.props.navigationConfig.useNativeAnimations} - renderNavigationView={this._renderNavigationView} - drawerPosition={ - this.props.navigationConfig.drawerPosition === 'right' - ? DrawerLayout.positions.Right - : DrawerLayout.positions.Left - } - /* props specific to react-native-gesture-handler/DrawerLayout */ - drawerType={this.props.navigationConfig.drawerType} - edgeWidth={this.props.navigationConfig.edgeWidth} - hideStatusBar={this.props.navigationConfig.hideStatusBar} - statusBarAnimation={this.props.navigationConfig.statusBarAnimation} - minSwipeDistance={this.props.navigationConfig.minSwipeDistance} - overlayColor={this.props.navigationConfig.overlayColor} - drawerContainerStyle={this.props.navigationConfig.drawerContainerStyle} - contentContainerStyle={ - this.props.navigationConfig.contentContainerStyle - } - > - - {this._renderContent()} - - + + + ); } } const styles = StyleSheet.create({ - pages: { + content: { flex: 1, }, });