Kaynağa Gözat

pref: refactor some UI components (#852)

* pref: refactor some UI components

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove comments

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

---------

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 1 hafta önce
ebeveyn
işleme
7d83cbe887

+ 18 - 18
client/src/components/cards/EmptyCard.tsx

@@ -1,24 +1,10 @@
-import { styled } from '@mui/material/styles';
 import Typography from '@mui/material/Typography';
 import CardContent from '@mui/material/CardContent';
+import Box from '@mui/material/Box';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import type { FC } from 'react';
 import type { EmptyCardProps } from './Types';
 
-const StyledSection = styled('section')(({ theme }) => ({
-  color: theme.palette.text.disabled,
-  backgroundColor: theme.palette.background.paper,
-  flexDirection: 'column',
-  textAlign: 'center',
-  display: 'flex',
-  alignItems: 'center',
-  justifyContent: 'center',
-}));
-
-const TitleTypography = styled(Typography)(({ theme }) => ({
-  marginTop: theme.spacing(2),
-}));
-
 const EmptyCard: FC<EmptyCardProps> = ({
   icon,
   text,
@@ -26,13 +12,27 @@ const EmptyCard: FC<EmptyCardProps> = ({
   loading = false,
 }) => {
   return (
-    <StyledSection className={wrapperClass}>
+    <Box
+      component="section"
+      className={wrapperClass}
+      sx={{
+        color: 'text.disabled',
+        backgroundColor: 'background.paper',
+        flexDirection: 'column',
+        textAlign: 'center',
+        display: 'flex',
+        alignItems: 'center',
+        justifyContent: 'center',
+      }}
+    >
       <CardContent>
         {loading && <StatusIcon type={LoadingType.CREATING} size={40} />}
         {icon}
-        <TitleTypography variant="h2">{text}</TitleTypography>
+        <Typography variant="h2" sx={{ mt: 2 }}>
+          {text}
+        </Typography>
       </CardContent>
-    </StyledSection>
+    </Box>
   );
 };
 

+ 2 - 2
client/src/components/customButton/CustomButton.tsx

@@ -28,8 +28,8 @@ const CustomButton = ({
   const button = <Button disabled={disabled} {...props} />;
   if (!tooltip) return button;
   return (
-    <Tooltip title={tooltip} placement={tooltipPlacement}>
-      {disabled ? <span>{button}</span> : button}
+    <Tooltip title={tooltip} placement={tooltipPlacement} arrow>
+      <span style={{ display: 'inline-block' }}>{button}</span>
     </Tooltip>
   );
 };

+ 20 - 32
client/src/components/customButton/CustomIconButton.tsx

@@ -1,42 +1,30 @@
 import Tooltip from '@mui/material/Tooltip';
 import IconButton from '@mui/material/IconButton';
-import { makeStyles } from '@mui/styles';
 import type { IconButtonProps } from '@mui/material/IconButton';
-import type { Theme } from '@mui/material/styles';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'inline-block',
-  },
-  iconBtn: {
-    padding: theme.spacing(0.5),
-  },
-}));
+interface CustomIconButtonProps extends IconButtonProps {
+  tooltip?: string;
+  className?: string;
+}
 
-const CustomIconButton = (props: IconButtonProps & { tooltip?: string }) => {
-  const { tooltip, className, ...otherProps } = props;
-  const classes = getStyles();
+const CustomIconButton = ({
+  tooltip,
+  className = '',
+  children,
+  ...otherProps
+}: CustomIconButtonProps) => {
+  const iconBtn = (
+    <IconButton sx={{ p: 0.5 }} className={className} {...otherProps}>
+      {children}
+    </IconButton>
+  );
+
+  if (!tooltip) return iconBtn;
 
   return (
-    <div className={`${classes.wrapper} ${className}`}>
-      {tooltip ? (
-        <Tooltip title={tooltip} arrow>
-          <span>
-            <IconButton classes={{ root: classes.iconBtn }} {...otherProps}>
-              {props.children}
-            </IconButton>
-          </span>
-        </Tooltip>
-      ) : (
-        <IconButton
-          classes={{ root: classes.iconBtn }}
-          {...otherProps}
-          size="large"
-        >
-          {props.children}
-        </IconButton>
-      )}
-    </div>
+    <Tooltip title={tooltip} arrow>
+      <span style={{ display: 'inline-block' }}>{iconBtn}</span>
+    </Tooltip>
   );
 };
 

+ 18 - 21
client/src/components/customButton/RefreshButton.tsx

@@ -5,47 +5,44 @@ import icons from '@/components/icons/Icons';
 import type { MouseEvent } from 'react';
 import type { IconButtonProps } from '@mui/material';
 
-const RefreshButton = (
-  props: IconButtonProps & {
-    tooltip?: string;
-    icon?: React.ReactNode;
-  }
-) => {
-  // props
-  const { onClick, icon, ...otherProps } = props;
-  // UI states
-  const [isLoading, setIsLoading] = useState(false);
+interface RefreshButtonProps extends IconButtonProps {
+  tooltip?: string;
+  icon?: React.ReactNode;
+}
 
-  // icon
+const RefreshButton = ({
+  onClick,
+  icon,
+  className,
+  ...otherProps
+}: RefreshButtonProps) => {
+  const [isLoading, setIsLoading] = useState(false);
   const RefreshIcon = icons.refresh;
 
   const onBtnClicked = async (event: MouseEvent<HTMLButtonElement>) => {
     setIsLoading(true);
-    onClick && (await onClick(event));
+    if (onClick) {
+      await onClick(event);
+    }
     setIsLoading(false);
   };
 
-  const styleObj = {
-    display: 'flex',
-    width: '23px',
-  };
-
   if (isLoading) {
     return (
-      <div className={props.className} style={styleObj}>
+      <span className={className} style={{ display: 'flex', width: 23 }}>
         <StatusIcon type={LoadingType.CREATING} />
-      </div>
+      </span>
     );
   }
 
   return (
     <CustomIconButton
-      className={props.className}
+      className={className}
       {...otherProps}
       onClick={onBtnClicked}
       disabled={isLoading}
     >
-      {icon ? icon : <RefreshIcon />}
+      {icon ?? <RefreshIcon />}
     </CustomIconButton>
   );
 };

+ 15 - 29
client/src/components/customDialog/CustomDialog.tsx

@@ -4,32 +4,10 @@ import DialogContent from '@mui/material/DialogContent';
 import Dialog from '@mui/material/Dialog';
 import Typography from '@mui/material/Typography';
 import { useTranslation } from 'react-i18next';
-import { styled } from '@mui/material/styles';
 import CustomButton from '../customButton/CustomButton';
 import CustomDialogTitle from './CustomDialogTitle';
 import type { CustomDialogType } from './Types';
 
-const StyledDialog = styled(Dialog, {
-  shouldForwardProp: prop =>
-    !['type', 'containerClass'].includes(prop as string),
-})<{ type?: 'notice' | 'custom'; containerClass?: string }>(
-  ({ theme, type, containerClass }) => ({
-    '& .MuiDialog-paper': {
-      border: `1px solid ${theme.palette.divider}`,
-      ...(type !== 'notice' && { maxWidth: '80%' }),
-    },
-    ...(containerClass && { container: containerClass }),
-  })
-);
-
-const StyledDialogContent = styled(DialogContent)(({ theme }) => ({
-  /* If needed, add dialog content styles here */
-}));
-
-const StyledCancelButton = styled(CustomButton)(({ theme }) => ({
-  color: theme.palette.text.secondary,
-}));
-
 const CustomDialog: FC<CustomDialogType> = props => {
   const { t } = useTranslation('btn');
   const { open, type, params, onClose, containerClass = '' } = props;
@@ -63,24 +41,32 @@ const CustomDialog: FC<CustomDialogType> = props => {
   };
 
   return (
-    <StyledDialog
-      type={type}
-      containerClass={containerClass}
+    <Dialog
+      className={containerClass}
       open={open}
       onClose={(event, reason) => {
         if (reason !== 'backdropClick') handleCancel();
       }}
+      sx={{
+        '& .MuiDialog-paper': {
+          border: theme => `1px solid ${theme.palette.divider}`,
+          ...(type !== 'notice' && { maxWidth: '80%' }),
+        },
+      }}
     >
       {type === 'notice' ? (
         <form onSubmit={handleConfirm}>
           <CustomDialogTitle onClose={handleCancel}>
             <Typography variant="body1">{title}</Typography>
           </CustomDialogTitle>
-          {component && <StyledDialogContent>{component}</StyledDialogContent>}
+          {component && <DialogContent>{component}</DialogContent>}
           <DialogActions>
-            <StyledCancelButton onClick={handleCancel}>
+            <CustomButton
+              onClick={handleCancel}
+              sx={{ color: theme => theme.palette.text.secondary }}
+            >
               {cancelLabel}
-            </StyledCancelButton>
+            </CustomButton>
             <CustomButton
               type="submit"
               color="primary"
@@ -94,7 +80,7 @@ const CustomDialog: FC<CustomDialogType> = props => {
       ) : (
         CustomComponent
       )}
-    </StyledDialog>
+    </Dialog>
   );
 };
 

+ 33 - 26
client/src/components/customDialog/CustomDialogTitle.tsx

@@ -1,29 +1,8 @@
 import Typography from '@mui/material/Typography';
 import MuiDialogTitle from '@mui/material/DialogTitle';
 import icons from '../icons/Icons';
-import { styled } from '@mui/material/styles';
 import type { DialogTitleProps } from '@mui/material/DialogTitle';
 
-const StyledDialogTitle = styled(MuiDialogTitle)(({ theme }) => ({
-  display: 'flex',
-  justifyContent: 'space-between',
-  alignItems: 'center',
-  marginBottom: theme.spacing(1),
-  paddingTop: theme.spacing(4),
-}));
-
-const TitleText = styled(Typography)(({ theme }) => ({
-  fontWeight: 500,
-  wordBreak: 'break-all',
-  fontSize: '20px',
-}));
-
-const CloseIcon = styled(icons.clear)(({ theme }) => ({
-  fontSize: '18px',
-  color: theme.palette.text.primary,
-  cursor: 'pointer',
-}));
-
 interface IProps extends DialogTitleProps {
   onClose?: () => void;
   showCloseIcon?: boolean;
@@ -35,17 +14,45 @@ const CustomDialogTitle = (props: IProps) => {
     classes = { root: '' },
     onClose,
     showCloseIcon = true,
+    sx,
     ...other
   } = props;
 
   return (
-    <StyledDialogTitle className={classes.root} {...other}>
-      <TitleText variant="body2">{children}</TitleText>
+    <MuiDialogTitle
+      className={classes.root}
+      {...other}
+      sx={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        mb: 1,
+        pt: 4,
+        ...sx,
+      }}
+    >
+      <Typography
+        variant="body2"
+        sx={{
+          fontWeight: 500,
+          wordBreak: 'break-all',
+          fontSize: '20px',
+        }}
+      >
+        {children}
+      </Typography>
       {showCloseIcon && onClose ? (
-        <CloseIcon data-testid="clear-icon" onClick={onClose} />
+        <icons.clear
+          data-testid="clear-icon"
+          onClick={onClose}
+          sx={{
+            fontSize: '18px',
+            cursor: 'pointer',
+          }}
+        />
       ) : null}
-    </StyledDialogTitle>
+    </MuiDialogTitle>
   );
 };
 
-export default CustomDialogTitle;
+export default CustomDialogTitle;

+ 41 - 55
client/src/components/customDialog/DeleteDialogTemplate.tsx

@@ -1,6 +1,5 @@
 import { FC, useContext, useState, ChangeEvent } from 'react';
 import { useTranslation } from 'react-i18next';
-import { styled } from '@mui/material/styles';
 import {
   DialogActions,
   DialogContent,
@@ -8,51 +7,13 @@ import {
   Typography,
   Checkbox,
   FormControlLabel,
+  Box,
 } from '@mui/material';
 import CustomButton from '@/components/customButton/CustomButton';
 import CustomDialogTitle from '@/components/customDialog/CustomDialogTitle';
 import { rootContext } from '@/context';
 import type { DeleteDialogContentType } from '@/components/customDialog/Types';
 
-const Root = styled('div')(({ theme }) => ({
-  maxWidth: 540,
-  backgroundColor: theme.palette.background.paper,
-}));
-
-const DialogSection = styled('div')(({ theme }) => ({
-  marginBottom: theme.spacing(2.5),
-}));
-
-const StyledDialogTitle = styled(CustomDialogTitle)(({ theme }) => ({
-  marginBottom: theme.spacing(2.5),
-}));
-
-const StyledTypography = styled(Typography)(({ theme }) => ({
-  marginBottom: theme.spacing(0.5),
-  color: theme.palette.text.secondary,
-}));
-
-const StyledTextField = styled(TextField)({
-  '& .MuiInputBase-input': {
-    padding: '10px 12px',
-  },
-  '& .MuiInputLabel-root': {
-    display: 'none',
-  },
-});
-
-const ActionButtons = styled(DialogActions)({
-  display: 'flex',
-});
-
-const CancelButton = styled(CustomButton)(({ theme }) => ({
-  color: theme.palette.text.secondary,
-}));
-
-const BoldText = styled('strong')({
-  fontWeight: 'bold',
-});
-
 const DeleteTemplate: FC<DeleteDialogContentType> = ({
   title,
   text,
@@ -76,54 +37,79 @@ const DeleteTemplate: FC<DeleteDialogContentType> = ({
   };
 
   return (
-    <Root>
+    <Box
+      sx={{
+        maxWidth: 540,
+        backgroundColor: theme => theme.palette.background.paper,
+      }}
+    >
       <form
         onSubmit={e => {
           e.preventDefault();
           handleDelete(force);
         }}
       >
-        <StyledDialogTitle onClose={handleCancelClick}>
+        <CustomDialogTitle onClose={handleCancelClick} sx={{ mb: 2.5 }}>
           {title}
-        </StyledDialogTitle>
+        </CustomDialogTitle>
 
         <DialogContent>
-          <DialogSection>
-            <StyledTypography
+          <Box sx={{ mb: 2.5 }}>
+            <Typography
               variant="body1"
+              sx={{ mb: 0.5, color: theme => theme.palette.text.secondary }}
               dangerouslySetInnerHTML={{ __html: text }}
             />
             <Typography variant="body1">
               {dialogTrans('deleteTipAction')}
-              <BoldText>{` ${(compare || label).toLowerCase()} `}</BoldText>
+              <Box
+                component="strong"
+                sx={{ fontWeight: 'bold', display: 'inline' }}
+              >
+                {` ${(compare || label).toLowerCase()} `}
+              </Box>
               {dialogTrans('deleteTipPurpose')}
             </Typography>
-          </DialogSection>
+          </Box>
 
-          <StyledTextField
+          <TextField
             fullWidth
             variant="filled"
             value={value}
             onChange={(e: ChangeEvent<HTMLInputElement>) =>
               setValue(e.target.value)
             }
+            sx={{
+              '& .MuiInputBase-input': {
+                padding: '10px 12px',
+              },
+              '& .MuiInputLabel-root': {
+                display: 'none',
+              },
+            }}
           />
 
           {forceDelLabel && (
             <FormControlLabel
               control={
-                <Checkbox onChange={(e, checked) => setForce(checked)} />
+                <Checkbox
+                  onChange={(e, checked) => setForce(checked)}
+                  checked={force}
+                />
               }
               label={forceDelLabel}
-              checked={force}
+              sx={{ mt: 1 }}
             />
           )}
         </DialogContent>
 
-        <ActionButtons>
-          <CancelButton onClick={handleCancelClick}>
+        <DialogActions sx={{ display: 'flex' }}>
+          <CustomButton
+            onClick={handleCancelClick}
+            sx={{ color: theme => theme.palette.text.secondary }}
+          >
             {btnTrans('cancel')}
-          </CancelButton>
+          </CustomButton>
           <CustomButton
             type="submit"
             variant="contained"
@@ -132,9 +118,9 @@ const DeleteTemplate: FC<DeleteDialogContentType> = ({
           >
             {label}
           </CustomButton>
-        </ActionButtons>
+        </DialogActions>
       </form>
-    </Root>
+    </Box>
   );
 };
 

+ 51 - 61
client/src/components/customDialog/DialogTemplate.tsx

@@ -1,57 +1,16 @@
 import { FC, useRef, useState } from 'react';
 import { useTranslation } from 'react-i18next';
-import { styled } from '@mui/material/styles';
-import { DialogContent, DialogActions, CircularProgress } from '@mui/material';
+import {
+  DialogContent,
+  DialogActions,
+  CircularProgress,
+  Box,
+} from '@mui/material';
 import CustomDialogTitle from './CustomDialogTitle';
 import CustomButton from '../customButton/CustomButton';
 import CodeView from '../code/CodeView';
 import type { DialogContainerProps } from './Types';
 
-const Wrapper = styled('section')({
-  display: 'flex',
-  '& form': {
-    display: 'flex',
-  },
-  '& .MuiDialogContent-root': {
-    maxHeight: '60vh',
-  },
-});
-
-const DialogBlock = styled('div')<{ showCode: boolean }>(
-  ({ theme, showCode }) => ({
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.paper,
-    minWidth: 540,
-    transition: 'width 0.2s',
-    width: showCode ? 540 : 'auto',
-  })
-);
-
-const CodeWrapper = styled('div')<{ showCode: boolean }>(
-  ({ theme, showCode }) => ({
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.paper,
-    width: showCode ? 540 : 0,
-    transition: 'width 0.2s',
-  })
-);
-
-const CodeContainer = styled('div')<{ showCode: boolean }>(
-  ({ theme, showCode }) => ({
-    height: '100%',
-    padding: showCode ? theme.spacing(4) : 0,
-  })
-);
-
-const Actions = styled(DialogActions)(({ theme }) => ({
-  paddingTop: theme.spacing(1),
-  paddingBottom: theme.spacing(2),
-  justifyContent: 'space-between',
-  '& .btn': {
-    margin: theme.spacing(0.5),
-  },
-}));
-
 const DialogTemplate: FC<DialogContainerProps> = ({
   title,
   cancelLabel,
@@ -91,12 +50,24 @@ const DialogTemplate: FC<DialogContainerProps> = ({
   };
 
   return (
-    <Wrapper>
+    <Box
+      sx={{
+        display: 'flex',
+        '& form': { display: 'flex' },
+        '& .MuiDialogContent-root': { maxHeight: '60vh' },
+      }}
+    >
       <form onSubmit={_handleConfirm}>
-        <DialogBlock
-          showCode={showCode}
+        <Box
           ref={dialogRef}
           className={dialogClass}
+          sx={{
+            color: theme => theme.palette.text.primary,
+            backgroundColor: theme => theme.palette.background.paper,
+            minWidth: 540,
+            transition: 'width 0.2s',
+            width: showCode ? 540 : 'auto',
+          }}
         >
           <CustomDialogTitle
             onClose={handleClose}
@@ -106,9 +77,16 @@ const DialogTemplate: FC<DialogContainerProps> = ({
           </CustomDialogTitle>
           <DialogContent>{children}</DialogContent>
           {showActions && (
-            <Actions>
-              <div>{leftActions}</div>
-              <div>
+            <DialogActions
+              sx={{
+                pt: 1,
+                pb: 2,
+                justifyContent: 'space-between',
+                '& .btn': { m: 0.5 },
+              }}
+            >
+              <Box>{leftActions}</Box>
+              <Box>
                 {showCancel && (
                   <CustomButton
                     onClick={onCancel}
@@ -130,20 +108,32 @@ const DialogTemplate: FC<DialogContainerProps> = ({
                 >
                   {confirming ? <CircularProgress size={16} /> : confirm}
                 </CustomButton>
-              </div>
-            </Actions>
+              </Box>
+            </DialogActions>
           )}
-        </DialogBlock>
+        </Box>
 
-        <CodeWrapper showCode={showCode}>
+        <Box
+          sx={{
+            color: theme => theme.palette.text.primary,
+            backgroundColor: theme => theme.palette.background.paper,
+            width: showCode ? 540 : 0,
+            transition: 'width 0.2s',
+          }}
+        >
           {showCode && (
-            <CodeContainer showCode={showCode}>
+            <Box
+              sx={{
+                height: '100%',
+                p: showCode ? 4 : 0,
+              }}
+            >
               <CodeView data={codeBlocksData} />
-            </CodeContainer>
+            </Box>
           )}
-        </CodeWrapper>
+        </Box>
       </form>
-    </Wrapper>
+    </Box>
   );
 };
 

+ 38 - 43
client/src/components/customSelector/CustomGroupedSelect.tsx

@@ -3,40 +3,9 @@ import InputLabel from '@mui/material/InputLabel';
 import ListSubheader from '@mui/material/ListSubheader';
 import MenuItem from '@mui/material/MenuItem';
 import Select from '@mui/material/Select';
-import { styled } from '@mui/material/styles';
 import { FC } from 'react';
 import type { GroupOption, ICustomGroupSelect } from './Types';
 
-const Wrapper = styled('div')({
-  width: '100%',
-});
-
-const StyledFormControl = styled(FormControl)({
-  width: '100%',
-});
-
-const GroupName = styled(ListSubheader)(({ theme }) => ({
-  paddingLeft: theme.spacing(2),
-  paddingRight: theme.spacing(2),
-  lineHeight: '32px',
-  color: theme.palette.text.primary,
-  fontWeight: 'bold',
-  fontSize: '12.8px',
-}));
-
-const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
-  padding: theme.spacing(0, 4),
-  lineHeight: '24px',
-
-  '&:hover': {
-    backgroundColor: 'rgba(18, 195, 244, 0.05)',
-  },
-
-  '&.Mui-selected': {
-    backgroundColor: 'rgba(0, 0, 0, 0.03)',
-  },
-}));
-
 const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
   const {
     options,
@@ -49,19 +18,45 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
   } = props;
 
   const renderSelectGroup = (option: GroupOption) => {
-    const items = option.children.map(child => {
-      return (
-        <StyledMenuItem key={child.value} value={child.value}>
-          {child.label}
-        </StyledMenuItem>
-      );
-    });
-    return [<GroupName key={option.label}>{option.label}</GroupName>, items];
+    const items = option.children.map(child => (
+      <MenuItem
+        key={child.value}
+        value={child.value}
+        sx={{
+          px: 4,
+          lineHeight: '24px',
+          '&:hover': {
+            backgroundColor: 'rgba(18, 195, 244, 0.05)',
+          },
+          '&.Mui-selected': {
+            backgroundColor: 'rgba(0, 0, 0, 0.03)',
+          },
+        }}
+      >
+        {child.label}
+      </MenuItem>
+    ));
+    return [
+      <ListSubheader
+        key={option.label}
+        sx={{
+          pl: 2,
+          pr: 2,
+          lineHeight: '32px',
+          color: theme => theme.palette.text.primary,
+          fontWeight: 'bold',
+          fontSize: '12.8px',
+        }}
+      >
+        {option.label}
+      </ListSubheader>,
+      items,
+    ];
   };
 
   return (
-    <Wrapper className={className}>
-      <StyledFormControl variant="filled">
+    <div className={className} style={{ width: '100%' }}>
+      <FormControl variant="filled" sx={{ width: '100%' }}>
         {haveLabel && <InputLabel htmlFor="grouped-select">{label}</InputLabel>}
         <Select
           displayEmpty={!haveLabel}
@@ -82,8 +77,8 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
         >
           {options.map(option => renderSelectGroup(option))}
         </Select>
-      </StyledFormControl>
-    </Wrapper>
+      </FormControl>
+    </div>
   );
 };
 

+ 24 - 11
client/src/components/customSelector/CustomMultiSelector.tsx

@@ -4,16 +4,9 @@ import InputLabel from '@mui/material/InputLabel';
 import MenuItem from '@mui/material/MenuItem';
 import Select from '@mui/material/Select';
 import Checkbox from '@mui/material/Checkbox';
-import { styled } from '@mui/material/styles';
 import { generateId } from '../../utils/Common';
 import type { CustomMultiSelectorType } from './Types';
 
-const StyledMenuItem = styled(MenuItem)({
-  minHeight: 'auto',
-  padding: '0 8px',
-  fontSize: '0.875rem',
-});
-
 const CustomSelector: FC<CustomMultiSelectorType> = props => {
   const {
     label,
@@ -32,7 +25,12 @@ const CustomSelector: FC<CustomMultiSelectorType> = props => {
   const id = generateId('selector');
 
   return (
-    <FormControl variant={variant} className={wrapperClass} size={size}>
+    <FormControl
+      variant={variant}
+      className={wrapperClass}
+      size={size}
+      sx={{ minWidth: 120 }}
+    >
       {label && (
         <InputLabel className={labelClass} htmlFor={id}>
           {label}
@@ -41,19 +39,34 @@ const CustomSelector: FC<CustomMultiSelectorType> = props => {
       <Select
         className={classes?.root}
         {...others}
-        multiple={true}
+        multiple
         value={values}
         onChange={onChange}
         inputProps={{
           id,
         }}
         renderValue={renderValue}
+        sx={{
+          '& .MuiSelect-multiple': {
+            display: 'flex',
+            flexWrap: 'wrap',
+            gap: 0.5,
+          },
+        }}
       >
         {options.map(v => (
-          <StyledMenuItem key={v.value} value={v.value}>
+          <MenuItem
+            key={v.value}
+            value={v.value}
+            sx={{
+              minHeight: 'auto',
+              px: 1,
+              fontSize: '0.875rem',
+            }}
+          >
             <Checkbox checked={values.indexOf(v.value as string) !== -1} />
             {v.label}
-          </StyledMenuItem>
+          </MenuItem>
         ))}
       </Select>
     </FormControl>

+ 26 - 34
client/src/components/customSnackBar/CustomSnackBar.tsx

@@ -2,7 +2,6 @@ import { forwardRef, FC } from 'react';
 import MuiAlert from '@mui/material/Alert';
 import Snackbar from '@mui/material/Snackbar';
 import Slide from '@mui/material/Slide';
-import { styled } from '@mui/material/styles';
 import type { AlertProps } from '@mui/material/Alert';
 import type { SlideProps } from '@mui/material/Slide';
 import type { CustomSnackBarType } from './Types';
@@ -17,24 +16,6 @@ const SlideTransition: FC<SlideProps> = props => {
   return <Slide {...props} direction="left" />;
 };
 
-// Styled components
-const StyledSnackbar = styled(Snackbar)(({ theme }) => ({
-  '&.MuiSnackbar-anchorOriginTopRight': {
-    [theme.breakpoints.up('md')]: {
-      top: '72px',
-      right: theme.spacing(4),
-    },
-    top: '72px',
-    right: theme.spacing(4),
-  },
-}));
-
-const StyledAlert = styled(Alert)({
-  maxWidth: '50vh',
-  wordBreak: 'break-all',
-});
-
-// CustomSnackBar component
 const CustomSnackBar: FC<CustomSnackBarType> = props => {
   const {
     vertical,
@@ -51,23 +32,34 @@ const CustomSnackBar: FC<CustomSnackBarType> = props => {
   };
 
   return (
-    <div>
-      <StyledSnackbar
-        anchorOrigin={{
-          vertical: vertical,
-          horizontal: horizontal,
-        }}
-        key={`${vertical}${horizontal}`}
-        open={open}
+    <Snackbar
+      anchorOrigin={{
+        vertical: vertical,
+        horizontal: horizontal,
+      }}
+      key={`${vertical}${horizontal}`}
+      open={open}
+      onClose={handleClose}
+      autoHideDuration={autoHideDuration}
+      TransitionComponent={SlideTransition}
+      sx={{
+        '&.MuiSnackbar-anchorOriginTopRight': {
+          top: { xs: 56, md: 72 },
+          right: theme => theme.spacing(4),
+        },
+      }}
+    >
+      <Alert
         onClose={handleClose}
-        autoHideDuration={autoHideDuration}
-        TransitionComponent={SlideTransition}
+        severity={type}
+        sx={{
+          maxWidth: '50vh',
+          wordBreak: 'break-all',
+        }}
       >
-        <StyledAlert onClose={handleClose} severity={type}>
-          {message}
-        </StyledAlert>
-      </StyledSnackbar>
-    </div>
+        {message}
+      </Alert>
+    </Snackbar>
   );
 };
 

+ 37 - 35
client/src/components/layout/Wrapper.tsx

@@ -1,6 +1,6 @@
 import { useTranslation } from 'react-i18next';
-import { styled } from '@mui/material/styles';
-import type { Theme } from '@mui/material/styles';
+import Box from '@mui/material/Box';
+import React from 'react';
 
 interface WrapperProps {
   hasPermission?: boolean;
@@ -8,49 +8,51 @@ interface WrapperProps {
   className?: string;
 }
 
-const WrapperRoot = styled('div')({
-  width: '100%',
-  height: '100%',
-  position: 'relative',
-});
-
-const Overlay = styled('div')(({ theme }: { theme: Theme }) => ({
-  position: 'absolute',
-  top: 0,
-  left: 0,
-  right: 0,
-  bottom: 0,
-  display: 'flex',
-  alignItems: 'center',
-  justifyContent: 'center',
-  backgroundColor: theme.palette.background.default,
-  zIndex: 1000,
-}));
-
-const Message = styled('div')(({ theme }: { theme: Theme }) => ({
-  fontSize: 14,
-  color: theme.palette.text.primary,
-  textAlign: 'center',
-  fontStyle: 'italic',
-}));
-
 const Wrapper = ({
   hasPermission = true,
   children,
   className,
 }: WrapperProps) => {
-  // i18n
-  const { t: commonTrans } = useTranslation();
+  const { t } = useTranslation();
 
   return (
-    <WrapperRoot className={className}>
+    <Box
+      className={className}
+      sx={{
+        width: '100%',
+        height: '100%',
+        position: 'relative',
+      }}
+    >
       {children}
       {!hasPermission && (
-        <Overlay>
-          <Message>{commonTrans('noPermissionTip')}</Message>
-        </Overlay>
+        <Box
+          sx={{
+            position: 'absolute',
+            top: 0,
+            left: 0,
+            right: 0,
+            bottom: 0,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            bgcolor: 'background.default',
+            zIndex: 1000,
+          }}
+        >
+          <Box
+            sx={{
+              fontSize: 14,
+              color: 'text.primary',
+              textAlign: 'center',
+              fontStyle: 'italic',
+            }}
+          >
+            {t('noPermissionTip')}
+          </Box>
+        </Box>
       )}
-    </WrapperRoot>
+    </Box>
   );
 };