Make layout responsive
This commit is contained in:
parent
e858d5ad3f
commit
c46bbdc01f
|
|
@ -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,18 +10,46 @@ 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) => ({
|
||||
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',
|
||||
},
|
||||
|
|
@ -34,7 +62,7 @@ const styles = (theme: Theme) => ({
|
|||
},
|
||||
});
|
||||
|
||||
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<IProps> {
|
||||
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 (
|
||||
<AppBar position="absolute" style={style} className={classes.appBar}>
|
||||
<Toolbar>
|
||||
<AppBar position={position} style={style} className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<div className={classes.title}>
|
||||
<Link to="/" className={classes.link}>
|
||||
<Typography variant="h5" className={classes.titleName} color="inherit">
|
||||
|
|
@ -69,7 +112,8 @@ class Header extends Component<IProps> {
|
|||
</Typography>
|
||||
</a>
|
||||
</div>
|
||||
{loggedIn && this.renderButtons(name, admin, logout)}
|
||||
{loggedIn && this.renderButtons(name, admin, logout, width, setNavOpen)}
|
||||
<div>
|
||||
<IconButton onClick={toggleTheme} color="inherit">
|
||||
<Highlight />
|
||||
</IconButton>
|
||||
|
|
@ -79,55 +123,97 @@ class Header extends Component<IProps> {
|
|||
<GitHubIcon />
|
||||
</IconButton>
|
||||
</a>
|
||||
</div>
|
||||
</Toolbar>
|
||||
</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;
|
||||
return (
|
||||
<div>
|
||||
{admin ? (
|
||||
<div className={classes.menuButtons}>
|
||||
<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">
|
||||
<Button color="inherit">
|
||||
<SupervisorAccount />
|
||||
users
|
||||
</Button>
|
||||
<ResponsiveButton
|
||||
icon={<SupervisorAccount />}
|
||||
label="users"
|
||||
width={width}
|
||||
color="inherit"
|
||||
/>
|
||||
</Link>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<Link className={classes.link} to="/applications" id="navigate-apps">
|
||||
<Button color="inherit">
|
||||
<Chat />
|
||||
apps
|
||||
</Button>
|
||||
<ResponsiveButton icon={<Chat />} label="apps" width={width} color="inherit" />
|
||||
</Link>
|
||||
<Link className={classes.link} to="/clients" id="navigate-clients">
|
||||
<Button color="inherit">
|
||||
<DevicesOther />
|
||||
clients
|
||||
</Button>
|
||||
<ResponsiveButton
|
||||
icon={<DevicesOther />}
|
||||
label="clients"
|
||||
width={width}
|
||||
color="inherit"
|
||||
/>
|
||||
</Link>
|
||||
<Link className={classes.link} to="/plugins" id="navigate-plugins">
|
||||
<Button color="inherit">
|
||||
<Apps />
|
||||
plugins
|
||||
</Button>
|
||||
<ResponsiveButton
|
||||
icon={<Apps />}
|
||||
label="plugins"
|
||||
width={width}
|
||||
color="inherit"
|
||||
/>
|
||||
</Link>
|
||||
<Button color="inherit" onClick={showSettings} id="changepw">
|
||||
<AccountCircle />
|
||||
|
||||
{name}
|
||||
</Button>
|
||||
<Button color="inherit" onClick={logout} id="logout">
|
||||
<ExitToApp />
|
||||
Logout
|
||||
</Button>
|
||||
<ResponsiveButton
|
||||
icon={<AccountCircle />}
|
||||
label={name}
|
||||
onClick={showSettings}
|
||||
id="changepw"
|
||||
width={width}
|
||||
color="inherit"
|
||||
/>
|
||||
<ResponsiveButton
|
||||
icon={<ExitToApp />}
|
||||
label="Logout"
|
||||
onClick={logout}
|
||||
id="logout"
|
||||
width={width}
|
||||
color="inherit"
|
||||
/>
|
||||
</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,
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
<div style={{display: 'flex'}}>
|
||||
<div style={{display: 'flex', flexDirection: 'column'}}>
|
||||
<CssBaseline />
|
||||
<Header
|
||||
style={{top: !connectionErrorMessage ? 0 : 64}}
|
||||
|
|
@ -116,9 +125,14 @@ class Layout extends React.Component<
|
|||
toggleTheme={this.toggleTheme.bind(this)}
|
||||
showSettings={() => (this.showSettings = true)}
|
||||
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}>
|
||||
<Switch>
|
||||
{authenticating ? (
|
||||
|
|
@ -130,13 +144,22 @@ class Layout extends React.Component<
|
|||
{loggedIn ? null : <Redirect to="/login" />}
|
||||
<Route exact path="/" 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="/users" component={Users} />
|
||||
<Route exact path="/plugins" component={Plugins} />
|
||||
<Route exact path="/plugins/:id" component={PluginDetailView} />
|
||||
<Route
|
||||
exact
|
||||
path="/plugins/:id"
|
||||
component={PluginDetailView}
|
||||
/>
|
||||
</Switch>
|
||||
</main>
|
||||
</div>
|
||||
{showSettings && (
|
||||
<SettingsDialog fClose={() => (this.showSettings = false)} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Link
|
||||
onClick={() => setNavOpen(false)}
|
||||
className={`${classes.link} item`}
|
||||
to={'/messages/' + app.id}
|
||||
key={app.id}>
|
||||
|
|
@ -68,12 +73,13 @@ class Navigation extends Component<
|
|||
];
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
<ResponsiveDrawer
|
||||
classes={{paper: classes.drawerPaper}}
|
||||
navOpen={navOpen}
|
||||
setNavOpen={setNavOpen}
|
||||
id="message-navigation">
|
||||
<div className={classes.toolbar} />
|
||||
<Link className={classes.link} to="/">
|
||||
<Link className={classes.link} to="/" onClick={() => setNavOpen(false)}>
|
||||
<ListItem button disabled={!loggedIn} className="all">
|
||||
<ListItemText primary="All Messages" />
|
||||
</ListItem>
|
||||
|
|
@ -92,9 +98,31 @@ class Navigation extends Component<
|
|||
</Button>
|
||||
) : null}
|
||||
</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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue