keystone
Version:
Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose
155 lines (142 loc) • 4.18 kB
JavaScript
/**
* The mobile navigation, displayed on screens < 768px
*/
import React from 'react';
import Transition from 'react-addons-css-transition-group';
import MobileSectionItem from './SectionItem';
const ESCAPE_KEY_CODE = 27;
const MobileNavigation = React.createClass({
displayName: 'MobileNavigation',
propTypes: {
brand: React.PropTypes.string,
currentListKey: React.PropTypes.string,
currentSectionKey: React.PropTypes.string,
sections: React.PropTypes.array.isRequired,
signoutUrl: React.PropTypes.string,
},
getInitialState () {
return {
barIsVisible: false,
};
},
// Handle showing and hiding the menu based on the window size when
// resizing
componentDidMount () {
this.handleResize();
window.addEventListener('resize', this.handleResize);
},
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize);
},
handleResize () {
this.setState({
barIsVisible: window.innerWidth < 768,
});
},
// Toggle the menu
toggleMenu () {
this[this.state.menuIsVisible ? 'hideMenu' : 'showMenu']();
},
// Show the menu
showMenu () {
this.setState({
menuIsVisible: true,
});
// Make the body unscrollable, so you can only scroll in the menu
document.body.style.overflow = 'hidden';
document.body.addEventListener('keyup', this.handleEscapeKey, false);
},
// Hide the menu
hideMenu () {
this.setState({
menuIsVisible: false,
});
// Make the body scrollable again
document.body.style.overflow = null;
document.body.removeEventListener('keyup', this.handleEscapeKey, false);
},
// If the escape key was pressed, hide the menu
handleEscapeKey (event) {
if (event.which === ESCAPE_KEY_CODE) {
this.hideMenu();
}
},
renderNavigation () {
if (!this.props.sections || !this.props.sections.length) return null;
return this.props.sections.map((section) => {
// Get the link and the classname
const href = section.lists[0].external ? section.lists[0].path : `${Keystone.adminPath}/${section.lists[0].path}`;
const className = (this.props.currentSectionKey && this.props.currentSectionKey === section.key) ? 'MobileNavigation__section is-active' : 'MobileNavigation__section';
// Render a SectionItem
return (
<MobileSectionItem
key={section.key}
className={className}
href={href}
lists={section.lists}
currentListKey={this.props.currentListKey}
onClick={this.toggleMenu}
>
{section.label}
</MobileSectionItem>
);
});
},
// Render a blockout
renderBlockout () {
if (!this.state.menuIsVisible) return null;
return <div className="MobileNavigation__blockout" onClick={this.toggleMenu} />;
},
// Render the sidebar menu
renderMenu () {
if (!this.state.menuIsVisible) return null;
return (
<nav className="MobileNavigation__menu">
<div className="MobileNavigation__sections">
{this.renderNavigation()}
</div>
</nav>
);
},
render () {
if (!this.state.barIsVisible) return null;
return (
<div className="MobileNavigation">
<div className="MobileNavigation__bar">
<button
type="button"
onClick={this.toggleMenu}
className="MobileNavigation__bar__button MobileNavigation__bar__button--menu"
>
<span className={'MobileNavigation__bar__icon octicon octicon-' + (this.state.menuIsVisible ? 'x' : 'three-bars')} />
</button>
<span className="MobileNavigation__bar__label">
{this.props.brand}
</span>
<a
href={this.props.signoutUrl}
className="MobileNavigation__bar__button MobileNavigation__bar__button--signout"
>
<span className="MobileNavigation__bar__icon octicon octicon-sign-out" />
</a>
</div>
<div className="MobileNavigation__bar--placeholder" />
<Transition
transitionName="MobileNavigation__menu"
transitionEnterTimeout={260}
transitionLeaveTimeout={200}
>
{this.renderMenu()}
</Transition>
<Transition
transitionName="react-transitiongroup-fade"
transitionEnterTimeout={0}
transitionLeaveTimeout={0}
>
{this.renderBlockout()}
</Transition>
</div>
);
},
});
module.exports = MobileNavigation;