Draggable диалоги

Draggable диалоги позволяют пользователю перемещать модальные окна по экрану, обеспечивая более гибкий интерфейс. В MUI (Material-UI) это достигается комбинацией компонентов Dialog и Paper, а также использованием внешней библиотеки react-draggable или встроенных возможностей MUI через PaperComponent.


Использование PaperComponent для перемещения диалога

Компонент Dialog принимает свойство PaperComponent, которое позволяет указать, какой компонент будет использоваться в качестве “коробки” диалога. Для реализации перетаскивания можно создать обёртку на базе Draggable:

import React from 'react';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import Paper from '@mui/material/Paper';
import Draggable from 'react-draggable';

function PaperComponent(props) {
  return (
    <Draggable handle="#draggable-dialog-title" cancel={'[class*="MuiDialogContent-root"]'}>
      <Paper {...props} />
    </Draggable>
  );
}

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

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

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

  return (
    <div>
      <button onCl ick={handleClickOpen}>Открыть диалог</button>
      <Dialog
        open={open}
        onCl ose={handleClose}
        PaperComponent={PaperComponent}
        aria-labelledby="draggable-dialog-title"
      >
        <DialogTitle style={{ cursor: 'move' }} id="draggable-dialog-title">
          Заголовок диалога
        </DialogTitle>
        <DialogContent>
          Содержимое диалога можно перетаскивать по экрану.
        </DialogContent>
      </Dialog>
    </div>
  );
}

Разбор ключевых моментов:

  • handle="#draggable-dialog-title" в Draggable указывает, что только заголовок диалога можно использовать для перетаскивания. Это предотвращает случайное перемещение при взаимодействии с контентом.
  • cancel={'[class*="MuiDialogContent-root"]'} блокирует перетаскивание при клике на контент.
  • PaperComponent={PaperComponent} подключает кастомный компонент для диалога, который поддерживает перетаскивание.

Настройка ограничений перемещения

Можно ограничить перемещение диалога, чтобы он не уходил за границы окна:

<Draggable
  handle="#draggable-dialog-title"
  bounds="parent"
  cancel={'[class*="MuiDialogContent-root"]'}
>
  <Paper {...props} />
</Draggable>
  • bounds="parent" заставляет диалог оставаться внутри родительского контейнера.
  • Можно использовать объект { left, top, right, bottom } для точного задания границ.

Анимация и плавность перемещения

Для улучшения UX можно настроить плавное движение с помощью CSS-переходов или использовать проп position и события onDrag:

<Draggable
  handle="#draggable-dialog-title"
  onD rag={(e, data) => console.log(data.x, data.y)}
  cancel={'[class*="MuiDialogContent-root"]'}
>
  <Paper style={{ transition: 'transform 0.2s ease-out' }} {...props} />
</Draggable>
  • Свойство transition добавляет анимацию при перемещении.
  • Событие onDrag позволяет отслеживать текущее положение диалога и при необходимости синхронизировать с другими компонентами.

Сочетание с другими компонентами MUI

Draggable диалоги полностью совместимы с DialogActions, TextField, Select и другими элементами:

<Dialog
  open={open}
  onCl ose={handleClose}
  PaperComponent={PaperComponent}
  aria-labelledby="draggable-dialog-title"
>
  <DialogTitle style={{ cursor: 'move' }} id="draggable-dialog-title">
    Форма ввода
  </DialogTitle>
  <DialogContent>
    <TextField label="Имя" fullWidth margin="dense" />
    <TextField label="Email" fullWidth margin="dense" />
  </DialogContent>
  <DialogActions>
    <button onCl ick={handleClose}>Отмена</button>
    <button onCl ick={() => console.log('Сохранено')}>Сохранить</button>
  </DialogActions>
</Dialog>

Оптимизация для мобильных устройств

На мобильных устройствах перетаскивание может конфликтовать с жестами прокрутки. Рекомендуется отключать Draggable на небольших экранах:

const isMobile = window.innerWidth < 600;

<Dialog
  open={open}
  onCl ose={handleClose}
  PaperComponent={isMobile ? Paper : PaperComponent}
>
  • Такой подход сохраняет UX на десктопе, но предотвращает случайные ошибки на тач-устройствах.

Draggable диалоги в MUI обеспечивают гибкость интерфейса и повышают интерактивность приложений. С помощью PaperComponent, ограничений перемещения и адаптивной логики можно создать диалоги, которые выглядят современно и ведут себя предсказуемо.