import React from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import { ThemeColors, ThemeContext, 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 DrawerSidebar from './DrawerSidebar';
import DrawerGestureContext from '../utils/DrawerGestureContext';
import ResourceSavingScene from '../views/ResourceSavingScene';
/**
* Component that renders the drawer.
*/
export default class DrawerView extends React.PureComponent {
static contextType = ThemeContext;
static defaultProps = {
lazy: true,
};
static getDerivedStateFromProps(nextProps, prevState) {
const { index } = nextProps.navigation.state;
return {
// Set the current tab to be loaded if it was not loaded before
loaded: prevState.loaded.includes(index)
? prevState.loaded
: [...prevState.loaded, index],
};
}
state = {
loaded: [this.props.navigation.state.index],
drawerWidth:
typeof this.props.navigationConfig.drawerWidth === 'function'
? this.props.navigationConfig.drawerWidth()
: this.props.navigationConfig.drawerWidth,
};
componentDidMount() {
Dimensions.addEventListener('change', this._updateWidth);
}
componentDidUpdate(prevProps) {
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))
.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();
}
}
});
}
componentWillUnmount() {
Dimensions.removeEventListener('change', this._updateWidth);
}
drawerGestureRef = React.createRef();
_handleDrawerStateChange = (newState, willShow) => {
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,
});
}
}
};
_handleDrawerOpen = () => {
this.props.navigation.dispatch({
type: DrawerActions.DRAWER_OPENED,
key: this.props.navigation.state.key,
});
};
_handleDrawerClose = () => {
this.props.navigation.dispatch({
type: DrawerActions.DRAWER_CLOSED,
key: this.props.navigation.state.key,
});
};
_updateWidth = () => {
const drawerWidth =
typeof this.props.navigationConfig.drawerWidth === 'function'
? this.props.navigationConfig.drawerWidth()
: this.props.navigationConfig.drawerWidth;
if (this.state.drawerWidth !== drawerWidth) {
this.setState({ drawerWidth });
}
};
_renderNavigationView = drawerOpenProgress => {
return (
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
<DrawerSidebar
screenProps={this.props.screenProps}
drawerOpenProgress={drawerOpenProgress}
navigation={this.props.navigation}
descriptors={this.props.descriptors}
contentComponent={this.props.navigationConfig.contentComponent}
contentOptions={this.props.navigationConfig.contentOptions}
drawerPosition={this.props.navigationConfig.drawerPosition}
style={this.props.navigationConfig.style}
{...this.props.navigationConfig}
/>
</DrawerGestureContext.Provider>
);
};
_renderContent = () => {
let { lazy, navigation } = this.props;
let { loaded } = this.state;
let { routes } = navigation.state;
if (this.props.navigationConfig.unmountInactiveRoutes) {
let activeKey = navigation.state.routes[navigation.state.index].key;
let descriptor = this.props.descriptors[activeKey];
return (
<SceneView
navigation={descriptor.navigation}
screenProps={this.props.screenProps}
component={descriptor.getComponent()}
/>
);
} else {
return (
<ScreenContainer style={styles.pages}>
{routes.map((route, index) => {
if (lazy && !loaded.includes(index)) {
// Don't render a screen if we've never navigated to it
return null;
}
let isFocused = navigation.state.index === index;
let descriptor = this.props.descriptors[route.key];
return (
<ResourceSavingScene
key={route.key}
style={[
StyleSheet.absoluteFill,
{ opacity: isFocused ? 1 : 0 },
]}
isVisible={isFocused}
>
<SceneView
navigation={descriptor.navigation}
screenProps={this.props.screenProps}
component={descriptor.getComponent()}
/>
</ResourceSavingScene>
);
})}
</ScreenContainer>
);
}
};
_setDrawerGestureRef = ref => {
this.drawerGestureRef.current = ref;
};
render() {
const { navigation } = this.props;
const activeKey = navigation.state.routes[navigation.state.index].key;
const { drawerLockMode } = this.props.descriptors[activeKey].options;
let { overlayColor, drawerBackgroundColor } = this.props.navigationConfig;
if (drawerBackgroundColor) {
drawerBackgroundColor =
typeof drawerBackgroundColor === 'string'
? drawerBackgroundColor
: drawerBackgroundColor[this.context];
} else {
drawerBackgroundColor = ThemeColors[this.context].bodyContent;
}
if (overlayColor) {
overlayColor =
typeof overlayColor === 'string'
? overlayColor
: overlayColor[this.context];
} else {
overlayColor = ThemeColors[this.context].bodyContent;
}
return (
<DrawerLayout
ref={c => {
this._drawer = c;
}}
onGestureRef={this._setDrawerGestureRef}
drawerLockMode={
drawerLockMode ||
(this.props.screenProps && this.props.screenProps.drawerLockMode) ||
this.props.navigationConfig.drawerLockMode
}
drawerBackgroundColor={drawerBackgroundColor}
overlayColor={overlayColor}
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}
drawerContainerStyle={this.props.navigationConfig.drawerContainerStyle}
contentContainerStyle={
this.props.navigationConfig.contentContainerStyle
}
>
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
{this._renderContent()}
</DrawerGestureContext.Provider>
</DrawerLayout>
);
}
}
const styles = StyleSheet.create({
pages: {
flex: 1,
},
});