From fa8a26e80d83d5c794a9d572cf716f0cb484e642 Mon Sep 17 00:00:00 2001 From: rolandlor <33499567+rolandlor@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:15:26 +0100 Subject: [PATCH] feat: spdv-995 (#225) --- src/components/SideBar.tsx | 112 +++++++++++++++++++++++++------ src/components/SideBarItem.tsx | 40 +++++++++-- src/components/SideBarStatus.tsx | 65 +++++++++++++----- 3 files changed, 173 insertions(+), 44 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 5ecfed9..e714139 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -1,6 +1,6 @@ import { BeeModes } from '@ethersphere/bee-js' -import { Box, Divider, Drawer, Grid, Link as MUILink, List, Typography } from '@mui/material' -import { ReactElement, useContext } from 'react' +import { Box, Divider, Drawer, Grid, IconButton, Link as MUILink, List, Tooltip, Typography } from '@mui/material' +import { ReactElement, useContext, useState } from 'react' import { Link } from 'react-router-dom' import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon' import DocsIcon from 'remixicon-react/BookOpenLineIcon' @@ -8,6 +8,8 @@ import ExternalLinkIcon from 'remixicon-react/ExternalLinkLineIcon' import FileManagerIcon from 'remixicon-react/FolderOpenLineIcon' import GithubIcon from 'remixicon-react/GithubFillIcon' 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 AccountIcon from 'remixicon-react/Wallet3LineIcon' import { makeStyles } from 'tss-react/mui' @@ -23,26 +25,75 @@ import SideBarItem from './SideBarItem' import SideBarStatus from './SideBarStatus' const drawerWidth = 300 +const drawerWidthCollapsed = 72 +const drawerHeaderHeight = 56 const useStyles = makeStyles()(theme => ({ root: { flexWrap: 'nowrap', - minHeight: '100vh', - paddingTop: theme.spacing(8), + minHeight: `calc(100vh - ${drawerHeaderHeight}px)`, paddingBottom: theme.spacing(8), }, drawer: { width: drawerWidth, 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: { width: drawerWidth, backgroundColor: '#212121', 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: { - marginLeft: theme.spacing(8), - marginRight: theme.spacing(8), + flex: 1, + display: 'flex', + alignItems: 'center', + overflow: 'hidden', + }, + logoImg: { + maxWidth: '100%', + display: 'block', + }, + toggleButton: { + color: '#9f9f9f', + '&:hover': { + color: '#f9f9f9', + backgroundColor: '#2c2c2c', + }, + }, + toggleButtonCollapsed: { + marginLeft: 'auto', }, icon: { height: theme.spacing(4), @@ -70,6 +121,7 @@ export default function SideBar(): ReactElement { const { classes } = useStyles() const { isDesktop } = useContext(SettingsContext) const { nodeInfo } = useContext(BeeContext) + const [isCollapsed, setIsCollapsed] = useState(false) const navBarItems = [ { @@ -103,13 +155,28 @@ export default function SideBar(): ReactElement { ] return ( - - - - - swarm + +
+ {!isCollapsed && ( + + swarm - + )} + + setIsCollapsed(!isCollapsed)} + className={`${classes.toggleButton} ${isCollapsed ? classes.toggleButtonCollapsed : ''}`} + > + {isCollapsed ? : } + + +
+ {navBarItems.map(p => ( @@ -120,6 +187,7 @@ export default function SideBar(): ReactElement { path={p.path} pathMatcherSubstring={p.pathMatcherSubstring} label={p.label} + isCollapsed={isCollapsed} /> ))} @@ -129,8 +197,9 @@ export default function SideBar(): ReactElement { } - iconEnd={} + iconEnd={!isCollapsed ? : undefined} label={Docs} + isCollapsed={isCollapsed} /> @@ -143,22 +212,25 @@ export default function SideBar(): ReactElement { > } - iconEnd={} + iconEnd={!isCollapsed ? : undefined} label={GitHub} + isCollapsed={isCollapsed} /> - - - Redeem gift code - - + {!isCollapsed && ( + + + Redeem gift code + + + )} - + diff --git a/src/components/SideBarItem.tsx b/src/components/SideBarItem.tsx index cb32217..7eb576f 100644 --- a/src/components/SideBarItem.tsx +++ b/src/components/SideBarItem.tsx @@ -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 { matchPath, useLocation } from 'react-router-dom' 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 => ({ icon: { color: 'inherit', + minWidth: 0, }, activeIcon: { color: theme.palette.primary.main, + minWidth: 0, + }, + label: { + marginLeft: theme.spacing(2), }, })) @@ -41,9 +51,17 @@ interface Props { path?: string label: ReactNode 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: itemClasses } = useItemStyles() const location = useLocation() @@ -52,10 +70,18 @@ export default function SideBarItem({ iconStart, iconEnd, path, label, pathMatch : Boolean(path && matchPath(location.pathname, path)) return ( - - {iconStart} - - {iconEnd} - + + + {iconStart} + {!isCollapsed && } + {!isCollapsed && iconEnd && ( + {iconEnd} + )} + + ) } diff --git a/src/components/SideBarStatus.tsx b/src/components/SideBarStatus.tsx index 1c47a43..dad394f 100644 --- a/src/components/SideBarStatus.tsx +++ b/src/components/SideBarStatus.tsx @@ -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 { matchPath, useLocation } from 'react-router-dom' import ArrowRight from 'remixicon-react/ArrowRightLineIcon' @@ -18,8 +18,8 @@ const useStyles = makeStyles()(theme => ({ root: { height: theme.spacing(4), - paddingLeft: theme.spacing(1), - paddingRight: theme.spacing(4), + paddingLeft: theme.spacing(0), + paddingRight: theme.spacing(0), color: '#f9f9f9', borderLeft: '0px solid rgba(0,0,0,0)', '&.Mui-selected, &.Mui-selected:hover': { @@ -46,31 +46,62 @@ const useStyles = makeStyles()(theme => ({ fontSize: '0.9rem', 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 { 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 { classes } = useStyles() const location = useLocation() const isSelected = Boolean(path && matchPath(location.pathname, path)) return ( - - - - - {`Node ${status.all}`}} /> - - {status.all ? null : } - - + + + + + + {!isCollapsed && ( + <> + {`Node ${status.all}`} + } + /> + + {status.all ? null : } + + + )} + + ) }