Skip to content

Commit 9b12f5a

Browse files
authored
UX left sidebar menu (#1356)
1 parent dd781dc commit 9b12f5a

File tree

6 files changed

+385
-283
lines changed

6 files changed

+385
-283
lines changed

portal-ui/src/screens/Console/Menu/Menu.tsx

Lines changed: 33 additions & 259 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,24 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17-
import React, { Suspense } from "react";
17+
import React from "react";
1818
import { connect } from "react-redux";
1919
import { NavLink } from "react-router-dom";
20-
import { Divider, Drawer, IconButton, Tooltip } from "@mui/material";
21-
import { ChevronLeft } from "@mui/icons-material";
20+
import { Drawer } from "@mui/material";
2221
import withStyles from "@mui/styles/withStyles";
2322
import { Theme } from "@mui/material/styles";
2423
import createStyles from "@mui/styles/createStyles";
25-
import ListItem from "@mui/material/ListItem";
26-
import ListItemIcon from "@mui/material/ListItemIcon";
27-
import ListItemText from "@mui/material/ListItemText";
28-
import List from "@mui/material/List";
2924
import clsx from "clsx";
3025
import { AppState } from "../../../store";
3126
import { setMenuOpen, userLoggedIn } from "../../../actions";
32-
import { menuGroups } from "./utils";
3327
import { IMenuItem } from "./types";
3428

3529
import { ErrorResponseHandler } from "../../../common/types";
3630
import { clearSession } from "../../../common/utils";
3731

38-
import OperatorLogo from "../../../icons/OperatorLogo";
39-
import ConsoleLogo from "../../../icons/ConsoleLogo";
4032
import history from "../../../history";
4133
import api from "../../../common/api";
4234

43-
import MenuIcon from "@mui/icons-material/Menu";
44-
import LogoutIcon from "../../../icons/LogoutIcon";
4535
import { resetSession } from "../actions";
4636
import {
4737
AccountIcon,
@@ -58,7 +48,6 @@ import {
5848
TiersIcon,
5949
ToolsIcon,
6050
UsersIcon,
61-
VersionIcon,
6251
} from "../../../icons";
6352
import {
6453
CONSOLE_UI_RESOURCE,
@@ -68,98 +57,13 @@ import {
6857
IAM_SCOPES,
6958
} from "../../../common/SecureComponent/permissions";
7059
import { hasPermission } from "../../../common/SecureComponent/SecureComponent";
60+
import MenuList from "./MenuList";
61+
import MenuToggle from "./MenuToggle";
7162

7263
const drawerWidth = 245;
7364

7465
const styles = (theme: Theme) =>
7566
createStyles({
76-
logo: {
77-
paddingTop: 25,
78-
height: 80,
79-
marginBottom: 30,
80-
paddingLeft: 45,
81-
borderBottom: "#1C3B64 1px solid",
82-
transition: theme.transitions.create("paddingLeft", {
83-
easing: theme.transitions.easing.sharp,
84-
duration: theme.transitions.duration.leavingScreen,
85-
}),
86-
"& img": {
87-
width: 120,
88-
},
89-
"& .MuiIconButton-root": {
90-
color: "#ffffff",
91-
float: "right",
92-
},
93-
},
94-
logoClosed: {
95-
paddingTop: 25,
96-
marginBottom: 0,
97-
paddingLeft: 34,
98-
transition: theme.transitions.create("paddingLeft", {
99-
easing: theme.transitions.easing.sharp,
100-
duration: theme.transitions.duration.leavingScreen,
101-
}),
102-
"& .MuiIconButton-root": {
103-
color: "#ffffff",
104-
},
105-
},
106-
menuList: {
107-
"& .active": {
108-
color: "#fff",
109-
backgroundBlendMode: "multiply",
110-
background:
111-
"transparent linear-gradient(90deg, rgba(0, 0, 0, 0.14) 0%, #00000000 100%) 0% 0% no-repeat padding-box",
112-
"& .min-icon": {
113-
color: "white",
114-
},
115-
"& .MuiTypography-root": {
116-
color: "#fff",
117-
fontWeight: "bold",
118-
},
119-
},
120-
"& .min-icon": {
121-
width: 16,
122-
height: 16,
123-
fill: "rgba(255, 255, 255, 0.8)",
124-
},
125-
"& .MuiListItemIcon-root": {
126-
minWidth: 36,
127-
},
128-
"& .MuiTypography-root": {
129-
fontSize: 15,
130-
color: "rgba(255, 255, 255, 0.8)",
131-
},
132-
"& .MuiListItem-gutters": {
133-
paddingRight: 0,
134-
fontWeight: 300,
135-
},
136-
"& .MuiListItem-root": {
137-
padding: "2px 0 2px 16px",
138-
marginLeft: 36,
139-
height: 50,
140-
width: "calc(100% - 30px)",
141-
},
142-
},
143-
menuDivider: {
144-
backgroundColor: "#1C3B64",
145-
marginRight: 36,
146-
marginLeft: 36,
147-
marginBottom: 0,
148-
height: 1,
149-
},
150-
extraMargin: {
151-
"&.MuiListItem-gutters": {
152-
marginLeft: 5,
153-
},
154-
},
155-
subTitleMenu: {
156-
fontWeight: 700,
157-
marginLeft: 10,
158-
"&.MuiTypography-root": {
159-
fontSize: 13,
160-
color: "#fff",
161-
},
162-
},
16367
drawer: {
16468
width: drawerWidth,
16569
flexShrink: 0,
@@ -184,63 +88,27 @@ const styles = (theme: Theme) =>
18488
duration: theme.transitions.duration.leavingScreen,
18589
}),
18690
overflowX: "hidden",
187-
width: 115,
188-
[theme.breakpoints.up("sm")]: {
189-
width: 115,
190-
},
191-
"& .logo": {
192-
background: "red",
193-
},
194-
"& .MuiListItem-root": {
195-
padding: "2px 0 2px 16px",
196-
marginLeft: 36,
197-
height: 50,
198-
width: "48px",
199-
transition: theme.transitions.create("marginLeft", {
200-
easing: theme.transitions.easing.sharp,
201-
duration: theme.transitions.duration.leavingScreen,
202-
}),
203-
"& .MuiListItemText-root": {
204-
display: "none",
205-
},
206-
},
207-
},
208-
logoIcon: {
209-
float: "left",
210-
"& svg": {
211-
fill: "white",
212-
width: 120,
213-
},
214-
},
215-
logoIconClosed: {
216-
color: "white",
217-
"& .min-icon": {
218-
marginLeft: 11,
219-
width: 24,
220-
fill: "rgba(255, 255, 255, 0.8)",
91+
[theme.breakpoints.up("xs")]: {
92+
width: 75,
22193
},
22294
},
22395
});
22496

22597
interface IMenuProps {
226-
classes: any;
98+
classes?: any;
22799
userLoggedIn: typeof userLoggedIn;
228-
operatorMode: boolean;
229-
distributedSetup: boolean;
100+
operatorMode?: boolean;
230101
sidebarOpen: boolean;
231102
setMenuOpen: typeof setMenuOpen;
232-
resetSession: typeof resetSession;
233-
features: string[] | null;
103+
features?: string[] | null;
234104
}
235105

236106
const Menu = ({
237107
userLoggedIn,
238108
classes,
239-
operatorMode,
240-
distributedSetup,
109+
operatorMode = false,
241110
sidebarOpen,
242111
setMenuOpen,
243-
resetSession,
244112
features,
245113
}: IMenuProps) => {
246114
const logout = () => {
@@ -441,138 +309,44 @@ const Menu = ({
441309
);
442310

443311
return (
444-
<React.Fragment>
445-
<Drawer
446-
variant="permanent"
447-
className={clsx(classes.drawer, {
312+
<Drawer
313+
variant="permanent"
314+
className={clsx(classes.drawer, {
315+
[classes.drawerOpen]: sidebarOpen,
316+
[classes.drawerClose]: !sidebarOpen,
317+
})}
318+
classes={{
319+
paper: clsx({
448320
[classes.drawerOpen]: sidebarOpen,
449321
[classes.drawerClose]: !sidebarOpen,
450-
})}
451-
classes={{
452-
paper: clsx({
453-
[classes.drawerOpen]: sidebarOpen,
454-
[classes.drawerClose]: !sidebarOpen,
455-
}),
322+
}),
323+
}}
324+
>
325+
<MenuToggle
326+
onToggle={(nextState) => {
327+
setMenuOpen(nextState);
456328
}}
457-
>
458-
<div
459-
className={clsx({
460-
[classes.logo]: sidebarOpen,
461-
[classes.logoClosed]: !sidebarOpen,
462-
})}
463-
>
464-
{sidebarOpen && (
465-
<span className={classes.logoIcon}>
466-
{operatorMode ? <OperatorLogo /> : <ConsoleLogo />}
467-
</span>
468-
)}
469-
{!sidebarOpen && (
470-
<div className={classes.logoIconClosed}>
471-
<Suspense fallback={<div>Loading...</div>}>
472-
<VersionIcon />
473-
</Suspense>
474-
</div>
475-
)}
476-
<IconButton
477-
onClick={() => {
478-
if (sidebarOpen) {
479-
setMenuOpen(false);
480-
} else {
481-
setMenuOpen(true);
482-
}
483-
}}
484-
size="large"
485-
>
486-
{sidebarOpen ? <ChevronLeft /> : <MenuIcon />}
487-
</IconButton>
488-
</div>
489-
<List className={classes.menuList}>
490-
{menuGroups.map((groupMember, index) => {
491-
const filterByGroup = (allowedItems || []).filter(
492-
(item: any) => item.group === groupMember.group
493-
);
494-
495-
const countableElements = filterByGroup.filter(
496-
(menuItem: any) => menuItem.type !== "title"
497-
);
498-
499-
if (countableElements.length === 0) {
500-
return null;
501-
}
329+
isOperatorMode={operatorMode}
330+
isOpen={sidebarOpen}
331+
/>
502332

503-
return (
504-
<React.Fragment key={`menuElem-${index.toString()}`}>
505-
{index !== 0 && <Divider className={classes.menuDivider} />}
506-
{filterByGroup.map((page: IMenuItem) => {
507-
switch (page.type) {
508-
case "item": {
509-
return (
510-
<ListItem
511-
key={page.to}
512-
button
513-
onClick={page.onClick}
514-
component={page.component}
515-
to={page.to}
516-
className={
517-
page.extraMargin ? classes.extraMargin : null
518-
}
519-
>
520-
{page.icon && (
521-
<Tooltip title={page.name}>
522-
<div>
523-
<ListItemIcon>
524-
<Suspense fallback={<div>Loading...</div>}>
525-
<page.icon />
526-
</Suspense>
527-
</ListItemIcon>
528-
</div>
529-
</Tooltip>
530-
)}
531-
{page.name && <ListItemText primary={page.name} />}
532-
</ListItem>
533-
);
534-
}
535-
case "title": {
536-
return (
537-
<ListItem
538-
key={page.name}
539-
component={page.component}
540-
className={classes.subTitleMenu}
541-
>
542-
{page.name}
543-
</ListItem>
544-
);
545-
}
546-
default:
547-
return null;
548-
}
549-
})}
550-
</React.Fragment>
551-
);
552-
})}
553-
<Divider className={classes.menuDivider} />
554-
<ListItem button onClick={logout}>
555-
<ListItemIcon>
556-
<LogoutIcon />
557-
</ListItemIcon>
558-
<ListItemText primary="Logout" />
559-
</ListItem>
560-
</List>
561-
</Drawer>
562-
</React.Fragment>
333+
<MenuList
334+
menuItems={allowedItems}
335+
isOpen={sidebarOpen}
336+
onLogoutClick={logout}
337+
/>
338+
</Drawer>
563339
);
564340
};
565341
const mapState = (state: AppState) => ({
566342
sidebarOpen: state.system.sidebarOpen,
567343
operatorMode: state.system.operatorMode,
568-
distributedSetup: state.system.distributedSetup,
569344
features: state.console.session.features,
570345
});
571346

572347
const connector = connect(mapState, {
573348
userLoggedIn,
574349
setMenuOpen,
575-
resetSession,
576350
});
577351

578352
export default connector(withStyles(styles)(Menu));

0 commit comments

Comments
 (0)