Использование в Vue.js

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

npm install dragula

или

yarn add dragula

В компоненте Vue.js Dragula импортируется следующим образом:

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

Импорт CSS обязателен для корректного отображения эффектов перетаскивания и стилей элементов.

Подготовка контейнеров для перетаскивания

Dragula работает с DOM-элементами, поэтому в шаблоне Vue необходимо определить контейнеры:

<template>
  <div class="container-wrapper">
    <div class="container" ref="leftContainer">
      <div class="item" v-for="item in leftItems" :key="item.id">{{ item.name }}</div>
    </div>
    <div class="container" ref="rightContainer">
      <div class="item" v-for="item in rightItems" :key="item.id">{{ item.name }}</div>
    </div>
  </div>
</template>

Использование ref позволяет получить доступ к DOM-элементам для передачи их в Dragula.

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

Инициализация происходит в хуке жизненного цикла mounted, чтобы контейнеры уже существовали в DOM:

export default {
  data() {
    return {
      leftItems: [
        { id: 1, name: 'Элемент A' },
        { id: 2, name: 'Элемент B' }
      ],
      rightItems: [
        { id: 3, name: 'Элемент C' }
      ]
    };
  },
  mounted() {
    const left = this.$refs.leftContainer;
    const right = this.$refs.rightContainer;

    this.drake = dragula([left, right]);

    this.drake.on('drop', (el, target, source, sibling) => {
      this.updateItems(el, source, target, sibling);
    });
  },
  methods: {
    updateItems(el, source, target, sibling) {
      const itemId = Number(el.dataset.id);
      const sourceArray = source === this.$refs.leftContainer ? this.leftItems : this.rightItems;
      const targetArray = target === this.$refs.leftContainer ? this.leftItems : this.rightItems;

      const index = sourceArray.findIndex(item => item.id === itemId);
      if (index === -1) return;

      const [movedItem] = sourceArray.splice(index, 1);

      let siblingIndex = targetArray.length;
      if (sibling) {
        const siblingId = Number(sibling.dataset.id);
        siblingIndex = targetArray.findIndex(item => item.id === siblingId);
      }

      targetArray.splice(siblingIndex, 0, movedItem);
    }
  }
};

Каждый элемент в шаблоне должен иметь data-id для корректного отслеживания перемещений:

<div class="item" 
     v-for="item in leftItems" 
     :key="item.id" 
     :data-id="item.id">
  {{ item.name }}
</div>

Настройка Dragula

Dragula поддерживает ряд опций:

  • copy — определяет, копируется ли элемент при перетаскивании.
  • revertOnSpill — возвращает элемент на место при неправильном сбросе.
  • removeOnSpill — удаляет элемент при сбросе за пределами контейнеров.
  • moves — функция, возвращающая true для разрешения перетаскивания конкретного элемента.
  • accepts — функция, контролирующая возможность сброса в определённый контейнер.

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

this.drake = dragula([left, right], {
  copy: false,
  revertOnSpill: true,
  moves: (el, source, handle, sibling) => {
    return !el.classList.contains('locked');
  },
  accepts: (el, target, source, sibling) => {
    return target !== source; 
  }
});

Реактивное обновление Vue.js

Dragula напрямую изменяет DOM, но Vue.js работает с реактивными массивами. Чтобы синхронизировать состояние, необходимо обновлять массивы через методы Vue, как показано в updateItems. Это обеспечивает правильное отображение элементов при повторных рендерах и при изменении данных.

Обработка событий Dragula

Dragula предоставляет набор событий для работы с перетаскиванием:

  • drag — когда элемент начинает перетаскиваться.
  • drop — при сбросе элемента.
  • over / out — при наведении на контейнер.
  • cancel — при отмене перетаскивания.

Пример подписки:

this.drake.on('drag', el => {
  el.classList.add('is-dragging');
});

this.drake.on('drop', el => {
  el.classList.remove('is-dragging');
});

Удаление Dragula

При уничтожении компонента важно отключить Dragula, чтобы избежать утечек памяти:

beforeUnmount() {
  this.drake.destroy();
}

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

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

this.$nextTick(() => {
  this.drake.containers = [this.$refs.leftContainer, this.$refs.rightContainer];
});

Это гарантирует, что новые элементы будут участвовать в перетаскивании без нарушения реактивности Vue.