React DnD

Dragula — это библиотека для реализации перетаскивания элементов (drag-and-drop) в веб-приложениях на JavaScript. Она ориентирована на простоту использования и минимальную настройку, сохраняя при этом гибкость для сложных сценариев. В основе Dragula лежит принцип drag-and-drop без состояния: библиотека не управляет данными приложения, а только DOM, что позволяет интегрировать её с любыми фреймворками, включая React, Angular и Vue.

Установка и подключение

Для работы с Dragula доступны два основных способа подключения: через npm и через CDN.

Через npm:

npm install dragula

Подключение в проекте:

import dragula from 'dragula';
import 'dragula/dist/dragula.css';

CSS-файл Dragula содержит базовые стили для визуального выделения перетаскиваемого элемента и контейнеров. Без него взаимодействие будет работать, но элементы не будут визуально подсвечиваться во время перетаскивания.

Инициализация контейнеров

Основной принцип Dragula заключается в создании коллекции контейнеров, внутри которых можно перетаскивать элементы. Контейнер — это любой DOM-элемент, содержащий дочерние элементы.

const containers = [
  document.getElementById('left-panel'),
  document.getElementById('right-panel')
];

const drake = dragula(containers);

После инициализации drake предоставляет набор событий и методов для управления перетаскиванием. Контейнеры могут быть любыми HTML-элементами, включая <div>, <ul> и <ol>.

Настройки Dragula

Dragula поддерживает несколько опций, позволяющих гибко контролировать поведение перетаскивания.

Основные опции:

  • moves(el, source, handle, sibling): функция, возвращающая true, если элемент el может быть перетащен.
  • accepts(el, target, source, sibling): функция, определяющая, можно ли вставить элемент el в контейнер target.
  • copy: логическое значение или функция, указывающая, следует ли копировать элемент вместо перемещения.
  • revertOnSpill: если true, элемент возвращается на исходную позицию, если сброшен вне контейнеров.
  • removeOnSpill: удаляет элемент, если сброшен вне контейнера.

Пример использования опций:

const drake = dragula(containers, {
  moves: (el, source, handle) => handle.classList.contains('handle'),
  accepts: (el, target) => target.id !== 'left-panel',
  copy: false,
  revertOnSpill: true
});

События Dragula

Dragula предоставляет богатый набор событий, которые позволяют реагировать на действия пользователя:

  • drag: элемент начал перетаскиваться.
  • dragend: элемент завершил перетаскивание.
  • drop: элемент успешно сброшен в контейнер.
  • over: элемент находится над контейнером.
  • out: элемент покинул контейнер.
  • cancel: перетаскивание было отменено.
  • cloned: создается копия элемента при включенной опции copy.

Пример подписки на события:

drake.on('drag', el => {
  console.log('Начало перетаскивания', el);
});

drake.on('drop', (el, target, source, sibling) => {
  console.log('Элемент перемещен', el, 'в контейнер', target.id);
});

Интеграция с React

Dragula не управляет состоянием React напрямую, поэтому необходимо синхронизировать DOM с состоянием компонентов. Стандартный подход — обновлять состояние компонентов после события drop.

Пример компонента React:

import React, { useEffect, useRef, useState } from 'react';
import dragula from 'dragula';
import 'dragula/dist/dragula.css';

function DragList() {
  const [items, setItems] = useState(['Элемент 1', 'Элемент 2', 'Элемент 3']);
  const containerRef = useRef(null);

  useEffect(() => {
    const drake = dragula([containerRef.current]);
    
    drake.on('drop', (el, target, source, sibling) => {
      const newOrder = Array.from(containerRef.current.children).map(child => child.textContent);
      setItems(newOrder);
    });

    return () => drake.destroy();
  }, []);

  return (
    <div ref={containerRef}>
      {items.map((item, index) => (
        <div key={index}>{item}</div>
      ))}
    </div>
  );
}

В этом примере Dragula управляет только визуальной частью перетаскивания, а состояние React обновляется на основе текущего порядка элементов DOM.

Расширенные возможности

Клонирование элементов: с помощью опции copy можно создавать копии элементов вместо перемещения.

Динамические контейнеры: можно добавлять новые контейнеры после инициализации Dragula с помощью метода drake.containers.push(newContainer).

Анимация и стили: можно задавать собственные CSS-классы для перетаскиваемых элементов с помощью dragula.css или кастомных стилей, чтобы улучшить визуальный отклик при перемещении.

Поддержка touch: Dragula работает на мобильных устройствах без дополнительных библиотек, что делает её удобной для адаптивных интерфейсов.

Практические рекомендации

  • Всегда подключать CSS для базового визуального взаимодействия.
  • Для интеграции с React обновлять состояние компонента после события drop, а не во время перетаскивания.
  • Использовать moves и accepts для ограничения зон перетаскивания и контроля логики приложения.
  • При сложных сценариях с несколькими контейнерами хранить ссылки на все контейнеры и элементы для точного контроля порядка.

Dragula сочетает простоту использования с высокой гибкостью, позволяя создавать как простые списки с перетаскиванием, так и сложные интерфейсы с множественными контейнерами и динамическими элементами.