mirror of
https://github.com/zhigang1992/react-native-paper.git
synced 2026-06-16 20:01:13 +08:00
feat: add <Drawer /> component (#72)
This commit is contained in:
committed by
Satyajit Sahoo
parent
c3a3e21196
commit
d0c9a4ad4e
@@ -2,32 +2,87 @@
|
||||
|
||||
import Exponent from 'exponent';
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import {
|
||||
NavigationProvider,
|
||||
StackNavigation,
|
||||
} from '@exponent/ex-navigation';
|
||||
import { Colors, ThemeProvider } from 'react-native-paper';
|
||||
import { Colors, ThemeProvider, Drawer } from 'react-native-paper';
|
||||
import Router from './src/Router';
|
||||
|
||||
|
||||
const DrawerItems = [
|
||||
{ label: 'Inbox', icon: 'inbox', key: 0 },
|
||||
{ label: 'Starred', icon: 'star', key: 1 },
|
||||
{ label: 'Sent mail', icon: 'send', key: 2 },
|
||||
{ label: 'A very long title that will be truncated', icon: 'delete', key: 3 },
|
||||
{ label: 'No Icon', key: 4 },
|
||||
];
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
open: false,
|
||||
drawerItemIndex: 0,
|
||||
}
|
||||
|
||||
_handleOpenDrawer = () => this.setState({ open: true })
|
||||
|
||||
_handleCloseDrawer = () => this.setState({ open: false })
|
||||
|
||||
_setDrawerItem = index => this.setState({ drawerItemIndex: index })
|
||||
|
||||
_renderDrawerItems = () => {
|
||||
return (
|
||||
<View style={styles.drawerContent}>
|
||||
<Drawer.Section label='Subheader'>
|
||||
{DrawerItems.map((props, index) => (
|
||||
<Drawer.Item
|
||||
{...props}
|
||||
key={props.key}
|
||||
active={this.state.drawerItemIndex === index}
|
||||
onPress={() => this._setDrawerItem(index)}
|
||||
/>))}
|
||||
</Drawer.Section>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<NavigationProvider router={Router}>
|
||||
<StackNavigation
|
||||
defaultRouteConfig={{
|
||||
navigationBar: {
|
||||
title: 'Examples',
|
||||
tintColor: Colors.white,
|
||||
backgroundColor: Colors.indigo500,
|
||||
},
|
||||
}}
|
||||
initialRoute={Router.getRoute('home')}
|
||||
/>
|
||||
<Drawer
|
||||
onOpen={this._handleOpenDrawer}
|
||||
onClose={this._handleCloseDrawer}
|
||||
open={this.state.open}
|
||||
content={this._renderDrawerItems()}
|
||||
>
|
||||
<StackNavigation
|
||||
defaultRouteConfig={{
|
||||
navigationBar: {
|
||||
title: 'Examples',
|
||||
tintColor: Colors.white,
|
||||
backgroundColor: Colors.indigo500,
|
||||
},
|
||||
}}
|
||||
initialRoute={Router.getRoute('home')}
|
||||
/>
|
||||
</Drawer>
|
||||
</NavigationProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
drawerContent: {
|
||||
flex: 1,
|
||||
marginTop: Platform.OS === 'android' ? 25 : 22,
|
||||
},
|
||||
});
|
||||
|
||||
Exponent.registerRootComponent(App);
|
||||
|
||||
23
index.js
Normal file
23
index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* @flow */
|
||||
|
||||
export { default as ThemeProvider } from './src/core/ThemeProvider';
|
||||
export { default as withTheme } from './src/core/withTheme';
|
||||
|
||||
export * as Colors from './src/styles/colors';
|
||||
|
||||
export { default as Button } from './src/components/Button';
|
||||
export { default as Card } from './src/components/Card';
|
||||
export { default as Checkbox } from './src/components/Checkbox';
|
||||
export { default as Paper } from './src/components/Paper';
|
||||
export { default as RadioButton } from './src/components/RadioButton';
|
||||
export { default as TouchableRipple } from './src/components/TouchableRipple';
|
||||
|
||||
export { default as Drawer } from './src/components/Drawer';
|
||||
export { default as Divider } from './src/components/Divider';
|
||||
|
||||
export { default as Caption } from './src/components/Typography/Caption';
|
||||
export { default as Headline } from './src/components/Typography/Headline';
|
||||
export { default as Paragraph } from './src/components/Typography/Paragraph';
|
||||
export { default as Subheading } from './src/components/Typography/Subheading';
|
||||
export { default as Title } from './src/components/Typography/Title';
|
||||
export { default as Text } from './src/components/Typography/Text';
|
||||
@@ -39,6 +39,7 @@
|
||||
"react-native-vector-icons": "~3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"color": "^0.11.4"
|
||||
"color": "^0.11.4",
|
||||
"react-native-drawer": "^2.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
142
src/components/Drawer/Drawer.js
Normal file
142
src/components/Drawer/Drawer.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/* @flow */
|
||||
|
||||
import RNDrawer from 'react-native-drawer';
|
||||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import DrawerItem from './DrawerItem';
|
||||
import DrawerSection from './DrawerSection';
|
||||
import { white } from '../../styles/colors';
|
||||
|
||||
type Props = {
|
||||
children?: any;
|
||||
content?: any;
|
||||
locked?: boolean;
|
||||
onClose?: Function;
|
||||
onOpen?: Function;
|
||||
open?: boolean;
|
||||
swipeRatio?: number;
|
||||
style?: any;
|
||||
side?: string;
|
||||
width?: number;
|
||||
}
|
||||
|
||||
type DefaultProps = {
|
||||
locked: boolean;
|
||||
open: boolean;
|
||||
swipeRatio: number;
|
||||
side: string;
|
||||
}
|
||||
|
||||
class Drawer extends Component<DefaultProps, Props, void> {
|
||||
static Item = DrawerItem;
|
||||
static Section = DrawerSection;
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
content: PropTypes.node.isRequired,
|
||||
/*
|
||||
Specifies the lock mode of the drawer. The drawer can be locked in 2 states:
|
||||
- false, meaning that the drawer will respond (open/close) to touch gestures
|
||||
- true, meaning that the drawer won't respond to touch gestures but it can be open by passing prop `open`
|
||||
*/
|
||||
locked: PropTypes.bool,
|
||||
onClose: PropTypes.func,
|
||||
onOpen: PropTypes.func,
|
||||
open: PropTypes.bool,
|
||||
/* Ratio of screen width that is valid for the start of a pan open action */
|
||||
swipeRatio: PropTypes.number,
|
||||
width: PropTypes.number,
|
||||
style: View.propTypes.style,
|
||||
side: PropTypes.oneOf([ 'left', 'right' ]),
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
side: 'left',
|
||||
locked: false,
|
||||
open: false,
|
||||
swipeRatio: 0.05,
|
||||
}
|
||||
|
||||
_root: any;
|
||||
|
||||
_tweenHandler = ratio => ({
|
||||
main: {
|
||||
opacity: 1,
|
||||
},
|
||||
mainOverlay: {
|
||||
opacity: ratio / 2,
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
})
|
||||
|
||||
_handleClose = () => {
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
global.requestAnimationFrame(() => {
|
||||
if (this.props.open === false) {
|
||||
return;
|
||||
} else {
|
||||
this._root.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_handleOpen = () => {
|
||||
if (this.props.onOpen) {
|
||||
this.props.onOpen();
|
||||
}
|
||||
global.requestAnimationFrame(() => {
|
||||
if (this.props.open === true) {
|
||||
return;
|
||||
} else {
|
||||
this._root.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_calculateOpenDrawerOffset = viewport => this.props.width ?
|
||||
(viewport.width - this.props.width) :
|
||||
(viewport.width - ((viewport.width * 80) / 100))
|
||||
|
||||
render(): ?React.Element<any> {
|
||||
const {
|
||||
children,
|
||||
content,
|
||||
style,
|
||||
side,
|
||||
locked,
|
||||
open,
|
||||
swipeRatio,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<RNDrawer
|
||||
{...this.props}
|
||||
ref={c => (this._root = c)}
|
||||
tapToClose
|
||||
captureGestures
|
||||
negotiatePan
|
||||
acceptPan={!locked}
|
||||
type='overlay'
|
||||
tweenEasing='easeInQuad'
|
||||
content={content}
|
||||
open={open}
|
||||
openDrawerOffset={this._calculateOpenDrawerOffset}
|
||||
panOpenMask={swipeRatio}
|
||||
onClose={this._handleClose}
|
||||
onOpen={this._handleOpen}
|
||||
tweenHandler={this._tweenHandler}
|
||||
side={side}
|
||||
styles={{ drawer: StyleSheet.flatten({ backgroundColor: white }, style) }}
|
||||
>
|
||||
{children}
|
||||
</RNDrawer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Drawer;
|
||||
61
src/components/Drawer/DrawerItem.js
Normal file
61
src/components/Drawer/DrawerItem.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/* @flow */
|
||||
|
||||
import color from 'color';
|
||||
import React, {
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import Icon from '.././Icon';
|
||||
import TouchableRipple from '../TouchableRipple';
|
||||
import { grey300 } from '../../styles/colors';
|
||||
import withTheme from '../../core/withTheme';
|
||||
import type { Theme } from '../../types/Theme';
|
||||
|
||||
type Props = {
|
||||
icon?: string;
|
||||
label: string;
|
||||
active?: boolean;
|
||||
onPress?: Function;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const DrawerItem = ({ icon, label, active, onPress, theme, ...props }: Props) => {
|
||||
const { colors } = theme;
|
||||
const labelColor = active ? colors.primary : color(colors.text).alpha(0.87).rgbaString();
|
||||
const iconColor = active ? colors.primary : color(colors.text).alpha(0.54).rgbaString();
|
||||
const fontFamily = theme.fonts.medium;
|
||||
const labelMargin = icon ? 32 : 0;
|
||||
return (
|
||||
<TouchableRipple {...props} onPress={onPress}>
|
||||
<View style={[ styles.wrapper, { backgroundColor: active ? grey300 : 'transparent' } ]}>
|
||||
{ icon &&
|
||||
<Icon
|
||||
name={icon}
|
||||
size={24}
|
||||
color={iconColor}
|
||||
/>}
|
||||
<Text numberOfLines={1} style={{ color: labelColor, fontFamily, marginLeft: labelMargin, marginRight: 16 }}>{label}</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
height: 48,
|
||||
},
|
||||
});
|
||||
|
||||
DrawerItem.propTypes = {
|
||||
icon: PropTypes.string,
|
||||
label: PropTypes.string.isRequired,
|
||||
active: PropTypes.bool,
|
||||
onPress: PropTypes.func,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default withTheme(DrawerItem);
|
||||
39
src/components/Drawer/DrawerSection.js
Normal file
39
src/components/Drawer/DrawerSection.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/* @flow */
|
||||
|
||||
import color from 'color';
|
||||
import React, { PropTypes } from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import Divider from '../Divider';
|
||||
import withTheme from '../../core/withTheme';
|
||||
import type { Theme } from '../../types/Theme';
|
||||
|
||||
type Props = {
|
||||
children: any;
|
||||
label?: string;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const DrawerSection = ({ children, label, theme, ...props }: Props) => {
|
||||
const { colors, fonts } = theme;
|
||||
const labelColor = color(colors.text).alpha(0.54).rgbaString();
|
||||
const fontFamily = fonts.medium;
|
||||
|
||||
return (
|
||||
<View {...props}>
|
||||
{ label &&
|
||||
<View style={{ height: 40, justifyContent: 'center' }}>
|
||||
<Text numberOfLines={1} style={{ color: labelColor, fontFamily, marginLeft: 16 }}>{label}</Text>
|
||||
</View>}
|
||||
{children}
|
||||
<Divider style={{ marginVertical: 4 }} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
DrawerSection.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
label: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default withTheme(DrawerSection);
|
||||
3
src/components/Drawer/index.js
Normal file
3
src/components/Drawer/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
/* @flow */
|
||||
|
||||
export { default as default } from './Drawer';
|
||||
@@ -20,3 +20,4 @@ export { default as Title } from './components/Typography/Title';
|
||||
export { default as Text } from './components/Typography/Text';
|
||||
|
||||
export { default as Divider } from './components/Divider';
|
||||
export { default as Drawer } from './components/Drawer';
|
||||
|
||||
Reference in New Issue
Block a user