Хуки и middleware

Dragula — это легковесная библиотека для реализации перетаскивания элементов между контейнерами в веб-приложениях. Её ключевая особенность — простота API и минимальная зависимость от внешних библиотек. Dragula строится на событиях перетаскивания, которые можно перехватывать и модифицировать через хуки и middleware.

Инициализация Dragula

Для работы Dragula необходимо создать экземпляр dragula с массивом контейнеров:

const containers = [document.getElementById('container1'), document.getElementById('container2')];
const drake = dragula(containers);

Ключевые параметры:

  • containers — массив HTML-элементов, между которыми возможно перетаскивание.
  • options — объект настроек, который позволяет контролировать поведение элементов при перетаскивании.

Пример настройки:

const drake = dragula(containers, {
  moves: function (el, source, handle, sibling) {
    return handle.classList.contains('handle'); 
  },
  accepts: function (el, target, source, sibling) {
    return !target.classList.contains('no-drop');
  },
  copy: false
});
  • moves — определяет, можно ли начать перетаскивание с данного элемента.
  • accepts — определяет, можно ли сбросить элемент в целевой контейнер.
  • copy — если true, элемент будет копироваться, а не перемещаться.

Хуки: управление жизненным циклом перетаскивания

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

Основные хуки:

  • drag — срабатывает в момент начала перетаскивания.
  • dragend — срабатывает при завершении перетаскивания.
  • drop — элемент был сброшен в контейнер.
  • over — элемент наведен на контейнер.
  • out — элемент покинул контейнер.
  • cloned — срабатывает при создании копии элемента (если включён copy).
  • shadow — срабатывает при создании тени перетаскиваемого элемента.

Пример использования хуков:

drake.on('drag', function(el) {
  el.style.opacity = '0.5';
});

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

drake.on('out', function(el, container, source) {
  console.log(`Элемент покинул контейнер ${container.id}`);
});

Особенности хуков:

  • Все хуки можно назначать динамически после инициализации Dragula.
  • Хуки получают конкретные параметры: элемент, исходный контейнер, целевой контейнер и соседний элемент (sibling).
  • Хуки не препятствуют стандартному поведению, если не используется дополнительная логика в accepts или moves.

Middleware: расширение функциональности

Middleware — это паттерн для перехвата и изменения поведения Dragula без модификации библиотеки напрямую. Он работает через настройку опций и обработку событий. Основные задачи middleware:

  • Валидация перетаскивания: проверка элементов перед перемещением.
  • Изменение визуального состояния: добавление классов, теней или анимаций.
  • Логирование действий: фиксация перемещений для аналитики.

Пример middleware для ограничения перетаскивания по количеству элементов:

function limitMiddleware(maxItems) {
  return function(el, target, source, sibling) {
    if (target.children.length >= maxItems) {
      return false;
    }
    return true;
  };
}

const drake = dragula(containers, {
  accepts: limitMiddleware(5)
});

В этом примере accepts выступает в роли middleware, проверяя количество элементов в контейнере и запрещая перетаскивание, если лимит превышен.


Работа с состоянием и стилями через хуки

Dragula позволяет изменять состояние элементов и контейнеров динамически, используя хуки.

Примеры:

  1. Подсветка контейнера при наведении:
drake.on('over', function(el, container) {
  container.classList.add('highlight');
});

drake.on('out', function(el, container) {
  container.classList.remove('highlight');
});
  1. Добавление эффекта перемещения для элемента:
drake.on('drag', function(el) {
  el.classList.add('dragging');
});

drake.on('dragend', function(el) {
  el.classList.remove('dragging');
});
  1. Логирование порядка элементов после перемещения:
drake.on('drop', function(el, target) {
  const order = Array.from(target.children).map(child => child.id);
  console.log('Новый порядок:', order);
});

Продвинутая настройка Dragula

  1. Контроль копирования элементов:
const drake = dragula(containers, {
  copy: function(el, source) {
    return source.id === 'template-container';
  }
});
  1. Сортировка внутри контейнера:
drake.on('drop', function(el, target, source, sibling) {
  if (sibling) {
    target.insertBefore(el, sibling);
  }
});
  1. Асинхронная проверка перетаскивания:
drake.on('drop', async function(el, target) {
  const allowed = await checkServerValidation(el.id, target.id);
  if (!allowed) {
    source.appendChild(el);
  }
});

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

Dragula можно использовать вместе с React, Vue или Angular, сохраняя работу хуков и middleware.

React-пример:

useEffect(() => {
  const drake = dragula([ref1.current, ref2.current]);

  drake.on('drop', (el, target) => {
    setItems(prev => reorderItems(prev, el.id, target.id));
  });

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

Vue-пример:

mounted() {
  this.drake = dragula([this.$refs.list1, this.$refs.list2]);
  this.drake.on('drop', (el, target) => {
    this.updateItems(el.id, target.id);
  });
}

Резюме функциональных возможностей

  • Dragula поддерживает как простые перемещения, так и сложные сценарии с копированием и ограничениями.
  • Хуки позволяют контролировать жизненный цикл перетаскивания, визуально и логически.
  • Middleware расширяет стандартное поведение без изменения библиотеки, обеспечивая гибкость.
  • Интеграция с современными фреймворками возможна при соблюдении жизненного цикла компонентов.

Эти механизмы делают Dragula мощным инструментом для реализации интерактивных интерфейсов с минимальными усилиями и высокой гибкостью настройки.