From c46bbdc01fd8939013340a33dd4fc87c72312913 Mon Sep 17 00:00:00 2001 From: Yasa Akbulut Date: Fri, 13 Mar 2020 02:01:49 +0100 Subject: [PATCH] Make layout responsive --- ui/src/layout/Header.tsx | 210 ++++++++++++++++++++++++----------- ui/src/layout/Layout.tsx | 65 +++++++---- ui/src/layout/Navigation.tsx | 40 ++++++- 3 files changed, 226 insertions(+), 89 deletions(-) diff --git a/ui/src/layout/Header.tsx b/ui/src/layout/Header.tsx index c1988d2..6c8aa6e 100644 --- a/ui/src/layout/Header.tsx +++ b/ui/src/layout/Header.tsx @@ -1,7 +1,7 @@ import AppBar from '@material-ui/core/AppBar'; import Button from '@material-ui/core/Button'; import IconButton from '@material-ui/core/IconButton'; -import {Theme, WithStyles, withStyles} from '@material-ui/core/styles'; +import {createStyles, Theme, WithStyles, withStyles} from '@material-ui/core/styles'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import AccountCircle from '@material-ui/icons/AccountCircle'; @@ -10,31 +10,59 @@ import DevicesOther from '@material-ui/icons/DevicesOther'; import ExitToApp from '@material-ui/icons/ExitToApp'; import Highlight from '@material-ui/icons/Highlight'; import GitHubIcon from '@material-ui/icons/GitHub'; +import MenuIcon from '@material-ui/icons/Menu'; import Apps from '@material-ui/icons/Apps'; import SupervisorAccount from '@material-ui/icons/SupervisorAccount'; import React, {Component, CSSProperties} from 'react'; import {Link} from 'react-router-dom'; import {observer} from 'mobx-react'; +import {Hidden, PropTypes, withWidth} from '@material-ui/core'; +import {Breakpoint} from '@material-ui/core/styles/createBreakpoints'; -const styles = (theme: Theme) => ({ - appBar: { - zIndex: theme.zIndex.drawer + 1, - }, - title: { - flex: 1, - display: 'flex', - alignItems: 'center', - }, - titleName: { - paddingRight: 10, - }, - link: { - color: 'inherit', - textDecoration: 'none', - }, -}); +const styles = (theme: Theme) => + createStyles({ + appBar: { + zIndex: theme.zIndex.drawer + 1, + [theme.breakpoints.down('xs')]: { + paddingBottom: 10, + }, + }, + toolbar: { + justifyContent: 'space-between', + [theme.breakpoints.down('xs')]: { + flexWrap: 'wrap', + }, + }, + menuButtons: { + display: 'flex', + [theme.breakpoints.down('sm')]: { + flex: 1, + }, + justifyContent: 'center', + [theme.breakpoints.down('xs')]: { + flexBasis: '100%', + marginTop: 5, + order: 1, + justifyContent: 'space-between', + }, + }, + title: { + [theme.breakpoints.up('md')]: { + flex: 1, + }, + display: 'flex', + alignItems: 'center', + }, + titleName: { + paddingRight: 10, + }, + link: { + color: 'inherit', + textDecoration: 'none', + }, + }); -type Styles = WithStyles<'link' | 'titleName' | 'title' | 'appBar'>; +type Styles = WithStyles<'link' | 'menuButtons' | 'toolbar' | 'titleName' | 'title' | 'appBar'>; interface IProps extends Styles { loggedIn: boolean; @@ -45,16 +73,31 @@ interface IProps extends Styles { showSettings: VoidFunction; logout: VoidFunction; style: CSSProperties; + width: Breakpoint; + setNavOpen: (open: boolean) => void; } @observer class Header extends Component { public render() { - const {classes, version, name, loggedIn, admin, toggleTheme, logout, style} = this.props; + const { + classes, + version, + name, + loggedIn, + admin, + toggleTheme, + logout, + style, + setNavOpen, + width, + } = this.props; + + const position = width === 'xs' ? 'sticky' : 'fixed'; return ( - - + +
@@ -69,65 +112,108 @@ class Header extends Component {
- {loggedIn && this.renderButtons(name, admin, logout)} - - - - - - - + {loggedIn && this.renderButtons(name, admin, logout, width, setNavOpen)} +
); } - private renderButtons(name: string, admin: boolean, logout: VoidFunction) { + private renderButtons( + name: string, + admin: boolean, + logout: VoidFunction, + width: Breakpoint, + setNavOpen: (open: boolean) => void + ) { const {classes, showSettings} = this.props; return ( -
- {admin ? ( +
+ + } + onClick={() => setNavOpen(true)} + label="menu" + width={width} + color="inherit" + /> + + {admin && ( - + } + label="users" + width={width} + color="inherit" + /> - ) : ( - '' )} - + } label="apps" width={width} color="inherit" /> - + } + label="clients" + width={width} + color="inherit" + /> - + } + label="plugins" + width={width} + color="inherit" + /> - - + } + label={name} + onClick={showSettings} + id="changepw" + width={width} + color="inherit" + /> + } + label="Logout" + onClick={logout} + id="logout" + width={width} + color="inherit" + />
); } } -export default withStyles(styles, {withTheme: true})(Header); +const ResponsiveButton: React.FC<{ + width: Breakpoint; + color: PropTypes.Color; + label: string; + id?: string; + onClick?: () => void; + icon: React.ReactNode; +}> = ({width, icon, children, label, ...rest}) => { + if (width === 'xs' || width === 'sm') { + return {icon}; + } + return ( + + ); +}; + +export default withWidth()(withStyles(styles, {withTheme: true})(Header)); diff --git a/ui/src/layout/Layout.tsx b/ui/src/layout/Layout.tsx index 7fdf906..d7f23df 100644 --- a/ui/src/layout/Layout.tsx +++ b/ui/src/layout/Layout.tsx @@ -29,6 +29,9 @@ const styles = (theme: Theme) => ({ marginTop: 64, padding: theme.spacing(4), width: '100%', + [theme.breakpoints.down('xs')]: { + marginTop: 0, + }, }, }); @@ -63,6 +66,12 @@ class Layout extends React.Component< private showSettings = false; @observable private version = Layout.defaultVersion; + @observable + private navOpen = false; + + private setNavOpen(open: boolean) { + this.navOpen = open; + } public componentDidMount() { if (this.version === Layout.defaultVersion) { @@ -105,7 +114,7 @@ class Layout extends React.Component< message={connectionErrorMessage} /> )} -
+
(this.showSettings = true)} logout={logout} + setNavOpen={this.setNavOpen.bind(this)} /> - - -
- - {authenticating ? ( - - - - ) : null} - - {loggedIn ? null : } - - - - - - - - -
+
+ +
+ + {authenticating ? ( + + + + ) : null} + + {loggedIn ? null : } + + + + + + + + +
+
{showSettings && ( (this.showSettings = false)} /> )} diff --git a/ui/src/layout/Navigation.tsx b/ui/src/layout/Navigation.tsx index 773b275..d83159d 100644 --- a/ui/src/layout/Navigation.tsx +++ b/ui/src/layout/Navigation.tsx @@ -8,7 +8,9 @@ import {Link} from 'react-router-dom'; import {observer} from 'mobx-react'; import {inject, Stores} from '../inject'; import {mayAllowPermission, requestPermission} from '../snack/browserNotification'; -import {Button, Typography} from '@material-ui/core'; +import {Button, Hidden, IconButton, Typography} from '@material-ui/core'; +import {DrawerProps} from '@material-ui/core/Drawer/Drawer'; +import CloseIcon from '@material-ui/icons/Close'; const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({ drawerPaper: { @@ -28,6 +30,8 @@ type Styles = WithStyles<'drawerPaper' | 'toolbar' | 'link'>; interface IProps { loggedIn: boolean; + navOpen: boolean; + setNavOpen: (open: boolean) => void; } @observer @@ -38,7 +42,7 @@ class Navigation extends Component< public state = {showRequestNotification: mayAllowPermission()}; public render() { - const {classes, loggedIn, appStore} = this.props; + const {classes, loggedIn, appStore, navOpen, setNavOpen} = this.props; const {showRequestNotification} = this.state; const apps = appStore.getItems(); @@ -48,6 +52,7 @@ class Navigation extends Component< : apps.map((app) => { return ( setNavOpen(false)} className={`${classes.link} item`} to={'/messages/' + app.id} key={app.id}> @@ -68,12 +73,13 @@ class Navigation extends Component< ]; return ( -
- + setNavOpen(false)}> @@ -92,9 +98,31 @@ class Navigation extends Component< ) : null} - + ); } } +const ResponsiveDrawer: React.FC< + DrawerProps & {navOpen: boolean; setNavOpen: (open: boolean) => void} +> = ({navOpen, setNavOpen, children, ...rest}) => { + return ( + <> + + + setNavOpen(false)}> + + + {children} + + + + + {children} + + + + ); +}; + export default withStyles(styles, {withTheme: true})(inject('appStore')(Navigation));