UNPKG

react-nav-bar

Version:

Simple yet very coustomizable navigation bar for react

166 lines (152 loc) 4.28 kB
import React, { Component, PropTypes, createClass, createElement } from 'react'; import _ from 'lodash'; import { springShape, toggleShape } from './../../lib/menuShapes'; import { createClassName, isMenuObject, checkActive } from './../../lib/utils'; import { DEFAULT_NAME } from './../../lib/constants'; import Menu from './../menu/Menu'; import './../../lib/themes'; export default class NavBar extends Component { constructor(props) { super(props); this.state = { menus: this.prepareMenus({ menus: this.props.menus, location: this.props.location }) }; } /** * recursivly check if menus are active. * * @param {Array} menus * @param {Object} location * @returns {*} */ prepareMenus({ menus, location }) { return menus.map( menu => { const isActive = isMenuObject( menu ) ? checkActive( { menu, location } ) : undefined; const subMenus = !_.isEmpty( menu.subMenus ) ? this.prepareMenus( { menus: menu.subMenus, location } ) : undefined; return Object.assign( {}, menu, isActive && { active: isActive }, subMenus && { subMenus } ); }); } /** * recurisvly renders the menus. * * @param {Array} menus * @param {Number} parentIndex * @param {Object} parent * @returns {*} */ renderMenus(menus, parentIndex, parent){ return menus.map((menu, index) => { // if this menu is a simple react component dont change it if( !isMenuObject(menu) ) { const menuComponent = createClass({ render() { return menu } }); return createElement(menuComponent, { key: index }); } if( _.isEmpty( menu.subMenus ) ){ if( menu.active && parent) parent.active = true; return createElement(Menu, Object.assign( { opened: false, permission: true, visible: false, subMenus: [] }, { key: index, theme: this.props.theme, index, toggle: this.props.toggle, parentIndex: ( parentIndex || 0 ), openOnHover: this.props.openOnHover }, menu ) ); } let children = this.renderMenus(menu.subMenus, index); /** * because top parent doesnt know if a children of a children is active we check the top parent children * in case of * * { paren, submenus: [ * {child}, * {child, submenuds:[ * {child} ---------------- if this menu is active top parent wont know about it until recursion is over * ]}, * * ]} * */ if(!parent) { _.forEach(menu.subMenus, (subMenu)=> { if( subMenu.active) { menu.active = true; return false; } }); } return createElement(Menu, Object.assign( { opened: false, permission: true, visible: false, subMenus: [] }, { key: index, theme: this.props.theme, spring: this.props.spring, toggle: this.props.toggle, index, parentIndex: ( parentIndex || 0 ), openOnHover: this.props.openOnHover }, menu ), children ); }); } /** * * Updating component in - case on menu change * @param props */ componentWillReceiveProps(props){ if ( props.menus ) { this.setState({ menus: this.prepareMenus({ menus: props.menus, location: props.location }) }); } } /** * component render * * @returns {XML} */ render() { let { theme } = this.props; theme = theme || DEFAULT_NAME; const menusMarkup = this.renderMenus(this.state.menus); return ( <ul className={ createClassName({ theme, classNames: 'nav-ul' }) }>{menusMarkup}</ul> ); } } NavBar.propTypes = { location : PropTypes.object.isRequired, // array of all menus theme: PropTypes.string, menus: PropTypes.array, spring: springShape, toggle: PropTypes.oneOfType([ toggleShape, PropTypes.bool ]), openOnHover: PropTypes.bool };