Code Monkey home page Code Monkey logo

react-native-side-menu's Introduction

Customizable side menu for react-native

iOS android

Content

Installation

npm install react-native-side-menu --save

Usage example

import SideMenu from 'react-native-side-menu'

class ContentView extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+Control+Z for dev menu
        </Text>
      </View>
    );
  }
}

class Application extends React.Component {
  render() {
    const menu = <Menu navigator={navigator}/>;

    return (
      <SideMenu menu={menu}>
        <ContentView/>
      </SideMenu>
    );
  }
}

Component props

prop default type description
menu inherited React.Component Menu component
isOpen false Boolean Props driven control over menu open state
openMenuOffset 2/3 of device screen width Number Content view left margin if menu is opened
hiddenMenuOffset none Number Content view left margin if menu is hidden
edgeHitWidth none Number Edge distance on content view to open side menu, defaults to 60
toleranceX none Number X axis tolerance
toleranceY none Number Y axis tolerance
disableGestures false Bool Disable whether the menu can be opened with gestures or not
onStartShould
SetResponderCapture
none Function Function that accepts event as an argument and specify if side-menu should react on the touch or not. Check https://facebook.github.io/react-native/docs/gesture-responder-system.html for more details
onChange none Function Callback on menu open/close. Is passed isOpen as an argument
onMove none Function Callback on menu move. Is passed left as an argument
onSliding none Function Callback when menu is sliding. It returns a decimal from 0 to 1 which represents the percentage of menu offset between hiddenMenuOffset and openMenuOffset.
menuPosition left String either 'left' or 'right'
animationFunction none (Function -> Object) Function that accept 2 arguments (prop, value) and return an object:
- prop you should use at the place you specify parameter to animate
- value you should use to specify the final value of prop
onAnimationComplete none (Function -> Void) Function that accept 1 optional argument (event):
- event you should this to capture the animation event after the animation has successfully completed
animationStyle none (Function -> Object) Function that accept 1 argument (value) and return an object:
- value you should use at the place you need current value of animated parameter (left offset of content view)
bounceBackOnOverdraw true boolean when true, content view will bounce back to openMenuOffset when dragged further
autoClosing true boolean When true, menu close automatically as soon as an event occurs

FAQ

ScrollView does not scroll to top on status bar press

On iPhone, the scroll-to-top gesture has no effect if there is more than one scroll view on-screen that has scrollsToTop set to true. Since it defaults to true in ReactNative, you have to set scrollsToTop={false} on your ScrollView inside Menu component in order to get it working as desired.

The swipe animation is extremely slow

Try disabling remote JS debugging (from developer menu on phone/VD)

My SideMenu contents are visible even when the side menu is hidden

Ensure that your main view has a background color applied

<Sidemenu menu={menu}>
<App style={{backgroundColor='white'}} />
</SideMenu>

Questions?

Feel free to contact me in twitter or create an issue

react-native-side-menu's People

Contributors

alimek avatar alinz avatar almost avatar alphasp avatar brentvatne avatar carlesnunez avatar chakrihacker avatar chirag04 avatar danscan avatar davidstubbs avatar dvcrn avatar grabbou avatar hannigand avatar hichambmg avatar kureev avatar morenoh149 avatar mwilc0x avatar naturalclar avatar nickineering avatar piyushchauhan2011 avatar pm0u avatar pointcom avatar sghiassy avatar sharadsxn24 avatar skevy avatar skv-headless avatar thstarshine avatar troywatt avatar zamiang avatar zjlovezj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-side-menu's Issues

InteractionManager Warning message

If you drag the menu and keep your finger on the screen, react-native throw a warning as below

screen shot 2015-06-03 at 11 12 37 am

Are you guys getting the same warning? Any suggestion? I think 2 seconds for slide is a little bit too low.

Implement fancy animation!

Hey, just finished playing around with the code, and following:

import SideMenu from 'react-native-side-menu';

export default class Menu extends SideMenu {

  updatePosition() {
    var style = {};
    var scaleFactor = this.left > 0
        ? this.left / this.props.scaleFactor / 100
        : 1;
    style.transform = [{
      scale: scaleFactor
    }];
    this.sideMenu.setNativeProps({
      left: this.left,
      style: style
    });
  }

}

makes this:
screen shot 2015-07-17 at 14 01 47

DefaultProps:

  • scaleFactor - 3
  • animationKind - 'slide' (for compatibility)

Can we implement it by exporting a ScaleMenu class that will extend Menu and override the above method + add two more props described above? Simple.

Navigation : undefined is not a function

Hi,
Very good job with this menu! I have a problem though...
It's probably me, but I can't find out how to create a navigation that works. By following your example, I just filled the blanks in navigator.push with a title and another component containing a simple View.
Doing this, I keep having the same mistake:
"undefined is not a function (evaluating this.props.navigator.push({title: 'Homepage', component: Homepage})') "

Could you please help me out?
Am I supposed to add a Navigator somewhere?

Support React 0.7 and above

As per latest changelog, looks like Dimensions shouldn't be required as a standalone module, but from React package instead, e.g.:

import {Dimensions} from 'react-native';

Currently, it prints warnings.

New Packager config for accessing react-native internal modules.

ok, I think we need to change couple of things and I want to know your (@Kureev) opinion about it.
based on this ticket, facebook/react-native#1808, we should change some code as follows:

instead of this:

var deviceScreen = require('Dimensions').get('window');

we should do it this way

var { Dimensions } = require('react-native');
var deviceScreen = Dimensions.get('window');

if you don't do it you will get this annoying error from packager.

Unable to resolve module Dimensions ...

what do you think? I really liked the idea of @providesModule in comment which makes it a lot easier to find a module. It seems that this feature will be removed. I don't know for sure.

Issue with ScrollView after react-native-side-menu installation.

I've installed the react-native-side-menu package and it blocks the scrollview for some reason. When I remove the sidemenu, my ListView and ScrollView works as expected. I don't know why is this happening. A simple example could be made by installing the react-native-side-menu and using ListView as sample component or ScrollView with many Text components. It could be seen that UI will have scrolling issues.

Empty menuActions

Within a child component or menu, the menuActions prop seems to be an empty object. Thoughts on why this might be?

How to use react-native-navbar with react-native-side-menu ?

Can someone help me with using react-native-navbar with react-native-side-menu ?

Currently I'm doing like this, but the menu is under navbar :(

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Navigator,
} = React;

var { Icon, } = require('react-native-icons');

var SideMenu = require('react-native-side-menu');
var NavigationBar = require('react-native-navbar');

var Menu = React.createClass({
  about: function() {
    this.props.menuActions.close();
    // this.props.navigator.push({...});
  },

  render: function() {
    return (
      <View>
        <Text>Menu</Text>
        <Text onPress={this.about}>About</Text>
      </View>
    );
  }
});

var ContentView = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Icon
          name='fontawesome|facebook-square'
          size={70}
          color='#3b5998'
          style={styles.facebook}
        />
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+D or shake for dev menu
        </Text>
      </View>
    );
  }
});

var SideBar = React.createClass({
  render: function() {
    var menu = <Menu navigator={navigator}/>;

    return (
      <SideMenu menu={menu}>
        <ContentView/>
      </SideMenu>
    );
  }
});


var Docit = React.createClass({

  renderScene: function(route, navigator) {
    var Component = route.component;
    var navBar = route.navigationBar;

    if (navBar) {
      navBar = React.addons.cloneWithProps(navBar, {
        navigator: navigator,
        route: route
      });
    }

    return (
      <View style={styles.navigator}>
        {navBar}
        <Component navigator={navigator} route={route} />
      </View>
    );
  },

  render: function() {
    return (
      <Navigator
        style={styles.navigator}
        renderScene={this.renderScene}
        initialRoute={{
          component: SideBar,
          navigationBar: <NavigationBar title="Initial View"/>
        }}
      />
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  facebook: {
    width: 70,
    height: 70,
    margin: 10
  },
});

AppRegistry.registerComponent('Docit', () => Docit);

screen shot 2015-07-17 at 2 06 22 pm

screen shot 2015-07-17 at 2 06 51 pm

Close Side Menu by Tap

How would you implement close slide menu if the menu is already open by tapping outside of menu?

Build Failed

  1. npm install
  2. npm start
  3. attempt to build the example

screen shot 2015-04-30 at 8 15 20 am

Disable/enable Side Menu

Scenario:

I'm using the side menu to open a view for filtering a list. The Filters Component is the "menu" and the Master Component is a table inside a navigator. After selecting an item in my table, the Detail Component is pushed onscreen. At this point, I'd like to disable the side-menu since filters are no longer needed.

I attempted this to no avail:

    this.refs.sideMenu.disableGestures = true;

Thoughts?

@Kureev

Any way to restrict menu opening to a certain point away from the screen edge?

I have a view with a horizontal ScrollView in it and I find myself accidentally opening the menu too often. I've played around with the toleranceX prop a little bit, and this does help - but not completely.

I find it weird that you can drag somewhere very far away from the edge of the screen and have it still trigger the menu. Would it be possible to add a prop like edgeTolerance where I could specify 40 and only touches that originate 40px from the edge can trigger the menu?

This would definitely prevent accidental menu openings when interacting with my horizontal ScrollView

Add isOpen prop

Hi guys,

Thanks for this awesome component, I'm trying to play with it but I don't find any property to open the menu from outside the Menu component (I need to open the menu from my NavBar).

I think we should simply add an isOpen={Boolean} property on the component and in the componentWillReceivePropsevent, update isOpen and call the toggleMenu method.

What do you think about it ?

Add onChange callback

Is there any way SideMenu can update the content whether it's opened or closed?
For example, I need to do something as soon as the SideMenu opens. Any suggestion?

Peer dependency issue with react native 0.6.0-rc

Getting this unmet peer dependency error on installation.
npm ERR! peerinvalid The package react-native does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants react-native@>= 0.4

here's the package.json
{
"name": "rn_hn",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node_modules/react-native/packager/packager.sh"
},
"dependencies": {
"firebase": "2.2.6",
"flux": "^2.0.3",
"keymirror": "^0.1.1",
"lodash": "3.9.3",
"moment": "2.10.3",
"object-assign": "^3.0.0",
"q": "1.4.1",
"react-native": "0.6.0-rc",
"react-native-htmlview": "0.1.2",
"url": "0.10.3"
}
}

Does rc qualify as a version ? the ">=0.4" check seems to be failing which is odd, any ideas ?

Both Side Menu

I want create left and right menu.please tell me how to do it?

How to use navigator with react native side menu ?

I want to use Navigator component with react-native-side-menu. You have mentioned the navigator props in the example but i'm not sure where the navigator is coming from ?

It would be very helpful if you can post an example with some links on sidebar changing the pages on right hand side while retaining the sidebar menu.

I tried to replicate the sidebar menu component on every page with navigator as top level component but it says expected a Component class, got [object Object]

I decided to wrote another example for this. Here is image and code:

screen shot 2015-05-24 at 7 53 30 pm

On clicking About link, this error occurs!

screen shot 2015-05-24 at 7 55 04 pm

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');
var SideMenu = require('react-native-side-menu');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Navigator,
} = React;

var ContentView = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+D or shake for dev menu
        </Text>
      </View>
    );
  }
});

var TestView = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to another page.
        </Text>
        <Text style={styles.instructions}>
          Testing react native side menu with navigator.
        </Text>
      </View>
    );
  }
});

var Menu = React.createClass({
  about: function() {
    this.props.menuActions.close();
    this.props.navigator.push({
      component: TestView,
      title: 'Test View',
    });
  },

  render: function() {
    return (
      <View style={styles.sidemenu}>
        <Text style={styles.paddingMenuItem}>Menu</Text>
        <Text onPress={this.about} style={styles.paddingMenuItem}>About</Text>
      </View>
    );
  }
});

var SideMenuTest = React.createClass({
  render: function() {
    return (
      <Navigator
       initialRoute={{
         component: Something,
         title: 'Something',
       }}
       configureScene={() => {
         return Navigator.SceneConfigs.FadeAndroid;
       }}
       renderScene={(route, navigator) => {
         if(route.component) {
           return React.createElement(route.component, { navigator });
         }
       }}/>
    );
  }
});

var Something = React.createClass({
  render: function() {
    var menu = <Menu navigator={this.props.navigator}/>;
    return (
      <SideMenu menu={menu}>
        <ContentView/>
      </SideMenu>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  sidemenu: {
    paddingTop: 50,
  },
  paddingMenuItem: {
    padding: 10,
  },
});

AppRegistry.registerComponent('SideMenuTest', () => SideMenuTest);

Main content layout doesn't stretch when rotate device

When we rotate device (for example, rotate iphone left or right), everything will automatically adjust from portrait mode to landscape mode for all react native UI components on ios8 devices. But this feature broken when we add side menu.

Here are some pictures and codes:

screen shot 2015-07-15 at 9 46 55 pm
screen shot 2015-07-15 at 9 47 45 pm

screen shot 2015-07-15 at 9 55 04 pm

We can see that top SegmentedControl and bottom TabBar all stretched correctly, but center content which wrapped in a SideMenu tag will always keep the portrait mode width (Even we start with landscape mode, it will still use portrait width, this is weird)

            <SideMenu
                ref='sideMenu'
                openMenuOffset={Constants.STYLE.SIDE_MENU_WIDTH}
                animation={'spring'}
                disableGestures={true}
                menu={<ReportMenu report={this.props.report}/>}>

                <View style={styles.container}>
                         some contents here
                </View>
            </SideMenu>

But if we remove side menu, everything works great when we rotate device (see background color):

screen shot 2015-07-15 at 10 01 13 pm

screen shot 2015-07-15 at 10 04 56 pm

                <View style={styles.container}>
                         some contents here
                </View>

Use flex: 1 style doesn't help. Any solution or suggestion?

forceUpdate for touchToClose calls too much

I've been heavily using this component. if you just try to open the side menu but in the middle of process decided to close the menu, forceUpdate is going to happen which is unnecessary. I will try to provide a PR.

unnecessary bind

Do you think we really need bind for this? I think react will do it automatically. Any thoughts on that?

Move `queueAnimation` to component

So it's overridable and we can easily ignore animationKind and provide own LayoutAnimations. Just an idea, as I am not that happy with the current one and I am investigating changing the LayoutAnimations to something different at all.

Unsupported layout animation createConfig property (null)

In linear animation mise to set property: LayoutAnimation.Properties.opacity, see
facebook/react-native#1135 (comment)

Here is code in Animation.js:

   linear: {
      duration: 300,
      create: {
        type: LayoutAnimation.Types.linear,
      },
      update: {
        type: LayoutAnimation.Types.linear,
        springDamping: 0.7,
      },
    },

It should be:

   linear: {
      duration: 300,
      create: {
        type: LayoutAnimation.Types.linear,
        property: LayoutAnimation.Properties.opacity,
      },
      update: {
        type: LayoutAnimation.Types.linear,
        springDamping: 0.7,
        property: LayoutAnimation.Properties.opacity,
      },
    },

Please add an example of controlling menu state through an external component.

I have a button which I would like to control the menu with, but I'm not sure of the suggested way to open/collapse the menu from an external component. Is there a way to get the ref and call a .toggle() on the menu?

Edit - seems the solution is pretty simple, you just need something like:

<SideMenu
      ref='slideMenu'
      menu={menu}>

this.refs.slideMenu.toggleMenu();

I would still recommend putting something into the README about this.

navigator

Hi, Thanks for the module, it looks great.

Is their any way of creating a inner shadow to the menu block.
:0)

Menu view child component gets hidden when width and height not explicitly defined

I ran into this issue when trying to render a menu component that was nested inside of another components view which I had no ability to set styles on such as flex: 1 or width and height in order to force layout.

Because of the menu component container view within react-native-side-menu has the style rule

menu: {
    position: 'absolute',
    top: 0,
    left: 0
}

the container has no width or height. Setting an explicit width and height on the menu component with fix this issue.

Roadmap

  • Make a working alpha version
  • Make an example and description for alpha
  • Expose menu API for menu view
  • Support different animations
  • Toggle menu functionality (thanks, @dvcrn)
  • Extend and describe side menu API
  • Create menuPosition property in cases of using menu on the right side (will be opened by slide right-to-left)

Mix side menu contents with main contents together after version 0.8

There is an issue after version 0.8( till latest version 0.9.2) which mix side menu contents with main contents together.

screen shot 2015-07-09 at 12 29 31 am

screen shot 2015-07-09 at 12 36 32 am

This happens when main contents wrapped in a ScrollView. If main contents is a WebView, it is OK.

And this issue happens after version 0.8. version 0.7 without any problem.

Here is my code:

            <SideMenu
                ref='sideMenu'
                openMenuOffset={Constants.STYLE.SIDE_MENU_WIDTH}
                animation={'spring'}
                disableGestures={true}
                menu={<ReportMenu report={this.props.report}/>}>

                <View style={styles.container}>
                    <View style={styles.controlBar}>
                    </View>

                    <ScrollView>

                                    <View key={index}>
                                        <Text> label: {component.label} </Text>
                                        <Text> Type: {component.type} </Text>
                                        <Text> Sheet: {WptService.getSheetById(component.sheetId).name} </Text>
                                    </View>
                    </ScrollView>
                </View>
            </SideMenu>


  var styles = StyleSheet.create({
      container: {
          flex: 1,
      },
      controlBar: {
          flexDirection: 'row',
          backgroundColor: Constants.STYLE.LEVEL1_MENU_BACKGROUND,
      },
      countText: {
          flex: 1,
          flexDirection: 'column'
      },
      webView: {
          flex: 1,
      },
  });

Can you provide an example that uses <Navigator />

I am having difficulty implementing the side menu with a navigator.

Here is what I have:

var Application = React.createClass({
  getInitialState: function() {
    return { applicationView: <SourceList /> };
  },
  handleChange: function(e) {
    this.setState({ applicationView: e });
  },
  render: function() {
    var nav = <Navigator ref="nav" initialRoute={{name: 'Sources', index: 0}} renderScene={(scene, navigator) => this.setState({ applicationView: e })} />;
    var menu = <Menu hiddenMenuOffset={10} close={true} navigator={nav} onChange={this.handleChange} />;
    return (
      <SideMenu menu={menu}>
        {this.state.applicationView}
      </SideMenu>
    );
  }
});

whenever I access this.props.navigator.push() in my

component, it is undefined - obviously I am doing this wrong (sorry react noob here) but I cannot find an example of how to do it properly.

Could you elaborate in your readme how to actually implement this properly with a <Navigator /> component?

iPad portrait

I am having an issue with an iPad Air in portrait mode. It doesnt take the full height of the screen.

Have also tested using the example on here.

Vertical Menu

Hi! I've been working on this for a couple days now and just can't figure it out. I've gotten the menu to appear vertically instead of horizontally, and messed with some of the offsets & margins to make it align properly. I also eliminated content from overlay, and only kept frontView – with frontView serving as my menu, resting on top of my content that is beneath. I did it this way because I already had a lot of code for the app done, and this seemed simpler (is this my problem?).

So, here's the issue: whenever I launch the app, or navigate to a new screen, the menu appears front and center. Changing variables like touchToClose and isOpen aren't fixing it. Here are more details and some thoughts:

Is it possible that iOS (or react native) doesn’t allow an item to be rendered off screen initially? I ask because of this behavior:

  1. Launch app: Menu appears on screen, though off by a few pixels. This is what I want to prevent from happening.
  2. Toggle menu away: Menu slides off of screen, north of the screen’s bounds, just as I want. Good.
  3. Toggle menu back in: Menu slides back into screen, in the precise location I want it. Also good.

When I change the openMenuOffset to something extreme (too far south), it still appears in the exact same spot for (1). But, for (3), it behaves properly and falls too far south.

Another interesting thing is whenever I load a new screen, menu reappears as in (1).

Any thoughts? I'm also happy to revert back to your menu and redo my vertical implementation if you have a better idea on how to execute. I essentially want my logo in the center to launch a slide-down menu from top to bottom. I'm using react-native-router, and setting

titleComponent:MainMenu

Thank you!

INDEX.js
(Note: I changed "left" to "top", just for my own sake / preventing confusion)

var React = require('react-native');
var deviceScreen = require('Dimensions').get('window');
var styles = require('./styles');
var queueAnimation = require('./animations');

var {
  PanResponder,
  View,
  TouchableWithoutFeedback,
  Component,
} = React;

/**
 * Default open menu offset. Describes a size of the amount you can
 * move content view from the top and release without opening it
 * @type {Number}
 */
var openMenuOffset = deviceScreen.height * 1/50;

/**
 * Content view offset in the `hidden` state
 * @type {Number}
 */
var hiddenMenuOffset = -655;

/**
 * Size of the amount you can move content view in the opened menu state and
 * release without menu closing
 * @type {Number}
 */
var barrierForward = deviceScreen.height / 4;

/**
 * Check if the current gesture offset bigger than allowed one
 * before opening menu
 * @param  {Number} dx Gesture offset from the top Top of the window
 * @return {Boolean}
 */
function shouldOpenMenu(dx: Number) {
  return dx > barrierForward;
}

/**
 * no operation function. does nothing.
 */
function noop() {}

class TopMenu extends Component {
  constructor(props) {
    super(props);

    /**
     * Current state of the menu, whether it is open or not
     * @type {Boolean}
     */
    this.isOpen = false;

    /**
     * Current style `top` attribute
     * @todo Check if it's possible to avoid using `top`
     * @type {Number}
     */
    this.top = 0;

    /**
     * Default top offset for content view
     * @todo Check if it's possible to avoid using `prevtop`
     * @type {Number}
     */
    this.prevtop = 0;
  }

  /**
   * Creates PanResponders and links to appropriate functions
   * @return {Void}
   */
  createResponders(disableGestures) {
    if (disableGestures || false) {
      this.responder = PanResponder.create({});
      return;
    }

    this.responder = PanResponder.create({
        onMoveShouldSetPanResponder: this.handleMoveShouldSetPanResponder.bind(this),
        onPanResponderMove: this.handlePanResponderMove.bind(this),
        onPanResponderRelease: this.handlePanResponderEnd.bind(this),
    });
  }

  /**
   * Set the initial responders
   * @return {Void}
   */
  componentWillMount() {
    this.createResponders(this.props.disableGestures);
  }

  /**
   * Update responders on new props whenever possible
   * @return {Void}
   */
  componentWillReceiveProps(nextProps) {
    this.createResponders(nextProps.disableGestures);
  }

  /**
   * Change `top` style attribute
   * Works only if `TopMenu` is a ref to React.Component
   * @return {Void}
   */
  updatePosition() {
    this.TopMenu.setNativeProps({ top: this.top, });
  }

  /**
   * Permission to use responder
   * @return {Boolean} true
   */
  handleMoveShouldSetPanResponder(e: Object, gestureState: Object) {
    var x = Math.round(Math.abs(gestureState.dx));
    var y = Math.round(Math.abs(gestureState.dy));

    return x > this.props.toleranceX && y < this.props.toleranceY;
  }

  /**
   * Handler on responder move
   * @param  {Synthetic Event} e
   * @param  {Object} gestureState
   * @return {Void}
   */
  handlePanResponderMove(e: Object, gestureState: Object) {
    this.top = this.prevtop + gestureState.dx;

    if ((this.menuPositionMultiplier() * this.top) > 0) {
      this.updatePosition();
    }
  }

  /**
   * Returns 1 or -1 depending on the menuPosition
   * @return {Number}
   */
  menuPositionMultiplier() {
    return this.props.menuPosition === 'bottom' ? -1 : 1;
  }

  /**
   * Open menu
   * @return {Void}
   */
  openMenu() {
    queueAnimation(this.props.animation);

    this.top = this.menuPositionMultiplier() *
      (this.props.openMenuOffset || openMenuOffset);

    this.updatePosition();
    this.prevtop = this.top;

    if (!this.isOpen) {
      this.props.onChange(this.isOpen);

      this.isOpen = true;

      // Force update to make the overlay appear (if touchToClose is set)
      if (this.props.touchToClose) {
        this.forceUpdate();
      }
    }
  }

  /**
   * Close menu
   * @return {Void}
   */
  closeMenu() {
    queueAnimation(this.props.animation);
    this.top = this.menuPositionMultiplier() *
      (this.props.hiddenMenuOffset || hiddenMenuOffset);

    this.updatePosition();
    this.prevtop = this.top;

    if (this.isOpen) {
      this.props.onChange(this.isOpen);

      this.isOpen = false;
      // Force update to make the overlay disappear (if touchToClose is set)
      if (this.props.touchToClose) {
        this.forceUpdate();
      }
    }
  }

  /**
   * Toggle menu
   * @return {Void}
   */
  toggleMenu() {
    if (this.isOpen) {
      this.closeMenu();
    } else {
      this.openMenu();
    }
  }

  /**
   * Handler on responder move ending
   * @param  {Synthetic Event} e
   * @param  {Object} gestureState
   * @return {Void}
   */
  handlePanResponderEnd(e: Object, gestureState: Object) {
    var shouldOpen = this.menuPositionMultiplier() *
      (this.top + gestureState.dx);

    if (shouldOpenMenu(shouldOpen)) {
      this.openMenu();
    } else {
      this.closeMenu();
    }

    this.updatePosition();
    this.prevtop = this.top;
  }

  handleOverlayPress(e: Object) {
    this.closeMenu();
  }

  /**
   * Get content view. This view will be rendered over menu
   * @return {React.Component}
   */
  getContentView() {
    var getMenuActions = this.getMenuActions();

    var overlay = null;

    if (this.isOpen && this.props.touchToClose) {
      overlay = (
        <TouchableWithoutFeedback onPress={this.handleOverlayPress.bind(this)}>
          <View style={styles.overlay} />
        </TouchableWithoutFeedback>
      );
    }

    var children = React.Children.map(this.props.children, (child) => {
      return React.cloneElement(child, {
        menuActions: getMenuActions,
      });
    });

    return (
      <View
        style={styles.frontView}
        ref={(TopMenu) => this.TopMenu = TopMenu}
        {...this.responder.panHandlers}>
        {children}
        {overlay}
      </View>
    );
  }

  /**
   * Get menu actions to expose it to
   * menu and children components
   * @return {Object} Public API methods
   */
  getMenuActions() {
    return {
      close: this.closeMenu.bind(this),
      toggle: this.toggleMenu.bind(this),
      open: this.openMenu.bind(this),
    };
  }

  /**
   * Get menu view. This view will be rendered under
   * content view. Also, this function will decorate
   * passed `menu` component with Top menu API
   * @return {React.Component}
   */
  getMenuView() {
    var menuActions = this.getMenuActions();

    return (
      <View style={styles.menu}>
        {React.addons.cloneWithProps(this.props.menu, { menuActions, })}
      </View>
    );
  }

  /**
   * Compose and render menu and content view
   * @return {React.Component}
   */
  render() {
    return (
      <View style={styles.container}>

        {this.getContentView()}
      </View>
    );
  }
}

TopMenu.propTypes = {
  toleranceX: React.PropTypes.number,
  toleranceY: React.PropTypes.number,
  onChange: React.PropTypes.func,
  touchToClose: React.PropTypes.bool,
};

TopMenu.defaultProps = {
  toleranceY: 33,
  toleranceX: -30,
  onChange: noop,
  touchToClose: false,
};

module.exports = TopMenu;

MAINMENU.js

'use strict';

var React = require('react-native');

var Menu = require('../../pages/MenuContent');
var SideMenu = require('react-native-side-menu');
// var Router = require('react-native-router');


var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
  ScrollView,
  TouchableOpacity,
  TouchableHighlight,
  Component
} = React;

class Button extends Component {
  handlePress(e) {
    this.props.menuActions.toggle();
    if (this.props.onPress) {
      this.props.onPress(e);
    }
  }

  render() {
    return (
      <TouchableOpacity
        onPress={this.handlePress.bind(this)}>
        <Text style={this.props.style}>{this.props.children}</Text>
      </TouchableOpacity>
    );
  }
}

class MainMenu extends Component {
  constructor(props) {
    super(props);

    this.state = {
      touchToClose: false
    };
  }

  handleOpenWithTouchToClose() {
    this.setState({
      touchToClose: false
    });
  }

  handleChange(isOpen) {
    if (!isOpen) {
      this.setState({
        touchToClose: false
      });
    }
  }

  render() {
    return (
      <SideMenu
        menu={<Menu />}
        touchToClose={this.state.touchToClose}
        onChange={this.handleChange.bind(this)}>
        <View style={styles.container}>
          <Text style={styles.welcome}>
            HLTHY
          </Text>
          <Text style={styles.menuItem}>
            MENU ITEM 1
          </Text>
          <Text style={styles.separator}>
            _____
          </Text>
          <Text style={styles.menuItem}>
            MENU ITEM 2
          </Text>
          <Text style={styles.separator}>
            _____
          </Text>
          <Text style={styles.menuItem}>
            MENU ITEM 3
          </Text>
          <Text style={styles.separator}>
            _____
          </Text>
          <Text style={styles.menuItem}>
            MENU ITEM 4
          </Text>
          <Text style={styles.separator}>
            _____
          </Text>
          <Text style={styles.menuItem}>
            MENU ITEM 5
          </Text>
        </View>
        <Button style={styles.toggleButton}>
          HLTHY
        </Button>
      </SideMenu>
    );
  }
}

var styles = StyleSheet.create({
  toggleButton: {
    position: 'absolute',
    bottom: 0,
    backgroundColor: 'transparent',
    color: 'white',
    fontSize: 30,
    borderRadius: 20,
    marginLeft: -230,
    marginTop: 30,
  },
  welcome: {
    fontSize: 30,
    textAlign: 'center',
    margin: 10,
    fontWeight: 'bold',
    marginBottom: 30
  },
  menuItem: {
    textAlign: 'center',
    color: '#333333',
    marginTop: 15,
    fontSize: 20,
    marginBottom: 15
  },
  separator: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 15,
  },
  caption: {
    fontSize: 20,
    fontWeight: 'bold',
    alignItems: 'center',
  },
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'stretch',
    backgroundColor: 'white',
    marginLeft: -750,
    marginTop: 0,
    marginBottom: 30,
  },
});

module.exports = MainMenu;

Have Navbar button control menuAction methods?

On a given scene, how can the menuAction methods be utilized from one of the bar buttons on the top nav bar?

Naturally, I am setting up the navbar buttons in the previous (from where I intend on using the react-native-side-menu). For example, I include the "onRightButtonPress()" function as a parameter when using "this.props.navigator.push()' function in the previous scene.

menuActions outside of the scope?

I'm trying to add a navbar with a button that should toggle the menu, but can't find where menuActions are exposed.

I tried various ideas with this.toggleMenu and having toggleMenu as a function in the menu class, but don't get it to work.

Any ideas?

    var menu = <Menu navigator={nav}/>;

    return (
      <View>
        <View>
          <TouchableOpacity onPress={() => menu.toggleMenu()}>
            <Text>Click</Text>
          </TouchableOpacity>
          <View>
            <Text>
              MyApp
            </Text>
          </View>
        </View>
        <SideMenu menu={menu}>
          <ItemView navigator={nav} />
        </SideMenu>
      </View>
    );

Correct way to handle NavigatorIOS?

I was playing around with the package to use in combination with NavigatorIOS. I've got it to work, but I'm not 100% sure if this is the best practice. From the example it looks like you somehow pass NavigatorIOS as props to the menu. However, in my case the <SideMenu/> is the parent of <NavigatorIOS/> and therefore I use this.refs and call the function using this.props. Here is the gist:
https://gist.github.com/swennemans/59f656f02497d0df6572

Do you take another approach? What is the benefit (besides your approach looks cleaner).

Thanks :)

Issue of having ScrollView as content

If you have a ScrollView as content of SlideMenu, toleranceX and toleranceY are not helping with the current implementation. After digging, I realize that this line, return x != this.props.toleranceX && y < this.props.toleranceY; needs to be changed to return x > this.props.toleranceX && y < this.props.toleranceY; in order for them to work properly.

Does it make sense?

I need to be updated as slide menu is dragged.

@Kureev, @skevy, have you guys seen Spotify app? As soon as you start dragging the menu, there is a nice animation happen behind the scene. Unfortunately there is no way for us to do this in current version. So I started to update the onChange method. So now, as soon as menu gets dragged, onChange method gets called with a value between 0 and 1. Zero means slide menu is completely close, and 1 means that slide menu is opened.

I will do the PR in few mins.

p.s. I think we should start rewrite the code in class way. I really don't like using setNativeProps.

Issue with scrolling a ListView when used with a side menu.

I made the following change in my app to get the scrolling to be smooth in the main list view. Otherwise it would periodically get stuck because of horizontal movements of the side menu.

handleMoveShouldSetPanResponder: function(e: Object, gestureState: Object) {
    var x = Math.round(Math.abs(gestureState.dx));
    var y = Math.round(Math.abs(gestureState.dy));

    if (x > 10 && y < 10) {
      return true;
    }
    return false;
  },

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.