Fix default width of drawer (#2978)

* Fix default width of drawer

Current implementation is simplified / broken.

- The drawerWidth doesn't work for tablets, the drawer becomes massively large
- Header size changes (and thus drawer width changes) in landscape mode on iOS are not accounted for
- An incorrect 64px header size is used for iOS (this includes the status bar height that doesn't belong in the calculation)

Implement a default drawerWidth as a function that follows the Material Design spec closer:
- Screen width - header height
- Use the correct iOS app bar height in portrait and landscape mode
- Drawer max height of 280 on mobile and 320 on tablet

* Update snapshot for drawer width change
This commit is contained in:
Daniel Friesen
2017-11-15 12:04:15 -08:00
committed by Dave Pack
parent 346264e132
commit e25bdd2ed2
4 changed files with 51 additions and 13 deletions

View File

@@ -87,7 +87,7 @@ The route configs object is a mapping from route name to a route config, which t
### DrawerNavigatorConfig
- `drawerWidth` - Width of the drawer.
- `drawerWidth` - Width of the drawer or a function returning it.
- `drawerPosition` - Options are `left` or `right`. Default is `left` position.
- `contentComponent` - Component used to render the content of the drawer, for example, navigation items. Receives the `navigation` prop for the drawer. Defaults to `DrawerItems`. For more information, see below.
- `contentOptions` - Configure the drawer content, see below.

View File

@@ -24,8 +24,6 @@ export type DrawerNavigatorConfig = {
} & NavigationTabRouterConfig &
DrawerViewConfig;
const { height, width } = Dimensions.get('window');
const defaultContentComponent = (props: *) => (
<ScrollView alwaysBounceVertical={false}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
@@ -35,11 +33,21 @@ const defaultContentComponent = (props: *) => (
);
const DefaultDrawerConfig = {
/*
* Default drawer width is screen width - header width
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
drawerWidth: Math.min(height, width) - (Platform.OS === 'android' ? 56 : 64),
drawerWidth: () => {
/*
* Default drawer width is screen width - header height
* with a max width of 280 on mobile and 320 on tablet
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const { height, width } = Dimensions.get('window');
const smallerAxisSize = Math.min(height, width);
const isLandscape = width > height;
const isTablet = smallerAxisSize >= 600;
const appBarHeight = Platform.OS === 'ios' ? (isLandscape ? 32 : 44) : 56;
const maxWidth = isTablet ? 320 : 280;
return Math.min(smallerAxisSize - appBarHeight, maxWidth);
},
contentComponent: defaultContentComponent,
drawerPosition: 'left',
drawerBackgroundColor: 'white',

View File

@@ -73,10 +73,10 @@ exports[`DrawerNavigator renders successfully 1`] = `
"top": 0,
"transform": Array [
Object {
"translateX": -686,
"translateX": -320,
},
],
"width": 686,
"width": 320,
"zIndex": 1001,
}
}

View File

@@ -1,6 +1,7 @@
/* @flow */
import * as React from 'react';
import { Dimensions } from 'react-native';
import DrawerLayout from 'react-native-drawer-layout-polyfill';
import addNavigationHelpers from '../../addNavigationHelpers';
@@ -31,7 +32,7 @@ export type DrawerItem = {
export type DrawerViewConfig = {
drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open',
drawerWidth?: number,
drawerWidth?: number | (() => number),
drawerPosition?: 'left' | 'right',
contentComponent?: React.ComponentType<*>,
contentOptions?: {},
@@ -53,14 +54,32 @@ export type DrawerViewProps = DrawerViewPropsExceptRouter & {
>,
};
type DrawerViewState = {
drawerWidth?: number,
};
/**
* Component that renders the drawer.
*/
export default class DrawerView<T: NavigationRoute> extends React.PureComponent<
DrawerViewProps
DrawerViewProps,
DrawerViewState
> {
state: DrawerViewState = {
drawerWidth:
typeof this.props.drawerWidth === 'function'
? this.props.drawerWidth()
: this.props.drawerWidth,
};
componentWillMount() {
this._updateScreenNavigation(this.props.navigation);
Dimensions.addEventListener('change', this._updateWidth);
}
componentWillUnmount() {
Dimensions.removeEventListener('change', this._updateWidth);
}
componentWillReceiveProps(nextProps: DrawerViewProps) {
@@ -120,6 +139,17 @@ export default class DrawerView<T: NavigationRoute> extends React.PureComponent<
});
};
_updateWidth = () => {
const drawerWidth =
typeof this.props.drawerWidth === 'function'
? this.props.drawerWidth()
: this.props.drawerWidth;
if (this.state.drawerWidth !== drawerWidth) {
this.setState({ drawerWidth });
}
};
_getNavigationState = (navigation: NavigationScreenProp<NavigationState>) => {
const navigationState = navigation.state.routes.find(
(route: *) => route.routeName === 'DrawerClose'
@@ -166,7 +196,7 @@ export default class DrawerView<T: NavigationRoute> extends React.PureComponent<
(config && config.drawerLockMode)
}
drawerBackgroundColor={this.props.drawerBackgroundColor}
drawerWidth={this.props.drawerWidth}
drawerWidth={this.state.drawerWidth}
onDrawerOpen={this._handleDrawerOpen}
onDrawerClose={this._handleDrawerClose}
useNativeAnimations={this.props.useNativeAnimations}