Условная логика перетаскивания

Dragula — это лёгкая библиотека на JavaScript, предназначенная для организации перетаскивания элементов между контейнерами и внутри одного контейнера. Она отличается минимальными зависимостями, простым API и возможностью гибкой настройки поведения перетаскивания. Основной принцип работы Dragula заключается в том, что контейнеры и элементы внутри них можно делать «перетаскиваемыми» с помощью вызова функции dragula(containers, options), где containers — массив DOM-элементов, а options — объект с параметрами конфигурации.

Создание контейнеров и элементов

Для работы Dragula необходимо определить контейнеры, которые будут содержать элементы для перетаскивания. Элементы внутри контейнеров могут быть любыми HTML-элементами. Например:

Элемент 1
Элемент 2
Элемент A
Элемент B

Jav * aScript:

const leftContainer = document.getElementById('left-container');
const rightContainer = document.getElementById('right-container');

dragula([leftContainer, rightContainer]);

После этого элементы становятся перетаскиваемыми между двумя контейнерами и внутри них.

Конфигурация перетаскивания

Объект options позволяет тонко настраивать поведение Dragula. Основные свойства:

  • moves(el, source, handle, sibling) — функция, которая определяет, можно ли перетаскивать элемент el. Возвращает true или false.
  • accepts(el, target, source, sibling) — функция, определяющая, разрешено ли помещать элемент el в контейнер target. Можно реализовать сложную логику условного перетаскивания.
  • copy — логическое значение или функция. Если true, элементы копируются вместо перемещения.
  • revertOnSpill — при true элементы возвращаются на исходное место, если их «выбросили» за пределы контейнеров.
  • direction — направление перетаскивания: vertical или horizontal.
  • invalid(el, handle) — функция, запрещающая перетаскивание конкретных элементов или с конкретной части элемента.

Пример ограничения перетаскивания:

dragula([leftContainer, rightContainer], {
    moves: function(el, source, handle, sibling) {
        return !el.classList.contains('locked'); // элементы с классом locked нельзя перетаскивать
    },
    accepts: function(el, target, source, sibling) {
        return target.id !== 'right-container' || el.classList.contains('allowed');
    }
});

События Dragula

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

  • drag — элемент начал перетаскиваться.
  • dragend — элемент завершил перетаскивание.
  • drop — элемент был помещён в новый контейнер.
  • over — курсор находится над контейнером.
  • out — курсор покинул контейнер.
  • cancel — перетаскивание было отменено.
  • remove — элемент был удалён после «выбрасывания» за пределы контейнера.

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

const drake = dragula([leftContainer, rightContainer]);

drake.on('drop', function(el, target, source, sibling) {
    console.log(`Элемент "${el.textContent}" перемещён из ${source.id} в ${target.id}`);
});

drake.on('over', function(el, container) {
    container.style.backgroundColor = '#f0f0f0';
});

drake.on('out', function(el, container) {
    container.style.backgroundColor = '';
});

Условная логика перетаскивания

Условная логика позволяет контролировать, где и когда элементы могут быть перемещены, и задается через функции moves и accepts.

  • moves управляет исходной возможностью перетаскивания.
  • accepts управляет конечным расположением элемента.

Пример сложной условной логики:

dragula([leftContainer, rightContainer], {
    moves: (el, source, handle, sibling) => {
        return el.dataset.draggable === 'true'; // перетаскивать можно только элементы с data-draggable="true"
    },
    accepts: (el, target, source, sibling) => {
        if (source.id === 'left-container' && target.id === 'right-container') {
            return el.dataset.type === 'allowed'; // разрешено перемещать только элементы с data-type="allowed"
        }
        return true; // все остальные перемещения разрешены
    }
});

Копирование вместо перемещения

Иногда требуется, чтобы перетаскивание не удаляло элемент из исходного контейнера, а создавало его копию:

dragula([leftContainer, rightContainer], {
    copy: true,
    accepts: (el, target) => target.id === 'right-container' // копирование только в правый контейнер
});

Ограничение по порядку элементов

Dragula позволяет определять позицию вставки с помощью параметра sibling в функции accepts. Это позволяет вставлять элементы только перед определённым элементом или в конец контейнера.

dragula([leftContainer, rightContainer], {
    accepts: (el, target, source, sibling) => {
        if (sibling && sibling.classList.contains('no-drop-before')) {
            return false; // запрещаем вставку перед элементами с классом no-drop-before
        }
        return true;
    }
});

Визуальные эффекты и стили

Для лучшего UX важно использовать CSS-классы, добавляемые Dragula:

  • gu-mirror — клонированный элемент, который следует за курсором во время перетаскивания.
  • gu-hide — скрывает исходный элемент при перетаскивании.
  • gu-transit — применяется к элементу, находящемуся в процессе перемещения.

Пример CSS:

.gu-mirror {
    opacity: 0.8;
    border: 2px dashed #555;
}
.gu-transit {
    opacity: 0.5;
}
.gu-hide {
    display: none !important;
}

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

Интеграция с динамическим контентом

Dragula поддерживает работу с элементами, добавленными в контейнер после инициализации. Для этого достаточно просто добавить новые элементы в DOM — Dragula будет работать с ними автоматически, если они находятся в уже определённых контейнерах.