Browse Source

pref: user should be able to switch back to the collection list from (#851)

headder

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 2 months ago
parent
commit
0dddb567f0
1 changed files with 174 additions and 178 deletions
  1. 174 178
      client/src/components/layout/Header.tsx

+ 174 - 178
client/src/components/layout/Header.tsx

@@ -1,105 +1,34 @@
 import { FC, useContext, useState, MouseEvent } from 'react';
 import { useTranslation } from 'react-i18next';
-import Typography from '@mui/material/Typography';
-import Tooltip from '@mui/material/Tooltip';
-import Menu from '@mui/material/Menu';
-import MenuItem from '@mui/material/MenuItem';
+import {
+  AppBar,
+  Toolbar,
+  Typography,
+  Tooltip,
+  Menu,
+  MenuItem,
+  IconButton,
+  Box,
+  Stack,
+} from '@mui/material';
 import { useNavigate } from 'react-router-dom';
-import { navContext, dataContext, authContext, rootContext } from '@/context';
+import {
+  navContext,
+  dataContext,
+  authContext,
+  rootContext,
+  ColorModeContext,
+} from '@/context';
 import { MilvusService } from '@/http';
-import CustomSelector from '@/components/customSelector/CustomSelector';
-import StatusIcon from '@/components/status/StatusIcon';
 import UpdateUser from '@/pages/user/dialogs/UpdateUserPassDialog';
 import icons from '../icons/Icons';
-import { styled } from '@mui/material/styles';
-import IconButton from '@mui/material/IconButton';
-import { ColorModeContext } from '@/context';
-import { LoadingType } from '@/components/status/StatusIcon';
-
-const HeaderWrapper = styled('header')(({ theme }) => ({
-  display: 'flex',
-  alignItems: 'center',
-  color: theme.palette.text.primary,
-  backgroundColor: theme.palette.background.paper,
-  paddingRight: theme.spacing(1),
-  borderBottom: `1px solid ${theme.palette.divider}`,
-  height: 48,
-}));
-
-const ContentWrapper = styled('div')({
-  display: 'flex',
-  justifyContent: 'space-between',
-  alignItems: 'center',
-  flex: 1,
-  height: 48,
-});
-
-const Navigation = styled('div')({
-  display: 'flex',
-  alignItems: 'center',
-});
-
-const StyledIcon = styled('div')(({ theme }) => ({
-  color: theme.palette.primary.main,
-  cursor: 'pointer',
-  marginRight: theme.spacing(1),
-}));
-
-const AddressWrapper = styled('div')(({ theme }) => ({
-  display: 'flex',
-  alignItems: 'center',
-
-  '& .text': {
-    marginRight: theme.spacing(2),
-
-    '& .address': {
-      fontSize: '12px',
-      lineHeight: 1.3,
-    },
-
-    '& .status': {
-      fontSize: '12px',
-      lineHeight: 1.3,
-      color: '#1ba954',
-    },
-  },
-}));
-
-const Title = styled(Typography)(({ theme }) => ({
-  paddingLeft: theme.spacing(2),
-}));
-
-const DatabaseSelector = styled(CustomSelector)(({ theme }) => ({
-  transform: 'translateY(-4px)',
-  width: 'auto',
-  minWidth: 120,
-  '& .MuiInputLabel-root': {
-    top: '4px',
-  },
-}));
-
-const ModeButton = styled(IconButton)(({ theme }) => ({
-  marginRight: theme.spacing(1),
-  '& svg': {
-    fontSize: 18,
-    color: theme.palette.text.primary,
-  },
-}));
-
-const Extra = styled('span')(({ theme }) => ({
-  marginLeft: theme.spacing(0.5),
-  display: 'flex',
-  '& svg': {
-    fontSize: 15,
-    color: theme.palette.primary.main,
-  },
-}));
+import Breadcrumbs from '@mui/material/Breadcrumbs';
 
 const Header: FC = () => {
   // use context
   const { navInfo } = useContext(navContext);
   const { mode, toggleColorMode } = useContext(ColorModeContext);
-  const { database, databases, setDatabase, loading } = useContext(dataContext);
+  const { database, databases, setDatabase } = useContext(dataContext);
   const { authReq, logout } = useContext(authContext);
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
@@ -108,11 +37,11 @@ const Header: FC = () => {
   const navigate = useNavigate();
 
   // UI states
-  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
+  const [dbAnchorEl, setDbAnchorEl] = useState<null | HTMLElement>(null);
+  const [userAnchorEl, setUserAnchorEl] = useState<null | HTMLElement>(null);
 
-  // i8n
+  // i18n
   const { t: commonTrans } = useTranslation();
-  const { t: dbTrans } = useTranslation('database');
   const { t: successTrans } = useTranslation('success');
   const { t: userTrans } = useTranslation('user');
 
@@ -134,16 +63,16 @@ const Header: FC = () => {
     await MilvusService.useDatabase({ database });
   };
 
-  const handleUserMenuClick = (event: MouseEvent<HTMLDivElement>) => {
-    setAnchorEl(event.currentTarget);
+  const handleUserMenuClick = (event: MouseEvent<HTMLButtonElement>) => {
+    setUserAnchorEl(event.currentTarget);
   };
 
   const handleUserMenuClose = () => {
-    setAnchorEl(null);
+    setUserAnchorEl(null);
   };
 
   const handleChangePassword = () => {
-    setAnchorEl(null);
+    setUserAnchorEl(null);
     setDialog({
       open: true,
       type: 'custom',
@@ -151,10 +80,10 @@ const Header: FC = () => {
         component: (
           <UpdateUser
             username={username}
-            onUpdate={res => {
+            onUpdate={() => {
               openSnackBar(successTrans('passwordChanged'));
               handleCloseDialog();
-              setAnchorEl(null);
+              setUserAnchorEl(null);
               logout();
             }}
             handleClose={handleCloseDialog}
@@ -164,95 +93,162 @@ const Header: FC = () => {
     });
   };
 
+  const handleDbClick = (event: MouseEvent<HTMLElement>) => {
+    setDbAnchorEl(event.currentTarget);
+  };
+
+  const handleDbMenuClose = () => {
+    setDbAnchorEl(null);
+  };
+
   // local computes
   const dbOptions = databases.map(d => ({ value: d.name, label: d.name }));
-  const isLoadingDb = dbOptions.length === 0;
 
   return (
-    <HeaderWrapper>
-      <ContentWrapper>
-        <Navigation>
+    <AppBar
+      position="static"
+      color="default"
+      elevation={0}
+      sx={{
+        borderBottom: theme => `1px solid ${theme.palette.divider}`,
+        height: 48,
+        justifyContent: 'center',
+        backgroundColor: theme =>
+          mode === 'dark' ? theme.palette.background.default : '#fff',
+      }}
+    >
+      <Toolbar
+        disableGutters
+        sx={{
+          minHeight: 48,
+          px: 2,
+          display: 'flex',
+          justifyContent: 'space-between',
+        }}
+      >
+        <Stack direction="row" alignItems="center" spacing={1}>
           {navInfo.backPath !== '' && (
-            <StyledIcon onClick={() => handleBack(navInfo.backPath)}>
+            <IconButton
+              size="small"
+              onClick={() => handleBack(navInfo.backPath)}
+              sx={{ color: 'primary.main' }}
+            >
               <BackIcon />
-            </StyledIcon>
+            </IconButton>
           )}
-          {navInfo.showDatabaseSelector &&
-            (!isLoadingDb ? (
-              <DatabaseSelector
-                label={dbTrans('database')}
-                value={database}
-                onChange={async (e: { target: { value: unknown } }) => {
-                  const database = e.target.value as string;
-                  await useDatabase(database);
-                  setDatabase(database);
-
-                  // if url contains databases, go to the database page
-                  if (window.location.hash.includes('databases')) {
-                    navigate(`/databases/${database}/collections`);
-                  }
-                }}
-                options={dbOptions}
-                variant="filled"
-                disabled={loading}
-              />
-            ) : (
-              <StatusIcon type={LoadingType.CREATING} />
-            ))}
-
-          <Title variant="h5" color="textPrimary">
-            {navInfo.navTitle}
-          </Title>
-          <Extra>{navInfo.extra}</Extra>
-        </Navigation>
-
-        <AddressWrapper>
-          <ModeButton onClick={toggleColorMode} color="inherit">
-            {mode === 'dark' ? <icons.night /> : <icons.day />}
-          </ModeButton>
-          <div className="text">
-            <Typography className="address">{address}</Typography>
-            <Typography className="status">
-              {commonTrans('status.running')}
-            </Typography>
-          </div>
-          {username && (
-            <>
-              <Tooltip title={username}>
-                <StyledIcon
-                  onClick={handleUserMenuClick}
-                  style={{ cursor: 'pointer' }}
-                >
-                  <Avatar />
-                </StyledIcon>
-              </Tooltip>
+          {navInfo.showDatabaseSelector && (
+            <Breadcrumbs aria-label="breadcrumb" sx={{ mx: 1 }}>
+              <Typography
+                sx={{ cursor: 'pointer', fontWeight: 500 }}
+                color="primary"
+                onClick={handleDbClick}
+              >
+                {database}
+              </Typography>
               <Menu
-                anchorEl={anchorEl}
-                open={Boolean(anchorEl)}
-                onClose={handleUserMenuClose}
-                anchorOrigin={{
-                  vertical: 'bottom',
-                  horizontal: 'right',
-                }}
-                transformOrigin={{
-                  vertical: 'top',
-                  horizontal: 'right',
-                }}
+                anchorEl={dbAnchorEl}
+                open={Boolean(dbAnchorEl)}
+                onClose={handleDbMenuClose}
+                anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
+                transformOrigin={{ vertical: 'top', horizontal: 'left' }}
               >
-                <MenuItem onClick={handleChangePassword}>
-                  {userTrans('changePassword')}
-                </MenuItem>
+                {dbOptions.map(option => (
+                  <MenuItem
+                    key={option.value}
+                    selected={option.value === database}
+                    onClick={async () => {
+                      await useDatabase(option.value);
+                      setDatabase(option.value);
+                      setDbAnchorEl(null);
+                      if (window.location.hash.includes('databases')) {
+                        navigate(`/databases/${option.value}/collections`);
+                      }
+                    }}
+                  >
+                    {option.label}
+                  </MenuItem>
+                ))}
               </Menu>
-            </>
+            </Breadcrumbs>
           )}
-          <Tooltip title={'disconnect'}>
-            <StyledIcon>
-              <LogoutIcon onClick={handleLogout} />
-            </StyledIcon>
-          </Tooltip>
-        </AddressWrapper>
-      </ContentWrapper>
-    </HeaderWrapper>
+          <Typography variant="h5" color="text.primary">
+            {navInfo.navTitle}
+          </Typography>
+          {navInfo.extra && (
+            <Box
+              sx={{
+                ml: 0.5,
+                display: 'flex',
+                alignItems: 'center',
+                '& svg': { fontSize: 15, color: 'primary.main' },
+              }}
+            >
+              {navInfo.extra}
+            </Box>
+          )}
+        </Stack>
+        <Stack direction="row" alignItems="center" spacing={1}>
+          <IconButton onClick={toggleColorMode} color="inherit" size="small">
+            {mode === 'dark' ? <icons.night /> : <icons.day />}
+          </IconButton>
+          <Box sx={{ display: 'flex', alignItems: 'center', mr: 2 }}>
+            <Box sx={{ mr: 2 }}>
+              <Typography
+                className="address"
+                sx={{ fontSize: 12, lineHeight: 1.3 }}
+              >
+                {address}
+              </Typography>
+              <Typography
+                className="status"
+                sx={{ fontSize: 12, lineHeight: 1.3, color: '#1ba954' }}
+              >
+                {commonTrans('status.running')}
+              </Typography>
+            </Box>
+            {username && (
+              <>
+                <Tooltip title={username}>
+                  <IconButton
+                    size="small"
+                    onClick={handleUserMenuClick}
+                    sx={{ color: 'primary.main' }}
+                  >
+                    <Avatar />
+                  </IconButton>
+                </Tooltip>
+                <Menu
+                  anchorEl={userAnchorEl}
+                  open={Boolean(userAnchorEl)}
+                  onClose={handleUserMenuClose}
+                  anchorOrigin={{
+                    vertical: 'bottom',
+                    horizontal: 'right',
+                  }}
+                  transformOrigin={{
+                    vertical: 'top',
+                    horizontal: 'right',
+                  }}
+                >
+                  <MenuItem onClick={handleChangePassword}>
+                    {userTrans('changePassword')}
+                  </MenuItem>
+                </Menu>
+              </>
+            )}
+            <Tooltip title={'disconnect'}>
+              <IconButton
+                size="small"
+                sx={{ color: 'primary.main' }}
+                onClick={handleLogout}
+              >
+                <LogoutIcon />
+              </IconButton>
+            </Tooltip>
+          </Box>
+        </Stack>
+      </Toolbar>
+    </AppBar>
   );
 };