feat: spdv-995 (#225)
This commit is contained in:
+92
-20
@@ -1,6 +1,6 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { Box, Divider, Drawer, Grid, Link as MUILink, List, Typography } from '@mui/material'
|
import { Box, Divider, Drawer, Grid, IconButton, Link as MUILink, List, Tooltip, Typography } from '@mui/material'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon'
|
import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon'
|
||||||
import DocsIcon from 'remixicon-react/BookOpenLineIcon'
|
import DocsIcon from 'remixicon-react/BookOpenLineIcon'
|
||||||
@@ -8,6 +8,8 @@ import ExternalLinkIcon from 'remixicon-react/ExternalLinkLineIcon'
|
|||||||
import FileManagerIcon from 'remixicon-react/FolderOpenLineIcon'
|
import FileManagerIcon from 'remixicon-react/FolderOpenLineIcon'
|
||||||
import GithubIcon from 'remixicon-react/GithubFillIcon'
|
import GithubIcon from 'remixicon-react/GithubFillIcon'
|
||||||
import HomeIcon from 'remixicon-react/Home3LineIcon'
|
import HomeIcon from 'remixicon-react/Home3LineIcon'
|
||||||
|
import MenuFoldIcon from 'remixicon-react/MenuFoldLineIcon'
|
||||||
|
import MenuUnfoldIcon from 'remixicon-react/MenuUnfoldLineIcon'
|
||||||
import SettingsIcon from 'remixicon-react/Settings2LineIcon'
|
import SettingsIcon from 'remixicon-react/Settings2LineIcon'
|
||||||
import AccountIcon from 'remixicon-react/Wallet3LineIcon'
|
import AccountIcon from 'remixicon-react/Wallet3LineIcon'
|
||||||
import { makeStyles } from 'tss-react/mui'
|
import { makeStyles } from 'tss-react/mui'
|
||||||
@@ -23,26 +25,75 @@ import SideBarItem from './SideBarItem'
|
|||||||
import SideBarStatus from './SideBarStatus'
|
import SideBarStatus from './SideBarStatus'
|
||||||
|
|
||||||
const drawerWidth = 300
|
const drawerWidth = 300
|
||||||
|
const drawerWidthCollapsed = 72
|
||||||
|
const drawerHeaderHeight = 56
|
||||||
|
|
||||||
const useStyles = makeStyles()(theme => ({
|
const useStyles = makeStyles()(theme => ({
|
||||||
root: {
|
root: {
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
minHeight: '100vh',
|
minHeight: `calc(100vh - ${drawerHeaderHeight}px)`,
|
||||||
paddingTop: theme.spacing(8),
|
|
||||||
paddingBottom: theme.spacing(8),
|
paddingBottom: theme.spacing(8),
|
||||||
},
|
},
|
||||||
drawer: {
|
drawer: {
|
||||||
width: drawerWidth,
|
width: drawerWidth,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
drawerCollapsed: {
|
||||||
|
width: drawerWidthCollapsed,
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
width: drawerWidth,
|
width: drawerWidth,
|
||||||
backgroundColor: '#212121',
|
backgroundColor: '#212121',
|
||||||
zIndex: 988,
|
zIndex: 988,
|
||||||
|
overflowX: 'hidden',
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
drawerPaperCollapsed: {
|
||||||
|
width: drawerWidthCollapsed,
|
||||||
|
transition: theme.transitions.create('width', {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: drawerHeaderHeight,
|
||||||
|
borderBottom: '1px solid #2c2c2c',
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(2),
|
||||||
|
flexShrink: 0,
|
||||||
},
|
},
|
||||||
logo: {
|
logo: {
|
||||||
marginLeft: theme.spacing(8),
|
flex: 1,
|
||||||
marginRight: theme.spacing(8),
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
logoImg: {
|
||||||
|
maxWidth: '100%',
|
||||||
|
display: 'block',
|
||||||
|
},
|
||||||
|
toggleButton: {
|
||||||
|
color: '#9f9f9f',
|
||||||
|
'&:hover': {
|
||||||
|
color: '#f9f9f9',
|
||||||
|
backgroundColor: '#2c2c2c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toggleButtonCollapsed: {
|
||||||
|
marginLeft: 'auto',
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
height: theme.spacing(4),
|
height: theme.spacing(4),
|
||||||
@@ -70,6 +121,7 @@ export default function SideBar(): ReactElement {
|
|||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
const { isDesktop } = useContext(SettingsContext)
|
const { isDesktop } = useContext(SettingsContext)
|
||||||
const { nodeInfo } = useContext(BeeContext)
|
const { nodeInfo } = useContext(BeeContext)
|
||||||
|
const [isCollapsed, setIsCollapsed] = useState(false)
|
||||||
|
|
||||||
const navBarItems = [
|
const navBarItems = [
|
||||||
{
|
{
|
||||||
@@ -103,13 +155,28 @@ export default function SideBar(): ReactElement {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer className={classes.drawer} variant="permanent" anchor="left" classes={{ paper: classes.drawerPaper }}>
|
<Drawer
|
||||||
<Grid container direction="column" justifyContent="space-between" className={classes.root}>
|
className={`${classes.drawer} ${isCollapsed ? classes.drawerCollapsed : ''}`}
|
||||||
<Grid className={classes.logo}>
|
variant="permanent"
|
||||||
<Link to={ROUTES.INFO}>
|
anchor="left"
|
||||||
<img alt="swarm" src={isDesktop ? DesktopLogo : DashboardLogo} />
|
classes={{ paper: `${classes.drawerPaper} ${isCollapsed ? classes.drawerPaperCollapsed : ''}` }}
|
||||||
|
>
|
||||||
|
<div className={classes.header}>
|
||||||
|
{!isCollapsed && (
|
||||||
|
<Link to={ROUTES.INFO} className={classes.logo}>
|
||||||
|
<img alt="swarm" className={classes.logoImg} src={isDesktop ? DesktopLogo : DashboardLogo} />
|
||||||
</Link>
|
</Link>
|
||||||
</Grid>
|
)}
|
||||||
|
<Tooltip title={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'} placement="right">
|
||||||
|
<IconButton
|
||||||
|
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||||
|
className={`${classes.toggleButton} ${isCollapsed ? classes.toggleButtonCollapsed : ''}`}
|
||||||
|
>
|
||||||
|
{isCollapsed ? <MenuUnfoldIcon /> : <MenuFoldIcon />}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Grid container direction="column" justifyContent="space-between" className={classes.root}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<List>
|
<List>
|
||||||
{navBarItems.map(p => (
|
{navBarItems.map(p => (
|
||||||
@@ -120,6 +187,7 @@ export default function SideBar(): ReactElement {
|
|||||||
path={p.path}
|
path={p.path}
|
||||||
pathMatcherSubstring={p.pathMatcherSubstring}
|
pathMatcherSubstring={p.pathMatcherSubstring}
|
||||||
label={p.label}
|
label={p.label}
|
||||||
|
isCollapsed={isCollapsed}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
@@ -129,8 +197,9 @@ export default function SideBar(): ReactElement {
|
|||||||
<MUILink href={BEE_DOCS_HOST} target="_blank" className={classes.link}>
|
<MUILink href={BEE_DOCS_HOST} target="_blank" className={classes.link}>
|
||||||
<SideBarItem
|
<SideBarItem
|
||||||
iconStart={<DocsIcon className={classes.icon} />}
|
iconStart={<DocsIcon className={classes.icon} />}
|
||||||
iconEnd={<ExternalLinkIcon className={classes.icon} color="#595959" />}
|
iconEnd={!isCollapsed ? <ExternalLinkIcon className={classes.icon} color="#595959" /> : undefined}
|
||||||
label={<span>Docs</span>}
|
label={<span>Docs</span>}
|
||||||
|
isCollapsed={isCollapsed}
|
||||||
/>
|
/>
|
||||||
</MUILink>
|
</MUILink>
|
||||||
</List>
|
</List>
|
||||||
@@ -143,22 +212,25 @@ export default function SideBar(): ReactElement {
|
|||||||
>
|
>
|
||||||
<SideBarItem
|
<SideBarItem
|
||||||
iconStart={<GithubIcon className={classes.icon} />}
|
iconStart={<GithubIcon className={classes.icon} />}
|
||||||
iconEnd={<ExternalLinkIcon className={classes.icon} color="#595959" />}
|
iconEnd={!isCollapsed ? <ExternalLinkIcon className={classes.icon} color="#595959" /> : undefined}
|
||||||
label={<span>GitHub</span>}
|
label={<span>GitHub</span>}
|
||||||
|
isCollapsed={isCollapsed}
|
||||||
/>
|
/>
|
||||||
</MUILink>
|
</MUILink>
|
||||||
</List>
|
</List>
|
||||||
<Divider className={classes.divider} />
|
<Divider className={classes.divider} />
|
||||||
<Box mt={4}>
|
{!isCollapsed && (
|
||||||
<Link to={ROUTES.TOP_UP_GIFT_CODE}>
|
<Box mt={4}>
|
||||||
<Typography align="center">Redeem gift code</Typography>
|
<Link to={ROUTES.TOP_UP_GIFT_CODE}>
|
||||||
</Link>
|
<Typography align="center">Redeem gift code</Typography>
|
||||||
</Box>
|
</Link>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<List>
|
<List>
|
||||||
<Link to={ROUTES.STATUS} className={classes.link}>
|
<Link to={ROUTES.STATUS} className={classes.link}>
|
||||||
<SideBarStatus path={ROUTES.STATUS} />
|
<SideBarStatus path={ROUTES.STATUS} isCollapsed={isCollapsed} />
|
||||||
</Link>
|
</Link>
|
||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material'
|
import { ListItemButton, ListItemIcon, ListItemText, Tooltip } from '@mui/material'
|
||||||
import type { ReactElement, ReactNode } from 'react'
|
import type { ReactElement, ReactNode } from 'react'
|
||||||
import { matchPath, useLocation } from 'react-router-dom'
|
import { matchPath, useLocation } from 'react-router-dom'
|
||||||
import { makeStyles } from 'tss-react/mui'
|
import { makeStyles } from 'tss-react/mui'
|
||||||
@@ -24,14 +24,24 @@ const useItemStyles = makeStyles()(theme => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
rootCollapsed: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingLeft: theme.spacing(1),
|
||||||
|
paddingRight: theme.spacing(1),
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const useStyles = makeStyles()(theme => ({
|
const useStyles = makeStyles()(theme => ({
|
||||||
icon: {
|
icon: {
|
||||||
color: 'inherit',
|
color: 'inherit',
|
||||||
|
minWidth: 0,
|
||||||
},
|
},
|
||||||
activeIcon: {
|
activeIcon: {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
|
minWidth: 0,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -41,9 +51,17 @@ interface Props {
|
|||||||
path?: string
|
path?: string
|
||||||
label: ReactNode
|
label: ReactNode
|
||||||
pathMatcherSubstring?: string
|
pathMatcherSubstring?: string
|
||||||
|
isCollapsed?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SideBarItem({ iconStart, iconEnd, path, label, pathMatcherSubstring }: Props): ReactElement {
|
export default function SideBarItem({
|
||||||
|
iconStart,
|
||||||
|
iconEnd,
|
||||||
|
path,
|
||||||
|
label,
|
||||||
|
pathMatcherSubstring,
|
||||||
|
isCollapsed,
|
||||||
|
}: Props): ReactElement {
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
const { classes: itemClasses } = useItemStyles()
|
const { classes: itemClasses } = useItemStyles()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
@@ -52,10 +70,18 @@ export default function SideBarItem({ iconStart, iconEnd, path, label, pathMatch
|
|||||||
: Boolean(path && matchPath(location.pathname, path))
|
: Boolean(path && matchPath(location.pathname, path))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItemButton className={itemClasses.root} selected={isSelected} disableRipple>
|
<Tooltip title={isCollapsed ? label : ''} placement="right">
|
||||||
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconStart}</ListItemIcon>
|
<ListItemButton
|
||||||
<ListItemText primary={label} />
|
className={`${itemClasses.root} ${isCollapsed ? itemClasses.rootCollapsed : ''}`}
|
||||||
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconEnd}</ListItemIcon>
|
selected={isSelected}
|
||||||
</ListItemButton>
|
disableRipple
|
||||||
|
>
|
||||||
|
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconStart}</ListItemIcon>
|
||||||
|
{!isCollapsed && <ListItemText primary={label} className={classes.label} />}
|
||||||
|
{!isCollapsed && iconEnd && (
|
||||||
|
<ListItemIcon className={isSelected ? classes.activeIcon : classes.icon}>{iconEnd}</ListItemIcon>
|
||||||
|
)}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material'
|
import { ListItemButton, ListItemIcon, ListItemText, Tooltip, Typography } from '@mui/material'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { matchPath, useLocation } from 'react-router-dom'
|
import { matchPath, useLocation } from 'react-router-dom'
|
||||||
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
||||||
@@ -18,8 +18,8 @@ const useStyles = makeStyles()(theme => ({
|
|||||||
|
|
||||||
root: {
|
root: {
|
||||||
height: theme.spacing(4),
|
height: theme.spacing(4),
|
||||||
paddingLeft: theme.spacing(1),
|
paddingLeft: theme.spacing(0),
|
||||||
paddingRight: theme.spacing(4),
|
paddingRight: theme.spacing(0),
|
||||||
color: '#f9f9f9',
|
color: '#f9f9f9',
|
||||||
borderLeft: '0px solid rgba(0,0,0,0)',
|
borderLeft: '0px solid rgba(0,0,0,0)',
|
||||||
'&.Mui-selected, &.Mui-selected:hover': {
|
'&.Mui-selected, &.Mui-selected:hover': {
|
||||||
@@ -46,31 +46,62 @@ const useStyles = makeStyles()(theme => ({
|
|||||||
fontSize: '0.9rem',
|
fontSize: '0.9rem',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
},
|
},
|
||||||
|
rootCollapsed: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingLeft: theme.spacing(1),
|
||||||
|
paddingRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
statusIcon: {
|
||||||
|
marginLeft: '30px',
|
||||||
|
minWidth: 0,
|
||||||
|
},
|
||||||
|
statusIconCollapsed: {
|
||||||
|
marginLeft: 0,
|
||||||
|
minWidth: 0,
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
path?: string
|
path?: string
|
||||||
|
isCollapsed?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SideBarItem({ path }: Props): ReactElement {
|
export default function SideBarItem({ path, isCollapsed }: Props): ReactElement {
|
||||||
const { status, isLoading } = useContext(Context)
|
const { status, isLoading } = useContext(Context)
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const isSelected = Boolean(path && matchPath(location.pathname, path))
|
const isSelected = Boolean(path && matchPath(location.pathname, path))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItemButton
|
<Tooltip title={isCollapsed ? `Node ${status.all}` : ''} placement="right">
|
||||||
classes={{ root: `${classes.root} ${status.all ? '' : classes.rootError}` }}
|
<ListItemButton
|
||||||
selected={isSelected}
|
classes={{
|
||||||
disableRipple
|
root: `${classes.root} ${status.all ? '' : classes.rootError} ${isCollapsed ? classes.rootCollapsed : ''}`,
|
||||||
>
|
}}
|
||||||
<ListItemIcon style={{ marginLeft: '30px' }}>
|
selected={isSelected}
|
||||||
<StatusIcon checkState={status.all} isLoading={isLoading} />
|
disableRipple
|
||||||
</ListItemIcon>
|
>
|
||||||
<ListItemText primary={<Typography className={classes.smallerText}>{`Node ${status.all}`}</Typography>} />
|
<ListItemIcon className={isCollapsed ? classes.statusIconCollapsed : classes.statusIcon}>
|
||||||
<ListItemIcon className={classes.icon}>
|
<StatusIcon checkState={status.all} isLoading={isLoading} />
|
||||||
{status.all ? null : <ArrowRight className={classes.iconSmall} />}
|
</ListItemIcon>
|
||||||
</ListItemIcon>
|
{!isCollapsed && (
|
||||||
</ListItemButton>
|
<>
|
||||||
|
<ListItemText
|
||||||
|
primary={
|
||||||
|
<Typography
|
||||||
|
className={`${classes.smallerText} ${classes.statusText}`}
|
||||||
|
>{`Node ${status.all}`}</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ListItemIcon className={classes.icon}>
|
||||||
|
{status.all ? null : <ArrowRight className={classes.iconSmall} />}
|
||||||
|
</ListItemIcon>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user