Dragula — это легковесная библиотека для реализации drag-and-drop взаимодействий в веб-приложениях. Её ключевая особенность — простота интеграции с любыми структурами DOM и минимальная конфигурация. Dragula ориентирована на прямую работу с элементами и контейнерами, предоставляя возможность перетаскивать элементы между ними или внутри одного контейнера без сложных настроек.
Контейнеры и элементы: Dragula оперирует двумя основными понятиями:
Dragula доступна через npm и может быть подключена в проект React следующим образом:
npm install dragula
Для корректной работы необходимо импортировать стили Dragula:
import 'dragula/dist/dragula.css';
import dragula from 'dragula';
В React Dragula рекомендуется использовать внутри
useEffect, чтобы гарантировать наличие DOM-элементов перед
созданием экземпляра drag-and-drop. Классическая схема инициализации
выглядит так:
import React, { useRef, useEffect } from 'react';
import dragula from 'dragula';
import 'dragula/dist/dragula.css';
function DragContainer() {
const containerRef = useRef(null);
useEffect(() => {
const drake = dragula([containerRef.current]);
return () => drake.destroy();
}, []);
return (
<div ref={containerRef}>
<div className="item">Элемент 1</div>
<div className="item">Элемент 2</div>
<div className="item">Элемент 3</div>
</div>
);
}
Особенности:
useRef обеспечивает прямой доступ к DOM-узлу
контейнера.useEffect гарантирует создание экземпляра Dragula после
рендера.drake.destroy() очищает подписки и предотвращает утечки
памяти.Dragula предоставляет множество опций для управления поведением перетаскивания:
const drake = dragula([containerRef.current], {
moves: (el, source, handle) => handle.classList.contains('handle'),
accepts: (el, target) => !target.classList.contains('no-drop'),
revertOnSpill: true,
removeOnSpill: false,
copy: false
});
Разбор ключевых опций:
Dragula поддерживает широкий набор событий, которые помогают отслеживать жизненный цикл перетаскивания:
drake.on('drag', (el) => {
console.log('Начало перетаскивания:', el);
});
drake.on('drop', (el, target, source, sibling) => {
console.log('Элемент сброшен:', el);
console.log('Целевой контейнер:', target);
console.log('Исходный контейнер:', source);
console.log('Элемент перед которым был сброшен:', sibling);
});
drake.on('over', (el, container) => {
console.log('Элемент над контейнером:', container);
});
drake.on('out', (el, container) => {
console.log('Элемент покинул контейнер:', container);
});
Ключевой момент: все события Dragula работают с нативными DOM-элементами. Для синхронизации состояния в React необходимо вручную обновлять состояние компонента на основе этих событий.
Чтобы Dragula корректно взаимодействовала со стейтом React, используется подход «синхронизации после drop»:
const [items, setItems] = React.useState(['Элемент 1', 'Элемент 2', 'Элемент 3']);
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();
}, []);
Такой подход гарантирует, что состояние React всегда соответствует текущему порядку элементов в DOM, избегая рассинхронизации между виртуальным DOM и реальным.
Dragula позволяет одновременно перетаскивать элементы между несколькими контейнерами. Для этого достаточно передать массив контейнеров при инициализации:
const drake = dragula([containerRef1.current, containerRef2.current]);
Можно управлять, какие элементы можно перемещать между контейнерами с
помощью функции accepts:
accepts: (el, target) => target !== containerRef2.current || el.classList.contains('movable')
Чтобы сделать перетаскивание более гибким, Dragula позволяет выделить конкретные зоны элементов, через которые осуществляется перемещение:
moves: (el, source, handle) => handle.classList.contains('drag-handle')
Такой метод позволяет создавать интерфейсы с кнопками, текстовыми зонами и другими интерактивными элементами без риска случайного перетаскивания.
Dragula автоматически добавляет классы во время перетаскивания:
gu-transit — элемент в процессе перемещения.gu-mirror — зеркальная копия элемента, которую тянет
курсор.gu-hide — скрытие исходного элемента при
копировании.gu-unselectable — предотвращает выделение текста.Для визуальных эффектов рекомендуется использовать CSS-переходы, чтобы обеспечить плавность перемещения и подсветку целевых контейнеров.
Dragula работает напрямую с DOM, поэтому повторные рендеры React могут сломать связь между элементами и контейнерами. Для предотвращения проблем:
key в списках элементов для точной
идентификации DOM-узлов.drake.destroy() перед
повторной инициализацией.Если элементы добавляются или удаляются динамически, необходимо
вызывать drake.containers.push(newContainer) или
пересоздавать экземпляр Dragula для новых контейнеров. Это обеспечивает
правильное поведение перетаскивания и предотвращает ошибки с
недоступными элементами.