Создание собственных плагинов

В основе Shopify Draggable лежит система плагинов, которая позволяет расширять функционал без изменения исходного кода библиотеки. Плагины — это классы, наследующиеся от базового Draggable.Plugins.AbstractPlugin, и реализующие набор обязательных методов для взаимодействия с жизненным циклом Draggable: инициализацию, активацию, деактивацию и очистку ресурсов.

Каждый плагин получает доступ к объекту draggable через конструктор, что позволяет подписываться на события (on, off) и работать с DOM-элементами, участвующими в перетаскивании.

Основные свойства плагина:

  • draggable — ссылка на экземпляр Draggable, которому принадлежит плагин.
  • options — объект с конфигурацией плагина, передаваемый при инициализации.
  • pluginName — уникальный идентификатор, используемый в реестре Draggable для управления плагинами.

Создание собственного плагина

Для создания нового плагина нужно:

  1. Создать класс, наследующий Draggable.Plugins.AbstractPlugin.
  2. Определить метод attach(), который вызывается при активации плагина.
  3. Определить метод detach(), вызываемый при деактивации.
  4. При необходимости реализовать дополнительные методы для внутренних целей плагина.

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

import { AbstractPlugin } from '@shopify/draggable';

class HighlightPlugin extends AbstractPlugin {
  attach() {
    this.draggable.on('drag:start', this.handleDragStart);
    this.draggable.on('drag:stop', this.handleDragStop);
  }

  detach() {
    this.draggable.off('drag:start', this.handleDragStart);
    this.draggable.off('drag:stop', this.handleDragStop);
  }

  handleDragStart = ({ source }) => {
    source.style.backgroundColor = 'rgba(255, 255, 0, 0.3)';
  }

  handleDragStop = ({ source }) => {
    source.style.backgroundColor = '';
  }
}

Подключение плагина к Draggable

После создания плагина его необходимо зарегистрировать при инициализации Draggable:

import { Draggable } from '@shopify/draggable';

const draggable = new Draggable(document.querySelectorAll('.items'), {
  draggable: '.item',
  plugins: [HighlightPlugin],
});

В данном случае HighlightPlugin автоматически вызовет attach() при инициализации Draggable. Если плагин необходимо отключить динамически, можно вызвать detach().

Работа с событиями Draggable внутри плагина

Плагины полностью интегрированы с системой событий Draggable. Они могут подписываться на:

  • drag:start — инициируется при начале перетаскивания.
  • drag:move — вызывается при перемещении элемента.
  • drag:stop — при завершении перетаскивания.
  • sortable:swap — если используется плагин Sortable, реагирует на смену позиций.

Для отписки необходимо использовать off, чтобы предотвратить утечки памяти при удалении плагина.

this.draggable.on('drag:move', this.handleDragMove);
this.draggable.off('drag:move', this.handleDragMove);

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

Каждый плагин может принимать собственные опции, которые передаются через объект конфигурации Draggable:

const draggable = new Draggable(document.querySelectorAll('.items'), {
  draggable: '.item',
  plugins: [new HighlightPlugin({ color: 'red', duration: 200 })],
});

Внутри плагина эти опции доступны через this.options, что позволяет реализовать кастомное поведение без жесткой привязки к конкретным значениями.

Взаимодействие нескольких плагинов

Draggable поддерживает подключение нескольких плагинов одновременно. Они работают независимо, но события draggable транслируются всем подключенным плагинам. Для согласованной работы рекомендуется:

  • Использовать уникальные имена методов обратного вызова.
  • Проверять наличие активного состояния плагина через this.enabled.
  • При изменении DOM внутри плагина использовать requestAnimationFrame для синхронизации с Draggable.

Методы жизненного цикла плагина

  • attach() — вызывается при активации, здесь выполняется основная логика подключения событий и модификации DOM.
  • detach() — отключает все слушатели и очищает внутренние данные.
  • destroy() — полностью удаляет плагин и освобождает ресурсы, обычно вызывает detach().

Пример расширенного плагина с жизненным циклом:

class LogPlugin extends AbstractPlugin {
  attach() {
    console.log('LogPlugin активирован');
    this.draggable.on('drag:start', this.logStart);
  }

  detach() {
    console.log('LogPlugin деактивирован');
    this.draggable.off('drag:start', this.logStart);
  }

  logStart = ({ source }) => {
    console.log('Начато перетаскивание элемента:', source);
  }
}

Практические советы по созданию плагинов

  • Минимизировать манипуляции DOM внутри обработчиков событий для производительности.
  • Использовать делегирование событий вместо навешивания их на каждый элемент отдельно.
  • Изолировать состояние плагина внутри экземпляра класса, избегая глобальных переменных.
  • Обрабатывать ошибки в обработчиках событий, чтобы один плагин не ломал работу других.
  • Документировать опции и поведение плагина, чтобы облегчить интеграцию и поддержку.

Расширение существующих плагинов

Плагины можно использовать как базу для других плагинов. Достаточно создать новый класс, наследующий существующий, и переопределить нужные методы:

class CustomHighlightPlugin extends HighlightPlugin {
  handleDragStart = ({ source }) => {
    source.style.border = '2px solid red';
    super.handleDragStart({ source });
  }
}

Таким образом, Shopify Draggable предоставляет гибкую систему расширений, позволяющую создавать как простые, так и комплексные плагины, полностью интегрированные с жизненным циклом перетаскивания и событиями библиотеки.