Unified header menus with components

This commit is contained in:
Jori Lallo
2016-05-30 11:36:43 -07:00
parent 3714e1fd7c
commit b6ab92dbb1
12 changed files with 193 additions and 131 deletions

View File

@@ -0,0 +1,66 @@
import React from 'react';
import styles from './DropdownMenu.scss';
const MenuItem = (props) => {
return (
<div
className={ styles.menuItem }
onClick={ props.onClick}
>{ props.children }</div>
);
};
MenuItem.propTypes = {
onClick: React.PropTypes.func,
children: React.PropTypes.node.isRequired,
};
//
class DropdownMenu extends React.Component {
static propTypes = {
label: React.PropTypes.string.isRequired,
children: React.PropTypes.node.isRequired,
}
state = {
menuVisible: false,
}
onMouseEnter = () => {
this.setState({ menuVisible: true });
}
onMouseLeave = () => {
this.setState({ menuVisible: false });
}
onClick = () => {
this.setState({ menuVisible: !this.state.menuVisible });
}
render() {
return (
<div
className={ styles.menuContainer }
onMouseEnter={ this.onMouseEnter }
onMouseLeave={ this.onMouseLeave }
>
<div className={ styles.label } onClick={ this.onClick }>
{ this.props.label }
</div>
{ this.state.menuVisible ? (
<div className={ styles.menu }>
{ this.props.children }
</div>
) : null }
</div>
);
}
};
export default DropdownMenu;
export {
MenuItem,
}

View File

@@ -0,0 +1,40 @@
@import '../../utils/constants.scss';
.label {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
min-height: 43px;
padding: 0 0.5rem;
color: $linkColor;
}
.menuContainer {
position: relative;
.menu {
position: absolute;
top: 42px;
right: 0;
z-index: 1000;
border: 1px solid #eee;
min-width: 150px;
padding: 5px 0;
}
}
.menuItem {
margin: 0;
padding: 5px 10px;
display: flex;
justify-content: flex-start;
align-items: center;
cursor: pointer;
a {
color: $textColor;
text-decoration: none;
}
}

View File

@@ -0,0 +1,5 @@
import DropdownMenu, { MenuItem } from './DropdownMenu';
export default DropdownMenu;
export {
MenuItem,
};

View File

@@ -1,10 +1,13 @@
import React from 'react';
import { connect } from 'react-redux';
import Link from 'react-router/lib/Link';
import { bindActionCreators } from 'redux';
import { logoutUser } from 'actions/UserActions';
import HeaderMenu from './components/HeaderMenu';
import DropdownMenu, { MenuItem } from 'components/DropdownMenu';
import Flex from 'components/Flex';
import LoadingIndicator from 'components/LoadingIndicator';
import { Avatar } from 'rebass';
import styles from './Layout.scss';
import classNames from 'classnames/bind';
@@ -18,6 +21,10 @@ class Layout extends React.Component {
loading: React.PropTypes.bool,
}
onLogout = () => {
this.props.logoutUser();
}
render() {
return (
<div className={ styles.container }>
@@ -35,11 +42,19 @@ class Layout extends React.Component {
<Flex align="center" className={ styles.actions }>
{ this.props.actions }
</Flex>
<HeaderMenu>
<img src={ this.props.avatarUrl } />
</HeaderMenu>
<DropdownMenu label={
<Avatar
circle
size={24}
src={ this.props.avatarUrl }
/>
}>
<MenuItem onClick={ this.onLogout }>Logout</MenuItem>
</DropdownMenu>
</Flex>
</div>
<div className={ styles.content }>
{ this.props.children }
</div>
@@ -55,6 +70,13 @@ const mapStateToProps = (state) => {
}
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
logoutUser,
}, dispatch)
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Layout);

View File

@@ -43,7 +43,3 @@
justify-content: center;
}
.actions a {
text-decoration: none;
margin-right: 15px;
}

View File

@@ -1,67 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import { logoutUser } from 'actions/UserActions';
import styles from './HeaderMenu.scss';
class HeaderMenu extends React.Component {
static propTypes = {
children: React.PropTypes.node.isRequired,
}
state = {
menuVisible: false,
}
onMouseEnter = () => {
this.setState({ menuVisible: true });
}
onMouseLeave = () => {
this.setState({ menuVisible: false });
}
onClick = () => {
this.setState({ menuVisible: !this.state.menuVisible });
}
logout = (event) => {
event.preventDefault();
this.props.logout();
}
render() {
return (
<div
className={ styles.menu }
onMouseEnter={ this.onMouseEnter }
onMouseLeave={ this.onMouseLeave }
>
<div className={ styles.content } onClick={ this.onClick }>
{ this.props.children }
</div>
{ this.state.menuVisible ? (
<div className={ styles.menu }>
<ul>
<li>
<a href='/' onClick={ this.logout }>Logout</a>
</li>
</ul>
</div>
) : null }
</div>
);
}
};
const mapDispatchToProps = (dispatch) => {
return {
logout: () => {
dispatch(logoutUser())
},
}
};
export default connect(null, mapDispatchToProps)(HeaderMenu);

View File

@@ -1,51 +0,0 @@
@import '../../../../utils/constants.scss';
.content {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
min-height: 43px;
min-width: 43px;
}
.menu {
position: relative;
img {
height: 24px;
width: 24px;
border-radius: 12px;
}
.menu {
position: absolute;
top: 42px;
right: 0;
z-index: 1000;
border: 1px solid #eee;
ul {
margin: 0;
padding: 5px 0;
font-size: 0.9em;
}
li {
margin: 0;
padding: 0;
list-style-type: none;
a {
display: flex;
margin: 0;
padding: 5px 10px;
min-width: 150px;
color: $textColor;
text-decoration: none;
}
}
}
}

View File

@@ -1,2 +0,0 @@
import HeaderMenu from './HeaderMenu';
export default HeaderMenu;

View File

@@ -1,8 +1,10 @@
import Layout from './Layout';
import Title from './components/Title';
import HeaderLink from './components/HeaderLink';
export default Layout;
export {
Title,
HeaderLink,
};