Skip to content

Commit

Permalink
Merge pull request #4936 from reactioncommerce/feat-4682-ajporlante-n…
Browse files Browse the repository at this point in the history
…av-dashboard-ui

feat: navigation manager UI
  • Loading branch information
machikoyasuda authored Feb 11, 2019
2 parents ca49b2b + 7b94e2a commit c4a17d6
Show file tree
Hide file tree
Showing 51 changed files with 2,253 additions and 232 deletions.
83 changes: 83 additions & 0 deletions imports/client/ui/components/ConfirmDialog/ConfirmDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";

class ConfirmDialog extends Component {
static propTypes = {
cancelActionText: PropTypes.string,
children: PropTypes.func.isRequired,
confirmActionText: PropTypes.string,
message: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
onConfirm: PropTypes.func,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
}

static defaultProps = {
cancelActionText: "Cancel",
confirmActionText: "OK",
onConfirm() {}
}

state = {
isOpen: false
}

handleClose = () => {
this.setState({
isOpen: false
});
}

handleConfirm = () => {
this.props.onConfirm();
this.handleClose();
}

handleOpen = () => {
this.setState({
isOpen: true
});
}

render() {
const { children, title, message, confirmActionText, cancelActionText } = this.props;
const { isOpen } = this.state;

return (
<Fragment>
{children({
openDialog: this.handleOpen
})}
<Dialog
aria-labelledby="confirm-action-dialog-title"
maxWidth="sm"
onClose={this.handleClose}
open={isOpen}
>
<DialogTitle id="confirm-action-dialog-title">{title}</DialogTitle>
{message && (
<DialogContent>
<DialogContentText>{message}</DialogContentText>
</DialogContent>
)}

<DialogActions>
<Button onClick={this.handleClose} color="primary" variant="outlined">
{cancelActionText}
</Button>
<Button onClick={this.handleConfirm} color="primary" variant="contained">
{confirmActionText}
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}
}

export default ConfirmDialog;
1 change: 1 addition & 0 deletions imports/client/ui/components/ConfirmDialog/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ConfirmDialog";
44 changes: 44 additions & 0 deletions imports/client/ui/layouts/ContentViewFullLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Component provies a fill width and height, non-scrollable container
* for dashboard layouts that want to defin their on scroll zones.
*/
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import withStyles from "@material-ui/core/styles/withStyles";

const styles = (theme) => ({
root: {
width: "100vw",
height: "100vh",
paddingTop: theme.mixins.toolbar.minHeight,
background: "",
flexGrow: 1,
transition: "padding 225ms cubic-bezier(0, 0, 0.2, 1) 0ms",
overflow: "hidden"
},
leftSidebarOpen: {
paddingLeft: 280
}
});

const ContentViewFullLayout = ({ children, classes, isMobile, isSidebarOpen }) => (
<div
className={
classNames(classes.root, {
[classes.leftSidebarOpen]: isSidebarOpen && isMobile === false
})
}
>
{children}
</div>
);

ContentViewFullLayout.propTypes = {
children: PropTypes.node,
classes: PropTypes.object,
isMobile: PropTypes.bool,
isSidebarOpen: PropTypes.bool
};

export default withStyles(styles, { name: "RuiContentViewFullLayout" })(ContentViewFullLayout);
46 changes: 46 additions & 0 deletions imports/client/ui/layouts/ContentViewStandardLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import withStyles from "@material-ui/core/styles/withStyles";

const styles = (theme) => ({
root: {
width: "100vw",
flexGrow: 1,
transition: "padding 225ms cubic-bezier(0, 0, 0.2, 1) 0ms"
},
content: {
maxWidth: 1140,
paddingTop: theme.mixins.toolbar.minHeight + (theme.spacing.unit * 2),
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit * 2,
margin: "0 auto"
},
leftSidebarOpen: {
paddingLeft: 280
}
});

const ContentViewStandardLayout = ({ children, classes, isMobile, isSidebarOpen }) => (
<div
className={
classNames(classes.root, {
[classes.leftSidebarOpen]: isSidebarOpen && isMobile === false
})
}
>
<div className={classes.content}>
{children}
</div>
</div>
);

ContentViewStandardLayout.propTypes = {
children: PropTypes.node,
classes: PropTypes.object,
isMobile: PropTypes.bool,
isSidebarOpen: PropTypes.bool
};

export default withStyles(styles, { name: "RuiContentViewStandardLayout" })(ContentViewStandardLayout);
152 changes: 59 additions & 93 deletions imports/client/ui/layouts/Dashboard.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import styled, { css, injectGlobal } from "styled-components";
import styledMUI from "styled-components-mui";
import { compose } from "recompose";
import classNames from "classnames";
import styled, { injectGlobal } from "styled-components";
import { ContainerQuery } from "react-container-query";
import MUIAppBar from "@material-ui/core/AppBar";
import MUIToolbar from "@material-ui/core/Toolbar";
import IconButton from "@material-ui/core/IconButton";
import { applyTheme, CustomPropTypes } from "@reactioncommerce/components/utils";
import withStyles from "@material-ui/core/styles/withStyles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import { CustomPropTypes } from "@reactioncommerce/components/utils";
import { withComponents } from "@reactioncommerce/components-context";
import { Route, Switch } from "react-router";
import ProfileImageWithData from "../components/ProfileImageWithData";
import Sidebar from "../components/Sidebar";
import { operatorRoutes } from "../index";
import ContentViewFullLayout from "./ContentViewFullLayout";
import ContentViewStandardayout from "./ContentViewStandardLayout";

const query = {
isMobile: {
Expand All @@ -31,82 +34,26 @@ const Container = styled.div`
display: flex;
`;

// The reason we can't simply do `styled.div` here is because we're passing in isMobile and isSidebarOpen
// props for the styled-components conditionals, but React does not recognize these as valid attributes
// for DOM elements and prints warnings in the console. Someday there may be a better solution.
// See https://github.com/styled-components/styled-components/issues/305
const Main = styled(({ children, isMobile, isSidebarOpen, ...divProps }) => (<div {...divProps}>{children}</div>))`
width: 100vw;
background-color: ${applyTheme("Layout.pageBackgroundColor")};
flex-grow: 1;
transition: ${(props) =>
(props.isSidebarOpen && props.isMobile !== true
? "padding 225ms cubic-bezier(0, 0, 0.2, 1) 0ms"
: "padding 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms")};
padding-left: ${(props) => (props.isSidebarOpen && props.isMobile === false ? "280px" : 0)};
`;

const MainContent = styled.div`
max-width: ${applyTheme("Layout.pageContentMaxWidth")};
padding-bottom: ${applyTheme("Layout.pageContentPaddingBottom")};
padding-left: ${applyTheme("Layout.pageContentPaddingLeft")};
padding-right: ${applyTheme("Layout.pageContentPaddingRight")};
padding-top: ${applyTheme("Layout.pageContentPaddingTop")};
margin: 0 auto;
`;

// The reason we can't simply do `styledMUI(MUIAppBar)` here is because we're passing in isMobile and isSidebarOpen
// props for the styled-components conditionals, but React does not recognize these as valid attributes
// for DOM elements and prints warnings in the console. Someday there may be a better solution.
// See https://github.com/styled-components/styled-components/issues/305
const AppBar = styledMUI(({ children, isMobile, isSidebarOpen, ...restProps }) => (<MUIAppBar {...restProps}>{children}</MUIAppBar>))`
background-color: ${applyTheme("Layout.pageHeaderBackgroundColor")};
transition: ${(props) =>
(props.isSidebarOpen && props.isMobile !== true
? "margin 225ms cubic-bezier(0.0, 0, 0.2, 1) 0ms,width 225ms cubic-bezier(0.0, 0, 0.2, 1) 0ms"
: "margin 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms,width 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms")};
${(props) => {
if (props.isSidebarOpen && props.isMobile !== true) {
return css`
margin-left: ${applyTheme("Sidebar.drawerWidth")};
width: calc(100% - ${applyTheme("Sidebar.drawerWidth")});`;
}
return null;
}};
`;

const Grow = styled.div`
flex-grow: 1;
`;

const HamburgerIconButton = styledMUI(IconButton)`
color: ${applyTheme("Layout.burgerIconColor")};
position: fixed;
left: ${applyTheme("Layout.burgerIconLeft")};
top: ${applyTheme("Layout.burgerIconTop")};
z-index: 2000;
`;

// This is an invisible element that is needed only to push the page content down below the `AppBar`
const DrawerHeader = styled.div`
min-height: 48px;
@media (min-width: 600px) {
min-height: 64px;
const styles = (theme) => ({
leftSidebarOpen: {
paddingLeft: 280 + (theme.spacing.unit * 2)
}
@media (min-width: 0px) and (orientation: landscape) {
min-height: 48px;
}
`;
});

class Dashboard extends Component {
static propTypes = {
classes: PropTypes.object,
components: PropTypes.shape({
IconHamburger: CustomPropTypes.component.isRequired
})
};

state = {
isSidebarOpen: null
isSidebarOpen: true
};

handleDrawerClose = () => {
Expand All @@ -118,8 +65,15 @@ class Dashboard extends Component {
};

render() {
const { components: { IconHamburger } } = this.props;
const { classes } = this.props;
const { isSidebarOpen } = this.state;
const uiState = {
isLeftDrawerOpen: isSidebarOpen
};

const toolbarClassName = classNames({
[classes.leftSidebarOpen]: uiState.isLeftDrawerOpen
});

return (
<ContainerQuery query={query}>
Expand All @@ -137,37 +91,46 @@ class Dashboard extends Component {

return (
<Container>
<HamburgerIconButton onClick={this.handleDrawerToggle}>
<IconHamburger />
</HamburgerIconButton>
<AppBar elevation={0} position="fixed" isMobile={isMobile} isSidebarOpen={isSidebarOpen}>
<MUIToolbar>
<AppBar position="fixed">
<Toolbar className={toolbarClassName}>
<Grow />
<ProfileImageWithData size={40} />
</MUIToolbar>
</Toolbar>
</AppBar>
<Sidebar
isMobile={isMobile}
isSidebarOpen={isSidebarOpen || false}
isSidebarOpen={true}
onDrawerClose={this.handleDrawerClose}
routes={operatorRoutes}
/>
<Main isMobile={isMobile} isSidebarOpen={isSidebarOpen}>
<DrawerHeader />
<MainContent>
<Switch>
{
operatorRoutes.map((route) => (
<Route
key={route.path}
path={`/operator${route.path}`}
component={route.mainComponent}
/>
))
}
</Switch>
</MainContent>
</Main>
<Switch>
{
operatorRoutes.map((route) => (
<Route
key={route.path}
path={`/operator${route.path}`}
render={(props) => {
// If the layout component is explicitly null
if (route.layoutComponent === null) {
return (
<ContentViewFullLayout isMobile={isMobile} isSidebarOpen={isSidebarOpen}>
<route.mainComponent uiState={uiState} {...props} />;
</ContentViewFullLayout>
);
}

const LayoutComponent = route.layoutComponent || ContentViewStandardayout;

return (
<LayoutComponent isMobile={isMobile} isSidebarOpen={isSidebarOpen}>
<route.mainComponent uiState={uiState} {...props} />
</LayoutComponent>
);
}}
/>
))
}
</Switch>
</Container>
);
}}
Expand All @@ -176,4 +139,7 @@ class Dashboard extends Component {
}
}

export default withComponents(Dashboard);
export default compose(
withComponents,
withStyles(styles, { name: "RuiDashboard" })
)(Dashboard);
Loading

0 comments on commit c4a17d6

Please sign in to comment.