Make layout responsive
This commit is contained in:
parent
e858d5ad3f
commit
c46bbdc01f
|
|
@ -1,7 +1,7 @@
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
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 Toolbar from '@material-ui/core/Toolbar';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||||
|
|
@ -10,18 +10,46 @@ import DevicesOther from '@material-ui/icons/DevicesOther';
|
||||||
import ExitToApp from '@material-ui/icons/ExitToApp';
|
import ExitToApp from '@material-ui/icons/ExitToApp';
|
||||||
import Highlight from '@material-ui/icons/Highlight';
|
import Highlight from '@material-ui/icons/Highlight';
|
||||||
import GitHubIcon from '@material-ui/icons/GitHub';
|
import GitHubIcon from '@material-ui/icons/GitHub';
|
||||||
|
import MenuIcon from '@material-ui/icons/Menu';
|
||||||
import Apps from '@material-ui/icons/Apps';
|
import Apps from '@material-ui/icons/Apps';
|
||||||
import SupervisorAccount from '@material-ui/icons/SupervisorAccount';
|
import SupervisorAccount from '@material-ui/icons/SupervisorAccount';
|
||||||
import React, {Component, CSSProperties} from 'react';
|
import React, {Component, CSSProperties} from 'react';
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import {observer} from 'mobx-react';
|
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) => ({
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
appBar: {
|
appBar: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
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: {
|
title: {
|
||||||
|
[theme.breakpoints.up('md')]: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
},
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
|
@ -32,9 +60,9 @@ const styles = (theme: Theme) => ({
|
||||||
color: 'inherit',
|
color: 'inherit',
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
type Styles = WithStyles<'link' | 'titleName' | 'title' | 'appBar'>;
|
type Styles = WithStyles<'link' | 'menuButtons' | 'toolbar' | 'titleName' | 'title' | 'appBar'>;
|
||||||
|
|
||||||
interface IProps extends Styles {
|
interface IProps extends Styles {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
|
|
@ -45,16 +73,31 @@ interface IProps extends Styles {
|
||||||
showSettings: VoidFunction;
|
showSettings: VoidFunction;
|
||||||
logout: VoidFunction;
|
logout: VoidFunction;
|
||||||
style: CSSProperties;
|
style: CSSProperties;
|
||||||
|
width: Breakpoint;
|
||||||
|
setNavOpen: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class Header extends Component<IProps> {
|
class Header extends Component<IProps> {
|
||||||
public render() {
|
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 (
|
return (
|
||||||
<AppBar position="absolute" style={style} className={classes.appBar}>
|
<AppBar position={position} style={style} className={classes.appBar}>
|
||||||
<Toolbar>
|
<Toolbar className={classes.toolbar}>
|
||||||
<div className={classes.title}>
|
<div className={classes.title}>
|
||||||
<Link to="/" className={classes.link}>
|
<Link to="/" className={classes.link}>
|
||||||
<Typography variant="h5" className={classes.titleName} color="inherit">
|
<Typography variant="h5" className={classes.titleName} color="inherit">
|
||||||
|
|
@ -69,7 +112,8 @@ class Header extends Component<IProps> {
|
||||||
</Typography>
|
</Typography>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{loggedIn && this.renderButtons(name, admin, logout)}
|
{loggedIn && this.renderButtons(name, admin, logout, width, setNavOpen)}
|
||||||
|
<div>
|
||||||
<IconButton onClick={toggleTheme} color="inherit">
|
<IconButton onClick={toggleTheme} color="inherit">
|
||||||
<Highlight />
|
<Highlight />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
@ -79,55 +123,97 @@ class Header extends Component<IProps> {
|
||||||
<GitHubIcon />
|
<GitHubIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
const {classes, showSettings} = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={classes.menuButtons}>
|
||||||
{admin ? (
|
<Hidden smUp implementation="css">
|
||||||
|
<ResponsiveButton
|
||||||
|
icon={<MenuIcon />}
|
||||||
|
onClick={() => setNavOpen(true)}
|
||||||
|
label="menu"
|
||||||
|
width={width}
|
||||||
|
color="inherit"
|
||||||
|
/>
|
||||||
|
</Hidden>
|
||||||
|
{admin && (
|
||||||
<Link className={classes.link} to="/users" id="navigate-users">
|
<Link className={classes.link} to="/users" id="navigate-users">
|
||||||
<Button color="inherit">
|
<ResponsiveButton
|
||||||
<SupervisorAccount />
|
icon={<SupervisorAccount />}
|
||||||
users
|
label="users"
|
||||||
</Button>
|
width={width}
|
||||||
|
color="inherit"
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
)}
|
||||||
<Link className={classes.link} to="/applications" id="navigate-apps">
|
<Link className={classes.link} to="/applications" id="navigate-apps">
|
||||||
<Button color="inherit">
|
<ResponsiveButton icon={<Chat />} label="apps" width={width} color="inherit" />
|
||||||
<Chat />
|
|
||||||
apps
|
|
||||||
</Button>
|
|
||||||
</Link>
|
</Link>
|
||||||
<Link className={classes.link} to="/clients" id="navigate-clients">
|
<Link className={classes.link} to="/clients" id="navigate-clients">
|
||||||
<Button color="inherit">
|
<ResponsiveButton
|
||||||
<DevicesOther />
|
icon={<DevicesOther />}
|
||||||
clients
|
label="clients"
|
||||||
</Button>
|
width={width}
|
||||||
|
color="inherit"
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<Link className={classes.link} to="/plugins" id="navigate-plugins">
|
<Link className={classes.link} to="/plugins" id="navigate-plugins">
|
||||||
<Button color="inherit">
|
<ResponsiveButton
|
||||||
<Apps />
|
icon={<Apps />}
|
||||||
plugins
|
label="plugins"
|
||||||
</Button>
|
width={width}
|
||||||
|
color="inherit"
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<Button color="inherit" onClick={showSettings} id="changepw">
|
<ResponsiveButton
|
||||||
<AccountCircle />
|
icon={<AccountCircle />}
|
||||||
|
label={name}
|
||||||
{name}
|
onClick={showSettings}
|
||||||
</Button>
|
id="changepw"
|
||||||
<Button color="inherit" onClick={logout} id="logout">
|
width={width}
|
||||||
<ExitToApp />
|
color="inherit"
|
||||||
Logout
|
/>
|
||||||
</Button>
|
<ResponsiveButton
|
||||||
|
icon={<ExitToApp />}
|
||||||
|
label="Logout"
|
||||||
|
onClick={logout}
|
||||||
|
id="logout"
|
||||||
|
width={width}
|
||||||
|
color="inherit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 <IconButton {...rest}>{icon}</IconButton>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button startIcon={icon} {...rest}>
|
||||||
|
{label}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withWidth()(withStyles(styles, {withTheme: true})(Header));
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ const styles = (theme: Theme) => ({
|
||||||
marginTop: 64,
|
marginTop: 64,
|
||||||
padding: theme.spacing(4),
|
padding: theme.spacing(4),
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
[theme.breakpoints.down('xs')]: {
|
||||||
|
marginTop: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -63,6 +66,12 @@ class Layout extends React.Component<
|
||||||
private showSettings = false;
|
private showSettings = false;
|
||||||
@observable
|
@observable
|
||||||
private version = Layout.defaultVersion;
|
private version = Layout.defaultVersion;
|
||||||
|
@observable
|
||||||
|
private navOpen = false;
|
||||||
|
|
||||||
|
private setNavOpen(open: boolean) {
|
||||||
|
this.navOpen = open;
|
||||||
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
if (this.version === Layout.defaultVersion) {
|
if (this.version === Layout.defaultVersion) {
|
||||||
|
|
@ -105,7 +114,7 @@ class Layout extends React.Component<
|
||||||
message={connectionErrorMessage}
|
message={connectionErrorMessage}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div style={{display: 'flex'}}>
|
<div style={{display: 'flex', flexDirection: 'column'}}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Header
|
<Header
|
||||||
style={{top: !connectionErrorMessage ? 0 : 64}}
|
style={{top: !connectionErrorMessage ? 0 : 64}}
|
||||||
|
|
@ -116,9 +125,14 @@ class Layout extends React.Component<
|
||||||
toggleTheme={this.toggleTheme.bind(this)}
|
toggleTheme={this.toggleTheme.bind(this)}
|
||||||
showSettings={() => (this.showSettings = true)}
|
showSettings={() => (this.showSettings = true)}
|
||||||
logout={logout}
|
logout={logout}
|
||||||
|
setNavOpen={this.setNavOpen.bind(this)}
|
||||||
|
/>
|
||||||
|
<div style={{display: 'flex'}}>
|
||||||
|
<Navigation
|
||||||
|
loggedIn={loggedIn}
|
||||||
|
navOpen={this.navOpen}
|
||||||
|
setNavOpen={this.setNavOpen.bind(this)}
|
||||||
/>
|
/>
|
||||||
<Navigation loggedIn={loggedIn} />
|
|
||||||
|
|
||||||
<main className={classes.content}>
|
<main className={classes.content}>
|
||||||
<Switch>
|
<Switch>
|
||||||
{authenticating ? (
|
{authenticating ? (
|
||||||
|
|
@ -130,13 +144,22 @@ class Layout extends React.Component<
|
||||||
{loggedIn ? null : <Redirect to="/login" />}
|
{loggedIn ? null : <Redirect to="/login" />}
|
||||||
<Route exact path="/" component={Messages} />
|
<Route exact path="/" component={Messages} />
|
||||||
<Route exact path="/messages/:id" component={Messages} />
|
<Route exact path="/messages/:id" component={Messages} />
|
||||||
<Route exact path="/applications" component={Applications} />
|
<Route
|
||||||
|
exact
|
||||||
|
path="/applications"
|
||||||
|
component={Applications}
|
||||||
|
/>
|
||||||
<Route exact path="/clients" component={Clients} />
|
<Route exact path="/clients" component={Clients} />
|
||||||
<Route exact path="/users" component={Users} />
|
<Route exact path="/users" component={Users} />
|
||||||
<Route exact path="/plugins" component={Plugins} />
|
<Route exact path="/plugins" component={Plugins} />
|
||||||
<Route exact path="/plugins/:id" component={PluginDetailView} />
|
<Route
|
||||||
|
exact
|
||||||
|
path="/plugins/:id"
|
||||||
|
component={PluginDetailView}
|
||||||
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{showSettings && (
|
{showSettings && (
|
||||||
<SettingsDialog fClose={() => (this.showSettings = false)} />
|
<SettingsDialog fClose={() => (this.showSettings = false)} />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import {Link} from 'react-router-dom';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import {inject, Stores} from '../inject';
|
import {inject, Stores} from '../inject';
|
||||||
import {mayAllowPermission, requestPermission} from '../snack/browserNotification';
|
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'> => ({
|
const styles = (theme: Theme): StyleRules<'drawerPaper' | 'toolbar' | 'link'> => ({
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
|
|
@ -28,6 +30,8 @@ type Styles = WithStyles<'drawerPaper' | 'toolbar' | 'link'>;
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
|
navOpen: boolean;
|
||||||
|
setNavOpen: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
|
@ -38,7 +42,7 @@ class Navigation extends Component<
|
||||||
public state = {showRequestNotification: mayAllowPermission()};
|
public state = {showRequestNotification: mayAllowPermission()};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {classes, loggedIn, appStore} = this.props;
|
const {classes, loggedIn, appStore, navOpen, setNavOpen} = this.props;
|
||||||
const {showRequestNotification} = this.state;
|
const {showRequestNotification} = this.state;
|
||||||
const apps = appStore.getItems();
|
const apps = appStore.getItems();
|
||||||
|
|
||||||
|
|
@ -48,6 +52,7 @@ class Navigation extends Component<
|
||||||
: apps.map((app) => {
|
: apps.map((app) => {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
onClick={() => setNavOpen(false)}
|
||||||
className={`${classes.link} item`}
|
className={`${classes.link} item`}
|
||||||
to={'/messages/' + app.id}
|
to={'/messages/' + app.id}
|
||||||
key={app.id}>
|
key={app.id}>
|
||||||
|
|
@ -68,12 +73,13 @@ class Navigation extends Component<
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<ResponsiveDrawer
|
||||||
variant="permanent"
|
|
||||||
classes={{paper: classes.drawerPaper}}
|
classes={{paper: classes.drawerPaper}}
|
||||||
|
navOpen={navOpen}
|
||||||
|
setNavOpen={setNavOpen}
|
||||||
id="message-navigation">
|
id="message-navigation">
|
||||||
<div className={classes.toolbar} />
|
<div className={classes.toolbar} />
|
||||||
<Link className={classes.link} to="/">
|
<Link className={classes.link} to="/" onClick={() => setNavOpen(false)}>
|
||||||
<ListItem button disabled={!loggedIn} className="all">
|
<ListItem button disabled={!loggedIn} className="all">
|
||||||
<ListItemText primary="All Messages" />
|
<ListItemText primary="All Messages" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
@ -92,9 +98,31 @@ class Navigation extends Component<
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Drawer>
|
</ResponsiveDrawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ResponsiveDrawer: React.FC<
|
||||||
|
DrawerProps & {navOpen: boolean; setNavOpen: (open: boolean) => void}
|
||||||
|
> = ({navOpen, setNavOpen, children, ...rest}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Hidden smUp implementation="css">
|
||||||
|
<Drawer variant="temporary" open={navOpen} {...rest}>
|
||||||
|
<IconButton onClick={() => setNavOpen(false)}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
{children}
|
||||||
|
</Drawer>
|
||||||
|
</Hidden>
|
||||||
|
<Hidden xsDown implementation="css">
|
||||||
|
<Drawer variant="permanent" {...rest}>
|
||||||
|
{children}
|
||||||
|
</Drawer>
|
||||||
|
</Hidden>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default withStyles(styles, {withTheme: true})(inject('appStore')(Navigation));
|
export default withStyles(styles, {withTheme: true})(inject('appStore')(Navigation));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue