👋

MUIでヘッダーのサイドバーを展開したとき、メイン部分の配置がずれないよう固定する

2024/05/22に公開
import * as React from 'react';
import { styled, useTheme } from '@mui/material/styles';
import {
  Avatar,
  Box,
  Divider,
  Drawer,
  List,
  ListItem,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  Toolbar,
  Tooltip,
} from '@mui/material';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
import { IconButton, Menu } from '@mui/material';
import HomeIcon from '@mui/icons-material/Home';
import LoginIcon from '@mui/icons-material/Login';
import LogoutIcon from '@mui/icons-material/Logout';
import MenuIcon from '@mui/icons-material/Menu';
import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import InboxIcon from '@mui/icons-material/MoveToInbox';
import MailIcon from '@mui/icons-material/Mail';
import { StyledButton, StyledTypography } from '@/styles/theme';
import Link from 'next/link';
import UserMenu from './Menu/UserMenu';
import LogoutConfirmModal from '../BasicTable/Modal/LogoutConfirmModal';

const drawerWidth = 240;
const navItems = [
  { name: 'ユーザアイコン', path: '/' },
  { name: 'ログイン', path: '/signin' },
  { name: 'サインアップ', path: '/signup' },
  // 他のメニュー項目...
];


// メイン部分がDrawer分右にずれるように修正
const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })<{
  open?: boolean;
}>(({ theme, open }) => ({
  flexGrow: 1,
  padding: theme.spacing(3),
  transition: theme.transitions.create('margin', {
    easing: theme.transitions.easing.sharp,
    duration: theme.transitions.duration.leavingScreen,
  }),
  marginLeft: 0, // ここを修正
  // 以下の部分をコメントアウト
  // ...(open && {
  //   transition: theme.transitions.create('margin', {
  //     easing: theme.transitions.easing.easeOut,
  //     duration: theme.transitions.duration.enteringScreen,
  //   }),
  //   marginLeft: `${drawerWidth}px`, // ここを修正
  // }),
}));

interface AppBarProps extends MuiAppBarProps {
  open?: boolean;
}

const AppBar = styled(MuiAppBar, {
  shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme, open }) => ({
  transition: theme.transitions.create(['margin', 'width'], {
    easing: theme.transitions.easing.sharp,
    duration: theme.transitions.duration.leavingScreen,
  }),
  ...(open && {
    width: `calc(100% - ${drawerWidth}px)`,
    marginLeft: open ? 0 : `${drawerWidth}px`,
    transition: theme.transitions.create(['margin', 'width'], {
      easing: theme.transitions.easing.easeOut,
      duration: theme.transitions.duration.enteringScreen,
    }),
  }),
}));

const DrawerHeader = styled('div')(({ theme }) => ({
  display: 'flex',
  alignItems: 'center',
  padding: theme.spacing(0, 1),
  // necessary for content to be below app bar
  ...theme.mixins.toolbar,
  justifyContent: 'flex-end',
}));

export default function PersistentDrawerLeft() {
  const theme = useTheme();
  const [open, setOpen] = React.useState(false);

  const handleDrawerOpen = () => {
    setOpen(true);
  };

  const handleDrawerClose = () => {
    setOpen(false);
  };

  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const menuOpen = Boolean(anchorEl);
  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };

  const [openLogoutModal, setOpenLogoutModal] = React.useState(false);

  const handleClickOpenLogoutModal = () => {
    setOpenLogoutModal(true);
  };

  const handleCloseLogoutModal = () => {
    setOpenLogoutModal(false);
  };

  return (
    <Box sx={{ display: 'flex' }}>
      <AppBar position="fixed" open={open} color="darkGray">
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            onClick={handleDrawerOpen}
            edge="start"
            sx={{ mr: 2, ...(open && { display: 'none' }) }}
          >
            <MenuIcon color="white" />
          </IconButton>
          <StyledTypography
            variant="h6"
            component="div"
            sx={{
              flexGrow: 1,
              display: { xs: 'none', sm: 'block' },
              color: 'white.main',
            }}
          >
            SNS APP
          </StyledTypography>
          <Box sx={{ display: { xs: 'none', sm: 'flex' } }}>
            <Tooltip title="Account settings">
              <IconButton
                onClick={handleClick}
                size="small"
                sx={{ ml: 2 }}
                aria-controls={open ? 'account-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
              >
                <Avatar sx={{ width: 32, height: 32 }}>M</Avatar>
              </IconButton>
            </Tooltip>
            <UserMenu
              handleClose={handleClose}
              menuOpen={menuOpen}
              anchorEl={anchorEl}
            />
            {navItems.map((item, index) => (
              <Link href={item.path} key={index}>
                <StyledButton sx={{ color: 'white.main' }}>
                  {item.name}
                </StyledButton>
              </Link>
            ))}
          </Box>
        </Toolbar>
      </AppBar>
      <Drawer
        sx={{
          width: drawerWidth,
          flexShrink: 0,
          '& .MuiDrawer-paper': {
            width: drawerWidth,
            boxSizing: 'border-box',
          },
        }}
        variant="persistent"
        anchor="left"
        open={open}
      >
        <DrawerHeader>
          <IconButton onClick={handleDrawerClose}>
            {theme.direction === 'ltr' ? (
              <ChevronLeftIcon />
            ) : (
              <ChevronRightIcon />
            )}
          </IconButton>
        </DrawerHeader>
        <Divider />
        <List>
          {/* {['SignUp', 'SignIn', 'Top', 'Validation'].map((text, index) => ( */}
          {/* <Link href={`${text.toLowerCase()}`} key={index}> */}
          <Link href="/">
            <ListItem disablePadding>
              <ListItemButton>
                <ListItemIcon>
                  <HomeIcon />
                </ListItemIcon>
                <ListItemText primary="トップ" />
              </ListItemButton>
            </ListItem>
          </Link>
          <Link href="/signup">
            <ListItem disablePadding>
              <ListItemButton>
                <ListItemIcon>
                  <PersonAddAltIcon />
                </ListItemIcon>
                <ListItemText primary="会員登録" />
              </ListItemButton>
            </ListItem>
          </Link>
          <Link href="/signin">
            <ListItem disablePadding>
              <ListItemButton>
                <ListItemIcon>
                  <LoginIcon />
                </ListItemIcon>
                <ListItemText primary="サインイン" />
              </ListItemButton>
            </ListItem>
          </Link>
          {/* ))} */}
        </List>
        <Divider />
        <List>
          <Link href="/test">
            <ListItem disablePadding>
              <ListItemButton>
                <ListItemIcon>
                  <IconButton onClick={handleClickOpenLogoutModal}>
                    <LogoutIcon />
                  </IconButton>
                </ListItemIcon>
                <ListItemText
                  primary="ログアウト"
                  onClick={handleClickOpenLogoutModal}
                />
                <LogoutConfirmModal
                  handleClickOpenLogoutModal={handleClickOpenLogoutModal}
                  handleCloseLogoutModal={handleCloseLogoutModal}
                  openLogoutModal={openLogoutModal}
                />
              </ListItemButton>
            </ListItem>
          </Link>
        </List>
      </Drawer>
      <Main open={open}>
        <DrawerHeader />
      </Main>
    </Box>
  );
}

Discussion