Merge pull request #57 from spotify/alund/new-nav
New collapsing navigation
This commit is contained in:
@@ -36,7 +36,7 @@ const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
display: 'grid',
|
||||
// FIXME: Don't used a fixed width here
|
||||
gridTemplateColumns: '224px auto',
|
||||
gridTemplateColumns: '64px auto',
|
||||
gridTemplateRows: 'auto 1fr',
|
||||
width: '100%',
|
||||
height: '100vh',
|
||||
|
||||
@@ -1,60 +1,221 @@
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Link from '@material-ui/core/Link';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, useState, createContext, useContext } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
makeStyles,
|
||||
SvgIcon,
|
||||
Typography,
|
||||
Link,
|
||||
styled,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
import ServiceIcon from '@material-ui/icons/DeviceHub';
|
||||
import WebIcon from '@material-ui/icons/Language';
|
||||
import LibIcon from '@material-ui/icons/LocalLibrary';
|
||||
import CreateIcon from '@material-ui/icons/AddCircleOutline';
|
||||
|
||||
const drawerWidthClosed = 64;
|
||||
const drawerWidthOpen = 220;
|
||||
|
||||
const Context = createContext<boolean>(false);
|
||||
|
||||
const useSidebarItemStyles = makeStyles({
|
||||
root: {
|
||||
color: '#b5b5b5',
|
||||
display: 'flex',
|
||||
flexFlow: 'row nowrap',
|
||||
alignItems: 'center',
|
||||
height: 40,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
closed: {
|
||||
width: drawerWidthClosed,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
open: {
|
||||
width: drawerWidthOpen,
|
||||
},
|
||||
iconContainer: {
|
||||
height: '100%',
|
||||
width: drawerWidthClosed,
|
||||
marginRight: -16,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
type SidebarItemProps = {
|
||||
icon: typeof SvgIcon;
|
||||
text: string;
|
||||
to?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const SidebarItem: FC<SidebarItemProps> = ({
|
||||
icon: Icon,
|
||||
text,
|
||||
to,
|
||||
onClick,
|
||||
}) => {
|
||||
const classes = useSidebarItemStyles();
|
||||
const open = useContext(Context);
|
||||
|
||||
if (!open) {
|
||||
return (
|
||||
<Link
|
||||
className={clsx(classes.root, classes.closed)}
|
||||
href={to}
|
||||
onClick={onClick}
|
||||
underline="none"
|
||||
>
|
||||
<Icon fontSize="small" />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={clsx(classes.root, classes.open)}
|
||||
href={to}
|
||||
onClick={onClick}
|
||||
underline="none"
|
||||
>
|
||||
<div className={classes.iconContainer}>
|
||||
<Icon fontSize="small" />
|
||||
</div>
|
||||
<Typography variant="subtitle2">{text}</Typography>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const useSidebarLogoStyles = makeStyles({
|
||||
root: {
|
||||
height: drawerWidthClosed,
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
flexFlow: 'row nowrap',
|
||||
alignItems: 'center',
|
||||
},
|
||||
logoContainer: {
|
||||
width: drawerWidthClosed,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
logo: {
|
||||
fontSize: 32,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginLeft: 22,
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
titleDot: {
|
||||
color: '#1DB954',
|
||||
},
|
||||
});
|
||||
|
||||
const SidebarLogo: FC<{}> = () => {
|
||||
const classes = useSidebarLogoStyles();
|
||||
const open = useContext(Context);
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Typography variant="h6" color="inherit" className={classes.title}>
|
||||
{open ? 'Backstage' : 'B'}
|
||||
<span className={classes.titleDot}>.</span>
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Space = styled('div')({
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
const Spacer = styled('div')({
|
||||
height: 8,
|
||||
});
|
||||
|
||||
const Divider = styled('hr')({
|
||||
height: 1,
|
||||
width: '100%',
|
||||
background: '#383838',
|
||||
border: 'none',
|
||||
});
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
background: '#181818',
|
||||
color: 'white',
|
||||
zIndex: 1000,
|
||||
position: 'relative',
|
||||
overflow: 'visible',
|
||||
width: theme.spacing(7) + 1,
|
||||
height: '100vh',
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingTop: theme.spacing(2),
|
||||
},
|
||||
drawer: {
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
alignItems: 'flex-start',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
padding: 0,
|
||||
background: '#171717',
|
||||
overflowX: 'hidden',
|
||||
width: drawerWidthClosed,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.shortest,
|
||||
}),
|
||||
},
|
||||
drawerOpen: {
|
||||
width: drawerWidthOpen,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.shorter,
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
const SideBar: FC<{}> = () => {
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Link href="#" variant="body1">
|
||||
[Co-brand Logo]
|
||||
</Link>
|
||||
<List component="nav">
|
||||
<ListItem button>
|
||||
<ListItemText primary="Home" />
|
||||
</ListItem>
|
||||
<ListItem button>
|
||||
<ListItemText primary="Services" />
|
||||
</ListItem>
|
||||
<ListItem button>
|
||||
<ListItemText primary="Libraries" />
|
||||
</ListItem>
|
||||
<ListItem button>
|
||||
<ListItemText primary="Websites" />
|
||||
</ListItem>
|
||||
<ListItem button>
|
||||
<ListItemText primary="+ Create..." />
|
||||
</ListItem>
|
||||
</List>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justify="flex-start"
|
||||
alignItems="center"
|
||||
spacing={2}
|
||||
>
|
||||
<Grid item>
|
||||
<Avatar>A</Avatar>
|
||||
</Grid>
|
||||
<Typography variant="caption">$userName</Typography>
|
||||
</Grid>
|
||||
<div
|
||||
className={classes.root}
|
||||
onMouseEnter={handleOpen}
|
||||
onFocus={handleOpen}
|
||||
onMouseLeave={handleClose}
|
||||
onBlur={handleClose}
|
||||
data-testid="sidebar-root"
|
||||
>
|
||||
<Context.Provider value={open}>
|
||||
<div className={clsx(classes.drawer, { [classes.drawerOpen]: open })}>
|
||||
<SidebarLogo />
|
||||
<Spacer />
|
||||
<Divider />
|
||||
<SidebarItem icon={HomeIcon} to="/" text="Home" />
|
||||
<Divider />
|
||||
<SidebarItem icon={ServiceIcon} to="/services" text="Services" />
|
||||
<SidebarItem icon={WebIcon} to="/websites" text="Websites" />
|
||||
<SidebarItem icon={LibIcon} to="/libraries" text="Libraries" />
|
||||
<Divider />
|
||||
<Space />
|
||||
<SidebarItem icon={CreateIcon} text="Create..." />
|
||||
</div>
|
||||
</Context.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,9 +26,9 @@ class Header extends Component {
|
||||
|
||||
return typeLink ? (
|
||||
// <Link to={typeLink}>
|
||||
<Typography className={classes.type}>{type}</Typography>
|
||||
// </Link>
|
||||
<Typography className={classes.type}>{type}</Typography>
|
||||
) : (
|
||||
// </Link>
|
||||
<Typography className={classes.type}>{type}</Typography>
|
||||
);
|
||||
}
|
||||
@@ -69,12 +69,17 @@ class Header extends Component {
|
||||
const { title, pageTitleOverride, children, style, classes } = this.props;
|
||||
const pageTitle = pageTitleOverride || title;
|
||||
if (typeof pageTitle !== 'string') {
|
||||
console.warn('<Header/> title prop is not a string, pageTitleOverride should be provided.');
|
||||
console.warn(
|
||||
'<Header/> title prop is not a string, pageTitleOverride should be provided.',
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Helmet titleTemplate={`${pageTitle} | %s | Backstage`} defaultTitle={`${pageTitle} | Backstage`} />
|
||||
<Helmet
|
||||
titleTemplate={`${pageTitle} | %s | Backstage`}
|
||||
defaultTitle={`${pageTitle} | Backstage`}
|
||||
/>
|
||||
<Theme.Consumer>
|
||||
{theme => (
|
||||
<header style={style} className={classes.header}>
|
||||
@@ -98,7 +103,7 @@ const styles = theme => ({
|
||||
gridArea: 'pageHeader',
|
||||
padding: theme.spacing(3),
|
||||
minHeight: 118,
|
||||
width: 'calc(100vw - 224px)',
|
||||
width: '100vw',
|
||||
boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)',
|
||||
position: 'relative',
|
||||
zIndex: 100,
|
||||
@@ -117,6 +122,7 @@ const styles = theme => ({
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
marginRight: theme.spacing(6),
|
||||
},
|
||||
title: {
|
||||
color: theme.palette.bursts.fontColor,
|
||||
|
||||
Reference in New Issue
Block a user