Skip to content

Instantly share code, notes, and snippets.

@innerdaze
Created April 24, 2018 12:41
Show Gist options
  • Save innerdaze/97e3e9b34b418d496e37a31bc7a1dc68 to your computer and use it in GitHub Desktop.
Save innerdaze/97e3e9b34b418d496e37a31bc7a1dc68 to your computer and use it in GitHub Desktop.
React Burger Menu example
import React, { Component } from 'react'
import { connect } from 'react-redux'
import {
decorator as reduxBurgerMenu,
action as toggleMenu
} from 'redux-burger-menu'
import { slide as Menu } from 'react-burger-menu'
import PropTypes from 'prop-types'
import autobind from 'autobind-decorator'
import styled from 'styled-components'
import Box from '~components/elements/Box'
import Button from '~components/elements/Button'
import CrossIcon from '~components/icons/Cross'
import Header from '~components/elements/Header'
import Heading from '~components/elements/Heading'
import Label from '~components/elements/Label'
import { bookingSelectors } from '~features/booking'
import { sessionOperations } from '~features/session'
import { cashierOperations } from '~features/cashier'
import { syncOperations } from '~features/sync'
import { uiSelectors } from '~features/ui'
const StyledMenu = styled(reduxBurgerMenu(Menu))`
/* Position and sizing of burger button */
.bm-burger-button {
position: fixed;
width: 36px;
height: 30px;
left: 36px;
top: 36px;
}
/* Color/shape of burger icon bars */
.bm-burger-bars {
background: #373a47;
}
/* Position and sizing of clickable cross button */
.bm-cross-button {
width: 1.5em !important;
height: 1.5em !important;
right: 1.2em !important;
top: 2.2em !important;
}
/* Color/shape of close button cross */
.bm-cross {
fill: aliceblue;
}
/* General sidebar styles */
.bm-menu {
background: linear-gradient(180deg, #54b0ed, #4280a9);
font-size: 1.15em;
text-transform: uppercase;
}
/* Morph shape necessary with bubble or elastic */
.bm-morph-shape {
fill: #373a47;
}
/* Wrapper for item list */
.bm-item-list {
box-sizing: border-box;
color: aliceblue;
padding: 1em;
display: flex;
flex-direction: column;
}
/* Styling of overlay */
.bm-overlay {
background: rgba(0, 0, 0, 0.3);
}
`
const MenuItem = ({ onClick, ...extraProps }) => (
<Button
fontSize={2}
borderWidth={0}
color="aliceblue"
bg="rgba(255,255,255,0.1)"
mb={2}
fontWeight={600}
style={{
textTransform: 'uppercase',
letterSpacing: '0.1em'
}}
focus={{
background: 'rgba(255,255,255,0.2)',
outline: 'none'
}}
onClick={onClick}
{...extraProps}
/>
)
const SubMenu = props => (
<Box
{...props}
flexDirection="column"
alignItems="stretch"
style={{
display: 'flex'
}}
/>
)
class MainMenu extends Component {
static propTypes = {
isOpen: PropTypes.bool,
closeMenu: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
sync: PropTypes.func.isRequired
}
static defaultProps = {
isOpen: false
}
static contextTypes = {
router: PropTypes.object
}
state = {
isOpen: false
}
componentWillReceiveProps({ isOpen }) {
if (this.props.isOpen !== isOpen) {
this.handleMenuStateChange({ isOpen })
}
}
@autobind
_handleMenuAction(next) {
this.props.closeMenu()
if (next) {
next()
}
}
@autobind
handleClickSales() {
const { router } = this.context
this._handleMenuAction(() => router.history.push('/sales/entry'))
}
@autobind
handleClickBooking() {
const { router } = this.context
const { selectedBookingId } = this.props
this._handleMenuAction(() =>
router.history.push(`/bookings/${selectedBookingId}`)
)
}
@autobind
handleClickLogout() {
this._handleMenuAction(this.props.logout)
}
@autobind
handleClickSync() {
this._handleMenuAction(this.props.sync)
}
@autobind
handleClickSettings() {
this._handleMenuAction()
}
@autobind
onBackButtonPress(e) {
e.preventDefault()
this.props.closeMenu()
}
@autobind
handleMenuStateChange({ isOpen }) {
this.setState({ isOpen })
if (isOpen) {
document.addEventListener('backbutton', this.onBackButtonPress, false)
} else {
document.removeEventListener('backbutton', this.onBackButtonPress)
}
}
render() {
const { isOpen } = this.state
return (
<StyledMenu
isOpen={isOpen}
width="95%"
customBurgerIcon={false}
customCrossIcon={<CrossIcon/>}
>
<Header wrap="wrap" fontSize={[24, 32]} px={2}>
<Heading
fontSize="inherit"
m={0}
style={{
letterSpacing: '0.1em'
}}
>
Blue Lagoon
</Heading>
</Header>
<SubMenu flex={1} mt={2}>
<MenuItem label="Sales" onClick={this.handleClickSales}/>
<MenuItem label="Booking" onClick={this.handleClickBooking}/>
</SubMenu>
<SubMenu flexDirection="column" alignItems="stretch">
<MenuItem label="Sync" onClick={this.handleClickSync}/>
{/* <MenuItem
disabled
label="Settings"
onClick={this.handleClickSettings}
/> */}
<MenuItem
bg="rgba(255,9,43,0.3)"
label="Sign Out"
color="rgb(255, 55, 83)"
fontWeight={600}
focus={{
backgroundColor: 'rgba(255,9,43,0.5)'
}}
onClick={this.handleClickLogout}
/>
</SubMenu>
{window.cordova &&
window.navigator && (
<Label>
{`v${window.navigator.appInfo.version}`}
</Label>
)}
</StyledMenu>
)
}
}
const { isMenuOpenSelector } = uiSelectors
const { activeBookingIdSelector } = bookingSelectors
const { logout } = cashierOperations
const { sync } = syncOperations
export default connect(
state => ({
isOpen: isMenuOpenSelector(state),
selectedBookingId: activeBookingIdSelector(state)
}),
dispatch => ({
closeMenu: () => dispatch(toggleMenu(false)),
logout: () => dispatch(logout()),
sync: () => dispatch(sync())
})
)(MainMenu)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment