Drawer для боковых меню

Drawer в MUI — это компонент для создания боковых панелей и навигационных меню. Он может появляться слева, справа, сверху или снизу экрана и поддерживает как временное, так и постоянное отображение. Основные варианты Drawer:

  • Temporary — панель появляется поверх содержимого, обычно используется для мобильных интерфейсов.
  • Persistent — панель фиксируется, но может скрываться и отображаться по триггеру.
  • Permanent — панель всегда видима и не закрывается, чаще используется на десктопах.

Простейший пример Drawer с временным отображением:

import * as React from 'react';
import Drawer from '@mui/material/Drawer';
import Button from '@mui/material/Button';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';

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

  const toggleDrawer = (state) => () => {
    setOpen(state);
  };

  const list = () => (
    <List>
      {['Главная', 'Профиль', 'Настройки'].map((text) => (
        <ListItem button key={text}>
          <ListItemText primary={text} />
        </ListItem>
      ))}
    </List>
  );

  return (
    <div>
      <Button onCl ick={toggleDrawer(true)}>Открыть меню</Button>
      <Drawer anchor="left" open={open} onCl ose={toggleDrawer(false)}>
        {list()}
      </Drawer>
    </div>
  );
}

Настройка позиции и размеров

Anchor определяет сторону экрана, откуда появляется Drawer:

  • "left" — слева.
  • "right" — справа.
  • "top" — сверху.
  • "bottom" — снизу.
<Drawer anchor="right" open={open} onCl ose={toggleDrawer(false)}>
  {list()}
</Drawer>

Ширина и стили регулируются через sx или PaperProps:

<Drawer
  anchor="left"
  open={open}
  onCl ose={toggleDrawer(false)}
  PaperProps={{
    sx: { width: 300, backgroundColor: '#f0f0f0' }
  }}
>
  {list()}
</Drawer>

Permanent и Persistent Drawer

Permanent Drawer всегда на экране, поэтому содержимое страницы сдвигается. Настройка:

<Drawer
  variant="permanent"
  sx={{
    width: 240,
    flexShrink: 0,
    '& .MuiDrawer-paper': { width: 240, boxSizing: 'border-box' }
  }}
>
  {list()}
</Drawer>

Persistent Drawer позволяет скрывать панель, но оставляет пространство для сдвига контента:

<Drawer
  variant="persistent"
  anchor="left"
  open={open}
  sx={{
    width: 240,
    flexShrink: 0,
    '& .MuiDrawer-paper': { width: 240, boxSizing: 'border-box' }
  }}
>
  {list()}
</Drawer>

Триггер для открытия/закрытия может быть реализован кнопкой:

<Button onCl ick={() => setOpen(!open)}>
  {open ? 'Закрыть меню' : 'Открыть меню'}
</Button>

Управление состоянием и анимациями

Drawer автоматически поддерживает анимацию появления. Для временных панелей ключевые события:

  • onOpen — вызывается при открытии.
  • onClose — вызывается при закрытии.

Для дополнительной анимации содержимого можно использовать Slide или Collapse внутри Drawer:

import Slide from '@mui/material/Slide';

<Slide direction="right" in={open}>
  <div>Содержимое с анимацией</div>
</Slide>

Вложенные и многоуровневые меню

Drawer удобно использовать с List и ListItem, поддерживая вложенные элементы через ListItemButton и Collapse:

import Collapse from '@mui/material/Collapse';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';

const [openSubMenu, setOpenSubMenu] = React.useState(false);

<List>
  <ListItemButton onCl ick={() => setOpenSubMenu(!openSubMenu)}>
    <ListItemText primary="Настройки" />
    {openSubMenu ? <ExpandLess /> : <ExpandMore />}
  </ListItemButton>
  <Collapse in={openSubMenu} timeout="auto" unmountOnExit>
    <List component="div" disablePadding>
      <ListItemButton sx={{ pl: 4 }}>
        <ListItemText primary="Профиль" />
      </ListItemButton>
      <ListItemButton sx={{ pl: 4 }}>
        <ListItemText primary="Безопасность" />
      </ListItemButton>
    </List>
  </Collapse>
</List>

Интеграция с AppBar и основным контентом

При использовании Persistent или Permanent Drawer важно корректно управлять отступами основного контента, чтобы он не перекрывался панелью:

<Box sx={{ display: 'flex' }}>
  <Drawer variant="persistent" open={open}>
    {list()}
  </Drawer>
  <Box
    component="main"
    sx={{
      flexGrow: 1,
      p: 3,
      transition: (theme) => theme.transitions.create('margin', {
        easing: theme.transitions.easing.sharp,
        duration: theme.transitions.duration.leavingScreen,
      }),
      marginLeft: open ? '240px' : '0',
    }}
  >
    <Toolbar />
    Основное содержимое страницы
  </Box>
</Box>

Пользовательская стилизация

MUI позволяет гибко настраивать Drawer через sx, PaperProps и кастомные темы. Например, можно добавить тень, скругления или градиент:

<Drawer
  variant="temporary"
  open={open}
  onCl ose={toggleDrawer(false)}
  PaperProps={{
    sx: {
      width: 280,
      boxShadow: 3,
      borderRadius: 2,
      background: 'linear-gradient(180deg, #ffffff, #e0e0e0)'
    }
  }}
>
  {list()}
</Drawer>

Управление доступностью

Для корректной навигации с клавиатуры и экранными читалками важно:

  • Использовать aria-label или aria-labelledby для Drawer.
  • Правильно обрабатывать события onClose.
  • Применять role="presentation" для списков внутри Drawer при временной панели.
<Drawer
  anchor="left"
  open={open}
  onCl ose={toggleDrawer(false)}
  aria-label="Главное меню"
>
  <Box role="presentation">
    {list()}
  </Box>
</Drawer>

Итоговая структура для комплексного приложения

Drawer может включать:

  • Многоуровневое меню с Collapse.
  • Интеграцию с AppBar и контентом.
  • Разные варианты отображения (temporary, persistent, permanent).
  • Пользовательскую стилизацию и темы.
  • Полную поддержку доступности и анимаций.

Это делает MUI Drawer мощным инструментом для построения удобных навигационных интерфейсов как на мобильных, так и на десктопных приложениях.