From d0c9a4ad4e4ecdb652afb1f9aad3d182da2e2718 Mon Sep 17 00:00:00 2001 From: Ahmed Elhanafy Date: Sun, 20 Nov 2016 20:28:47 +0200 Subject: [PATCH] feat: add component (#72) --- example/main.js | 77 ++++++++++++-- index.js | 23 ++++ package.json | 3 +- src/components/Drawer/Drawer.js | 142 +++++++++++++++++++++++++ src/components/Drawer/DrawerItem.js | 61 +++++++++++ src/components/Drawer/DrawerSection.js | 39 +++++++ src/components/Drawer/index.js | 3 + src/index.js | 1 + 8 files changed, 337 insertions(+), 12 deletions(-) create mode 100644 index.js create mode 100644 src/components/Drawer/Drawer.js create mode 100644 src/components/Drawer/DrawerItem.js create mode 100644 src/components/Drawer/DrawerSection.js create mode 100644 src/components/Drawer/index.js diff --git a/example/main.js b/example/main.js index 0929692..5fb36cf 100644 --- a/example/main.js +++ b/example/main.js @@ -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 ( + + + {DrawerItems.map((props, index) => ( + this._setDrawerItem(index)} + />))} + + + ); + }; + render() { return ( - + + + ); } } +const styles = StyleSheet.create({ + drawerContent: { + flex: 1, + marginTop: Platform.OS === 'android' ? 25 : 22, + }, +}); + Exponent.registerRootComponent(App); diff --git a/index.js b/index.js new file mode 100644 index 0000000..cf850cb --- /dev/null +++ b/index.js @@ -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'; diff --git a/package.json b/package.json index 40bf968..5d47ec5 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/src/components/Drawer/Drawer.js b/src/components/Drawer/Drawer.js new file mode 100644 index 0000000..d56014d --- /dev/null +++ b/src/components/Drawer/Drawer.js @@ -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 { + 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 { + const { + children, + content, + style, + side, + locked, + open, + swipeRatio, + } = this.props; + + return ( + (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} + + ); + } +} + +export default Drawer; diff --git a/src/components/Drawer/DrawerItem.js b/src/components/Drawer/DrawerItem.js new file mode 100644 index 0000000..a8f0971 --- /dev/null +++ b/src/components/Drawer/DrawerItem.js @@ -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 ( + + + { icon && + } + {label} + + + ); +}; + +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); diff --git a/src/components/Drawer/DrawerSection.js b/src/components/Drawer/DrawerSection.js new file mode 100644 index 0000000..4d84f7f --- /dev/null +++ b/src/components/Drawer/DrawerSection.js @@ -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 ( + + { label && + + {label} + } + {children} + + + ); +}; + +DrawerSection.propTypes = { + children: PropTypes.node.isRequired, + label: PropTypes.string, + theme: PropTypes.object.isRequired, +}; + +export default withTheme(DrawerSection); diff --git a/src/components/Drawer/index.js b/src/components/Drawer/index.js new file mode 100644 index 0000000..84d04bd --- /dev/null +++ b/src/components/Drawer/index.js @@ -0,0 +1,3 @@ +/* @flow */ + +export { default as default } from './Drawer'; diff --git a/src/index.js b/src/index.js index 9ec2313..76c5e26 100644 --- a/src/index.js +++ b/src/index.js @@ -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';