From 9b12f5a41eb3afc2c046aa70d3f3e23819805bd9 Mon Sep 17 00:00:00 2001 From: Prakash Senthil Vel <23444145+prakashsvmx@users.noreply.github.com> Date: Tue, 4 Jan 2022 02:26:32 +0000 Subject: [PATCH] UX left sidebar menu (#1356) --- portal-ui/src/screens/Console/Menu/Menu.tsx | 292 ++---------------- .../src/screens/Console/Menu/MenuItem.tsx | 62 ++++ .../src/screens/Console/Menu/MenuList.tsx | 114 +++++++ .../screens/Console/Menu/MenuStyleUtils.tsx | 61 ++++ .../src/screens/Console/Menu/MenuToggle.tsx | 115 +++++++ portal-ui/src/screens/Console/Menu/utils.ts | 24 -- 6 files changed, 385 insertions(+), 283 deletions(-) create mode 100644 portal-ui/src/screens/Console/Menu/MenuItem.tsx create mode 100644 portal-ui/src/screens/Console/Menu/MenuList.tsx create mode 100644 portal-ui/src/screens/Console/Menu/MenuStyleUtils.tsx create mode 100644 portal-ui/src/screens/Console/Menu/MenuToggle.tsx delete mode 100644 portal-ui/src/screens/Console/Menu/utils.ts diff --git a/portal-ui/src/screens/Console/Menu/Menu.tsx b/portal-ui/src/screens/Console/Menu/Menu.tsx index d7bed5bdd..46c5e217f 100644 --- a/portal-ui/src/screens/Console/Menu/Menu.tsx +++ b/portal-ui/src/screens/Console/Menu/Menu.tsx @@ -14,34 +14,24 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { Suspense } from "react"; +import React from "react"; import { connect } from "react-redux"; import { NavLink } from "react-router-dom"; -import { Divider, Drawer, IconButton, Tooltip } from "@mui/material"; -import { ChevronLeft } from "@mui/icons-material"; +import { Drawer } from "@mui/material"; import withStyles from "@mui/styles/withStyles"; import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; -import ListItem from "@mui/material/ListItem"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import ListItemText from "@mui/material/ListItemText"; -import List from "@mui/material/List"; import clsx from "clsx"; import { AppState } from "../../../store"; import { setMenuOpen, userLoggedIn } from "../../../actions"; -import { menuGroups } from "./utils"; import { IMenuItem } from "./types"; import { ErrorResponseHandler } from "../../../common/types"; import { clearSession } from "../../../common/utils"; -import OperatorLogo from "../../../icons/OperatorLogo"; -import ConsoleLogo from "../../../icons/ConsoleLogo"; import history from "../../../history"; import api from "../../../common/api"; -import MenuIcon from "@mui/icons-material/Menu"; -import LogoutIcon from "../../../icons/LogoutIcon"; import { resetSession } from "../actions"; import { AccountIcon, @@ -58,7 +48,6 @@ import { TiersIcon, ToolsIcon, UsersIcon, - VersionIcon, } from "../../../icons"; import { CONSOLE_UI_RESOURCE, @@ -68,98 +57,13 @@ import { IAM_SCOPES, } from "../../../common/SecureComponent/permissions"; import { hasPermission } from "../../../common/SecureComponent/SecureComponent"; +import MenuList from "./MenuList"; +import MenuToggle from "./MenuToggle"; const drawerWidth = 245; const styles = (theme: Theme) => createStyles({ - logo: { - paddingTop: 25, - height: 80, - marginBottom: 30, - paddingLeft: 45, - borderBottom: "#1C3B64 1px solid", - transition: theme.transitions.create("paddingLeft", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - "& img": { - width: 120, - }, - "& .MuiIconButton-root": { - color: "#ffffff", - float: "right", - }, - }, - logoClosed: { - paddingTop: 25, - marginBottom: 0, - paddingLeft: 34, - transition: theme.transitions.create("paddingLeft", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - "& .MuiIconButton-root": { - color: "#ffffff", - }, - }, - menuList: { - "& .active": { - color: "#fff", - backgroundBlendMode: "multiply", - background: - "transparent linear-gradient(90deg, rgba(0, 0, 0, 0.14) 0%, #00000000 100%) 0% 0% no-repeat padding-box", - "& .min-icon": { - color: "white", - }, - "& .MuiTypography-root": { - color: "#fff", - fontWeight: "bold", - }, - }, - "& .min-icon": { - width: 16, - height: 16, - fill: "rgba(255, 255, 255, 0.8)", - }, - "& .MuiListItemIcon-root": { - minWidth: 36, - }, - "& .MuiTypography-root": { - fontSize: 15, - color: "rgba(255, 255, 255, 0.8)", - }, - "& .MuiListItem-gutters": { - paddingRight: 0, - fontWeight: 300, - }, - "& .MuiListItem-root": { - padding: "2px 0 2px 16px", - marginLeft: 36, - height: 50, - width: "calc(100% - 30px)", - }, - }, - menuDivider: { - backgroundColor: "#1C3B64", - marginRight: 36, - marginLeft: 36, - marginBottom: 0, - height: 1, - }, - extraMargin: { - "&.MuiListItem-gutters": { - marginLeft: 5, - }, - }, - subTitleMenu: { - fontWeight: 700, - marginLeft: 10, - "&.MuiTypography-root": { - fontSize: 13, - color: "#fff", - }, - }, drawer: { width: drawerWidth, flexShrink: 0, @@ -184,63 +88,27 @@ const styles = (theme: Theme) => duration: theme.transitions.duration.leavingScreen, }), overflowX: "hidden", - width: 115, - [theme.breakpoints.up("sm")]: { - width: 115, - }, - "& .logo": { - background: "red", - }, - "& .MuiListItem-root": { - padding: "2px 0 2px 16px", - marginLeft: 36, - height: 50, - width: "48px", - transition: theme.transitions.create("marginLeft", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - "& .MuiListItemText-root": { - display: "none", - }, - }, - }, - logoIcon: { - float: "left", - "& svg": { - fill: "white", - width: 120, - }, - }, - logoIconClosed: { - color: "white", - "& .min-icon": { - marginLeft: 11, - width: 24, - fill: "rgba(255, 255, 255, 0.8)", + [theme.breakpoints.up("xs")]: { + width: 75, }, }, }); interface IMenuProps { - classes: any; + classes?: any; userLoggedIn: typeof userLoggedIn; - operatorMode: boolean; - distributedSetup: boolean; + operatorMode?: boolean; sidebarOpen: boolean; setMenuOpen: typeof setMenuOpen; - resetSession: typeof resetSession; - features: string[] | null; + features?: string[] | null; } const Menu = ({ userLoggedIn, classes, - operatorMode, - distributedSetup, + operatorMode = false, sidebarOpen, setMenuOpen, - resetSession, features, }: IMenuProps) => { const logout = () => { @@ -441,138 +309,44 @@ const Menu = ({ ); return ( - - + { + setMenuOpen(nextState); }} - > -
- {sidebarOpen && ( - - {operatorMode ? : } - - )} - {!sidebarOpen && ( -
- Loading...
}> - - -
- )} - { - if (sidebarOpen) { - setMenuOpen(false); - } else { - setMenuOpen(true); - } - }} - size="large" - > - {sidebarOpen ? : } - - - - {menuGroups.map((groupMember, index) => { - const filterByGroup = (allowedItems || []).filter( - (item: any) => item.group === groupMember.group - ); + isOperatorMode={operatorMode} + isOpen={sidebarOpen} + /> - const countableElements = filterByGroup.filter( - (menuItem: any) => menuItem.type !== "title" - ); - - if (countableElements.length === 0) { - return null; - } - - return ( - - {index !== 0 && } - {filterByGroup.map((page: IMenuItem) => { - switch (page.type) { - case "item": { - return ( - - {page.icon && ( - -
- - Loading...
}> - - - - -
- )} - {page.name && } -
- ); - } - case "title": { - return ( - - {page.name} - - ); - } - default: - return null; - } - })} -
- ); - })} - - - - - - - -
-
-
+ + ); }; const mapState = (state: AppState) => ({ sidebarOpen: state.system.sidebarOpen, operatorMode: state.system.operatorMode, - distributedSetup: state.system.distributedSetup, features: state.console.session.features, }); const connector = connect(mapState, { userLoggedIn, setMenuOpen, - resetSession, }); export default connector(withStyles(styles)(Menu)); diff --git a/portal-ui/src/screens/Console/Menu/MenuItem.tsx b/portal-ui/src/screens/Console/Menu/MenuItem.tsx new file mode 100644 index 000000000..5d4f03097 --- /dev/null +++ b/portal-ui/src/screens/Console/Menu/MenuItem.tsx @@ -0,0 +1,62 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Suspense } from "react"; +import { ListItem, ListItemIcon, ListItemText, Tooltip } from "@mui/material"; +import { + menuItemContainerStyles, + menuItemIconStyles, + menuItemTextStyles, +} from "./MenuStyleUtils"; + +const MenuItem = ({ + page, + stateClsName = "", +}: { + page: any; + stateClsName?: string; +}) => { + return ( + + {page.icon && ( + + + ...}> + + + + + )} + {page.name && ( + + )} + + ); +}; + +export default MenuItem; diff --git a/portal-ui/src/screens/Console/Menu/MenuList.tsx b/portal-ui/src/screens/Console/Menu/MenuList.tsx new file mode 100644 index 000000000..824948cac --- /dev/null +++ b/portal-ui/src/screens/Console/Menu/MenuList.tsx @@ -0,0 +1,114 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React from "react"; +import MenuItem from "./MenuItem"; +import { Box } from "@mui/material"; +import ListItem from "@mui/material/ListItem"; +import ListItemIcon from "@mui/material/ListItemIcon"; +import LogoutIcon from "../../../icons/LogoutIcon"; +import ListItemText from "@mui/material/ListItemText"; +import List from "@mui/material/List"; +import { IMenuItem } from "./types"; +import { + menuItemContainerStyles, + menuItemIconStyles, + menuItemTextStyles, +} from "./MenuStyleUtils"; + +const MenuList = ({ + menuItems, + onLogoutClick, + isOpen, +}: { + menuItems: IMenuItem[]; + isOpen: boolean; + onLogoutClick: () => void; +}) => { + const stateClsName = isOpen ? "wide" : "mini"; + + return ( + + + + {(menuItems || []).map((page: IMenuItem) => { + switch (page.type) { + case "item": { + return ( + + ); + } + default: + return null; + } + })} + + + + + + + + + + + + ); +}; + +export default MenuList; diff --git a/portal-ui/src/screens/Console/Menu/MenuStyleUtils.tsx b/portal-ui/src/screens/Console/Menu/MenuStyleUtils.tsx new file mode 100644 index 000000000..09253d480 --- /dev/null +++ b/portal-ui/src/screens/Console/Menu/MenuStyleUtils.tsx @@ -0,0 +1,61 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export const menuItemContainerStyles: any = { + paddingLeft: 0, + paddingBottom: "18px", + "&.active div:nth-of-type(1)": { + border: "2px solid #ffffff", + }, + "&:hover": { + background: "none", + "& div:nth-of-type(1)": { + background: "#073052", + "& svg": { + fill: "#ffffff", + }, + }, + }, +}; +export const menuItemIconStyles: any = { + width: 37, + minWidth: 37, + height: 37, + background: "#00274D", + border: "2px solid #002148", + display: "flex", + alignItems: "center", + borderRadius: "50%", + justifyContent: "center", + + "& svg": { + width: 16, + height: 16, + fill: "#8399AB", + }, +}; + +export const menuItemTextStyles: any = { + color: "#BCC7D1", + fontSize: "14px", + marginLeft: "11px", + "& span": { + fontSize: "14px", + }, + "&.mini": { + display: "none", + }, +}; diff --git a/portal-ui/src/screens/Console/Menu/MenuToggle.tsx b/portal-ui/src/screens/Console/Menu/MenuToggle.tsx new file mode 100644 index 000000000..6e26f683f --- /dev/null +++ b/portal-ui/src/screens/Console/Menu/MenuToggle.tsx @@ -0,0 +1,115 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Suspense } from "react"; +import OperatorLogo from "../../../icons/OperatorLogo"; +import ConsoleLogo from "../../../icons/ConsoleLogo"; +import { VersionIcon } from "../../../icons"; +import { Box, IconButton } from "@mui/material"; +import { ChevronLeft } from "@mui/icons-material"; +import MenuIcon from "@mui/icons-material/Menu"; + +type MenuToggleProps = { + isOpen: boolean; + isOperatorMode: boolean; + onToggle: (nextState: boolean) => void; +}; +const MenuToggle = ({ isOpen, isOperatorMode, onToggle }: MenuToggleProps) => { + const stateClsName = isOpen ? "wide" : "mini"; + + return ( + + {isOpen ? ( +
+ {isOperatorMode ? : } +
+ ) : ( +
+ ...
}> + + + + )} + + { + if (isOpen) { + onToggle(false); + } else { + onToggle(true); + } + }} + size="small" + > + {isOpen ? : } + +
+ ); +}; + +export default MenuToggle; diff --git a/portal-ui/src/screens/Console/Menu/utils.ts b/portal-ui/src/screens/Console/Menu/utils.ts deleted file mode 100644 index e43485cba..000000000 --- a/portal-ui/src/screens/Console/Menu/utils.ts +++ /dev/null @@ -1,24 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -export const menuGroups = [ - { label: "", group: "common", collapsible: false }, - { label: "User", group: "User", collapsible: true }, - { label: "Admin", group: "Admin", collapsible: true }, - { label: "Tools", group: "Tools", collapsible: true }, - { label: "Operator", group: "Operator", collapsible: false }, - { label: "", group: "License", collapsible: false }, -];