diff --git a/README.md b/README.md
index 4292911..e480b76 100644
--- a/README.md
+++ b/README.md
@@ -523,6 +523,7 @@ import {
StackNavigation,
DrawerNavigation,
DrawerNavigationItem,
+ DrawerNavigationChild,
} from '@exponent/ex-navigation';
// Treat the DrawerNavigationLayout route like any other route -- you may want to set
@@ -554,6 +555,10 @@ class DrawerNavigationLayout extends React.Component {
/>
+
+ Meta
+
+
{this.props.title}
+ );
+ }
+}
- _renderHeader = () => {
+class DrawerItem extends DrawerNavigationItem {
+ renderIcon = (isSelected: bool) => {
+ let extraStyle = {marginTop: 2};
+ if (this.props.icon === 'md-alert') {
+ extraStyle = {...extraStyle, marginLeft: -3};
+ }
return (
-
-
-
+
);
};
- _renderTitle = (text: string, isSelected: bool) => {
+ renderTitle = (isSelected: bool) => {
return (
- {text}
+ {this.props.title}
);
};
- _renderIcon = (name: string, isSelected: bool) => {
- let extraStyle = {marginTop: 2};
- if (name === 'md-alert') {
- extraStyle = {...extraStyle, marginLeft: -3};
- }
+ get selectedItemStyle() {
+ return styles.selectedItemStyle;
+ }
+
+ get children() {
+ let { defaultRouteConfig } = this.props;
+
return (
-
);
+ }
+}
+
+export default class DrawerNavigationExample extends Component {
+ renderHeader = () => {
+ return (
+
+
+
+ );
};
render() {
return (
- this._renderTitle('Examples', isSelected)}
- renderIcon={isSelected => this._renderIcon('md-apps', isSelected)}>
-
-
-
+
+ this._renderTitle('About', isSelected)}
- renderIcon={isSelected => this._renderIcon('md-alert', isSelected)}>
-
-
+ icon="md-alert"
+ title="About"
+ defaultRouteConfig={{
+ navigationBar: {
+ backgroundColor: '#0084FF',
+ tintColor: '#fff',
+ },
+ }}
+ stack={{
+ id: 'root',
+ initialRoute: Router.getRoute('about'),
+ }}
+ />
);
}
@@ -90,6 +121,12 @@ const styles = StyleSheet.create({
width: null,
resizeMode: 'cover',
},
+ headingText: {
+ paddingVertical: 10,
+ paddingHorizontal: 15,
+ color: '#777',
+ fontWeight: 'bold',
+ },
buttonTitleText: {
color: '#222',
fontWeight: 'bold',
diff --git a/src/ExNavigation.js b/src/ExNavigation.js
index c8c3cb2..6bf7c31 100644
--- a/src/ExNavigation.js
+++ b/src/ExNavigation.js
@@ -18,6 +18,7 @@ export { default as SlidingTabNavigationItem } from './sliding-tab/ExNavigationS
export { default as DrawerNavigation } from './drawer/ExNavigationDrawer';
export { default as DrawerNavigationItem } from './drawer/ExNavigationDrawerItem';
+export { default as DrawerNavigationChild } from './drawer/ExNavigationDrawerChild';
export { default as NavigationBar } from './ExNavigationBar';
diff --git a/src/drawer/ExNavigationDrawer.js b/src/drawer/ExNavigationDrawer.js
index ad77eca..7e45c1c 100644
--- a/src/drawer/ExNavigationDrawer.js
+++ b/src/drawer/ExNavigationDrawer.js
@@ -11,7 +11,6 @@ import {
} from 'react-native';
import DrawerLayout from 'react-native-drawer-layout';
import PureComponent from '../utils/PureComponent';
-import StaticContainer from 'react-static-container';
import invariant from 'invariant';
import _ from 'lodash';
@@ -22,6 +21,7 @@ import { createNavigatorComponent } from '../ExNavigationComponents';
import ExNavigationDrawerLayout from './ExNavigationDrawerLayout';
import ExNavigationDrawerItem from './ExNavigationDrawerItem';
+import ExNavigationDrawerChild from './ExNavigationDrawerChild';
import type ExNavigationContext from '../ExNavigationContext';
export class ExNavigationDrawerContext extends ExNavigatorContext {
@@ -77,7 +77,7 @@ type Props = {
type State = {
id: string,
navigatorUID: string,
- drawerItems: Array,
+ drawerItems: Array>,
parentNavigatorUID: string,
renderedItemKeys: Array,
};
@@ -147,6 +147,7 @@ class ExNavigationDrawer extends PureComponent {
drawerPosition: this.props.drawerPosition,
width: this.props.drawerWidth,
renderNavigationView: this.props.renderNavigationView,
+ setActiveItem: this.setActiveItem,
style: [
this.props.drawerStyle,
],
@@ -163,7 +164,7 @@ class ExNavigationDrawer extends PureComponent {
renderContent = () => {
const items = this.state.renderedItemKeys.map(key => {
- return this.state.drawerItems.find(i => i.id === key);
+ return this.state.drawerItems.find(i => i.props.id === key);
});
return (
@@ -173,26 +174,16 @@ class ExNavigationDrawer extends PureComponent {
);
};
- renderItemContent(drawerItem: Object) {
- if (!drawerItem.element) {
- return null;
- }
-
+ renderItemContent(drawerItem: React.Element) {
const navState = this._getNavigationState();
const selectedChild = navState.routes[navState.index];
- const isSelected = drawerItem.id === selectedChild.key;
+ const isSelected = drawerItem.props.id === selectedChild.key;
- return (
-
-
- {drawerItem.element}
-
-
- );
+ return React.cloneElement(drawerItem, {
+ key: drawerItem.props.id,
+ isSelected,
+ renderTo: 'content',
+ });
}
componentWillMount() {
@@ -265,33 +256,12 @@ class ExNavigationDrawer extends PureComponent {
}
invariant(
- child.type === ExNavigationDrawerItem,
- 'All children of DrawerNavigation must be DrawerNavigationItems.',
+ child.type.prototype === ExNavigationDrawerChild.prototype ||
+ child.type instanceof Object && ExNavigationDrawerChild.prototype.isPrototypeOf(child.type.prototype),
+ 'All children of DrawerNavigation must be DrawerNavigationChild descendant components.'
);
- const drawerItemProps = child.props;
-
- let drawerItem = {
- ..._.omit(drawerItemProps, ['children']),
- };
-
- if (Children.count(drawerItemProps.children) > 0) {
- drawerItem.element = Children.only(drawerItemProps.children);
- }
-
- const drawerItemOnPress = () => {
- this._setActiveItem(drawerItemProps.id, index);
- };
-
- if (typeof drawerItemProps.onPress === 'function') {
- drawerItem.onPress = drawerItem.onPress.bind(this, drawerItemOnPress);
- } else {
- drawerItem.onPress = drawerItemOnPress;
- }
-
- drawerItem.onLongPress = drawerItemProps.onLongPress;
-
- return drawerItem;
+ return child;
});
this.setState({
@@ -299,12 +269,12 @@ class ExNavigationDrawer extends PureComponent {
});
}
- _setActiveItem(id, index) {
+ setActiveItem = (id) => {
this._getNavigatorContext().jumpToItem(id);
if (typeof this.props.onPress === 'function') {
this.props.onPress(id);
}
- }
+ };
toggleDrawer = () => {
this._drawerLayout && this._drawerLayout.toggle();
@@ -346,11 +316,4 @@ const styles = StyleSheet.create({
itemContentOuter: {
flex: 1,
},
- itemContentInner: {
- position: 'absolute',
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
});
diff --git a/src/drawer/ExNavigationDrawerChild.js b/src/drawer/ExNavigationDrawerChild.js
new file mode 100644
index 0000000..b163e3b
--- /dev/null
+++ b/src/drawer/ExNavigationDrawerChild.js
@@ -0,0 +1,74 @@
+import React from 'react';
+import {
+ StyleSheet,
+ View,
+} from 'react-native';
+import StaticContainer from 'react-static-container';
+
+export type Props = {
+ children: React.Element,
+ renderTo: 'drawer' | 'content',
+ showsTouches: ?boolean,
+ isSelected: boolean,
+ selectedStyle: any,
+};
+
+export default class ExNavigationDrawerChild extends React.Component {
+ props: Props;
+ hasContent: boolean = false;
+
+ renderDrawerItem() {
+ let { children } = this.props;
+ return React.createElement(View, null, children);
+ }
+
+ renderContent() {
+ return null;
+ }
+
+ _renderContent() {
+ const { isSelected } = this.props;
+ let content = this.renderContent();
+
+ if (content === null) {
+ return null;
+ }
+
+ return (
+
+
+ {content}
+
+
+ );
+ }
+
+ render() {
+ let { renderTo } = this.props;
+
+ if (renderTo === 'drawer') {
+ return this.renderDrawerItem();
+ } else if (renderTo === 'content') {
+ return this._renderContent();
+ } else {
+ console.warn('renderTo must be "drawer" or "content"');
+ return null;
+ }
+ }
+}
+
+const styles = StyleSheet.create({
+ itemContentOuter: {
+ flex: 1,
+ },
+ itemContentInner: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ },
+});
diff --git a/src/drawer/ExNavigationDrawerItem.js b/src/drawer/ExNavigationDrawerItem.js
index dfcdba1..4f0d26e 100644
--- a/src/drawer/ExNavigationDrawerItem.js
+++ b/src/drawer/ExNavigationDrawerItem.js
@@ -1,7 +1,113 @@
import React from 'react';
+import {
+ TouchableWithoutFeedback,
+ StyleSheet,
+ View,
+} from 'react-native';
+import TouchableNativeFeedbackSafe from '@exponent/react-native-touchable-native-feedback-safe';
+import ExNavigationDrawerChild from './ExNavigationDrawerChild';
+import type { Props } from './ExNavigationDrawerChild';
+
+function _set(self, name, fn) {
+ // Stop use of `get` from interfering with `renderTitle => ...` in subclasses
+ Object.defineProperty(self, name, {
+ value: fn,
+ });
+}
+
+export default class ExNavigationDrawerItem extends ExNavigationDrawerChild {
+ props: Props;
+
+ get showsTouches() { return this.props.showsTouches; }
+ get renderIcon() { return this.props.renderIcon; }
+ get renderTitle() { return this.props.renderTitle; }
+ get renderRight() { return this.props.renderRight; }
+ get onPress() { return this.props.onPress; }
+ get onLongPress() { return this.props.onLongPress; }
+ get children() { return this.props.children; }
+ set showsTouches(fn) { _set(this, 'showsTouches', fn); }
+ set renderIcon(fn) { _set(this, 'renderIcon', fn); }
+ set renderTitle(fn) { _set(this, 'renderTitle', fn); }
+ set renderRight(fn) { _set(this, 'renderRight', fn); }
+ set onPress(fn) { _set(this, 'onPress', fn); }
+ set onLongPress(fn) { _set(this, 'onLongPress', fn); }
+ set children(fn) { _set(this, 'children', fn); }
+
+ renderDrawerItem() {
+ let { isSelected } = this.props;
+ let { showsTouches, renderIcon, renderTitle, renderRight, onPress, onLongPress } = this;
+ const icon = renderIcon && renderIcon(isSelected);
+ const title = renderTitle && renderTitle(isSelected);
+ const rightElement = renderRight && renderRight(isSelected);
+
+ if (showsTouches !== false) {
+ return (
+
+
+ {
+ icon && {icon}
+ }
+ {
+ title && {title}
+ }
+ {
+ rightElement && {rightElement}
+ }
+
+
+ );
+ } else {
+ return (
+
+
+ {
+ icon && {icon}
+ }
+ {
+ title && {title}
+ }
+ {
+ rightElement && {rightElement}
+ }
+
+
+ );
+ }
+ }
+
+ renderContent() {
+ const children = this.children;
+ if (React.Children.count(children) > 0) {
+ return React.Children.only(children);
+ }
-export default class ExNavigationDrawerItem extends React.Component {
- render() {
return null;
}
}
+
+const styles = StyleSheet.create({
+ buttonContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ paddingVertical: 10,
+ paddingHorizontal: 15,
+ },
+ elementContainer: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ },
+ rightElementContainer: {
+ flex: 1,
+ justifyContent: 'flex-end',
+ },
+});
diff --git a/src/drawer/ExNavigationDrawerLayout.js b/src/drawer/ExNavigationDrawerLayout.js
index 51c1d4a..4d24ab3 100644
--- a/src/drawer/ExNavigationDrawerLayout.js
+++ b/src/drawer/ExNavigationDrawerLayout.js
@@ -4,23 +4,30 @@
import React from 'react';
import {
- NativeModules,
ScrollView,
StyleSheet,
- Text,
- TouchableWithoutFeedback,
View,
} from 'react-native';
import DrawerLayout from 'react-native-drawer-layout';
-import TouchableNativeFeedbackSafe from '@exponent/react-native-touchable-native-feedback-safe';
+
+type NavigationViewOptions = {
+ renderHeader: () => React.Element,
+ renderDrawerItems: (items: Array>) => Array>,
+ items: Array>,
+ containerStyle: Array,
+ scrollableContentContainerStyle: Array,
+};
type Props = {
renderHeader: () => React.Element,
+ renderNavigationView: (options:NavigationViewOptions) => React.Element,
width: number,
+ items: Array>,
children: React.Element,
drawerBackgroundColor: string,
drawerPosition: 'left' | 'right',
selectedItem: any,
+ setActiveItem: (id: string) => void,
};
type State = {
@@ -44,7 +51,7 @@ export default class ExNavigationDrawerLayout extends React.Component {
drawerBackgroundColor={this.props.drawerBackgroundColor}
drawerWidth={this.props.width}
drawerPosition={DrawerLayout.positions[position]}
- renderNavigationView={this.props.renderNavigationView || this._renderNavigationView}>
+ renderNavigationView={this._renderNavigationView}>
{this.props.children}
);
@@ -59,73 +66,45 @@ export default class ExNavigationDrawerLayout extends React.Component {
}
_renderNavigationView = () => {
+ const renderNavigationView = this.props.renderNavigationView || this.renderNavigationView;
+ return renderNavigationView({
+ renderHeader: this.props.renderHeader,
+ renderDrawerItems: this.renderDrawerItems,
+ items: this.props.items,
+ containerStyle: [styles.navigationViewContainer, this.props.style],
+ scrollableContentContainerStyle: [styles.navigationViewScrollableContentContainer],
+ });
+ }
+
+ renderNavigationView = ({renderHeader, renderDrawerItems, items, containerStyle, scrollableContentContainerStyle}:NavigationViewOptions) => {
return (
-
+
- {this.props.renderHeader()}
+ {renderHeader()}
-
- {this._renderDrawerItems()}
+
+ {renderDrawerItems(items)}
);
}
- _renderDrawerItems = () => {
- if (!this.props.items) {
- return null;
+ renderDrawerItems = (items: Array>) => {
+ if (!items) {
+ return [];
}
- return this.props.items.map((item, index) => {
- let { renderIcon, renderTitle, renderRight } = item;
- let isSelected = this.props.selectedItem === item.id;
- const icon = renderIcon && renderIcon(isSelected);
- const title = renderTitle && renderTitle(isSelected);
- const rightElement = renderRight && renderRight(isSelected);
-
- if (item.showsTouches !== false) {
- return (
- { this._handlePress(item); }}
- onLongPress={() => { this._handleLongPress(item); }}
- delayPressIn={0}
- style={[isSelected ? item.selectedStyle : item.style]}
- background={item.nativeFeedbackBackground}>
-
- {
- icon && {icon}
- }
- {
- title && {title}
- }
- {
- rightElement && {rightElement}
- }
-
-
- );
- } else {
- return (
- { this._handlePress(item); }}
- onLongPress={() => { this._handleLongPress(item); }}>
-
- {
- icon && {icon}
- }
- {
- title && {title}
- }
- {
- rightElement && {rightElement}
- }
-
-
- );
- }
+ return items.map((item, index) => {
+ let isSelected = this.props.selectedItem === item.props.id;
+
+ return React.cloneElement(item, {
+ key: index,
+ isSelected,
+ onPress: () => { this._handlePress(item.props); },
+ onLongPress: () => { this._handleLongPress(item.props); },
+ renderTo: 'drawer',
+ });
});
}
@@ -133,7 +112,11 @@ export default class ExNavigationDrawerLayout extends React.Component {
// onPress and onLongPress should fire after close drawer!
//
_handlePress = (item: any) => {
- item.onPress();
+ if (item.onPress) {
+ item.onPress();
+ } else {
+ this.props.setActiveItem(item.id);
+ }
this._component.closeDrawer();
}
@@ -155,21 +138,4 @@ const styles = StyleSheet.create({
navigationViewScrollableContentContainer: {
paddingTop: 8,
},
- buttonContainer: {
- flex: 1,
- flexDirection: 'row',
- justifyContent: 'flex-start',
- alignItems: 'center',
- paddingVertical: 10,
- paddingHorizontal: 15,
- },
- elementContainer: {
- flexDirection: 'row',
- justifyContent: 'flex-start',
- alignItems: 'center',
- },
- rightElementContainer: {
- flex: 1,
- justifyContent: 'flex-end'
- }
});