Кэширование состояния

Dragula — это легковесная библиотека для реализации drag-and-drop интерфейсов в браузере. Она позволяет перемещать элементы между контейнерами и внутри них без необходимости писать сложные обработчики событий. В основе работы Dragula лежит концепция контейнеров и элементов, которые можно перетаскивать. Контейнеры — это DOM-узлы, внутри которых располагаются перетаскиваемые элементы, а элементы — это дочерние узлы контейнера, которые пользователи могут перемещать.

Инициализация и базовая конфигурация

Для начала работы создаётся объект Dragula, которому передаётся массив контейнеров:

const containers = [document.getElementById('left'), document.getElementById('right')];
const drake = dragula(containers);

Библиотека автоматически обрабатывает события mousedown, mousemove и mouseup для элементов внутри контейнеров, создавая визуальный эффект перетаскивания.

Основные опции Dragula:

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

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

const drake = dragula(containers, {
    moves: (el, source, handle, sibling) => handle.classList.contains('handle'),
    accepts: (el, target, source, sibling) => !target.classList.contains('locked')
});

Кэширование состояния

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

Форматы хранения состояния

Наиболее удобные форматы:

  • JSON — универсальный формат, легко сериализуется и хранится в localStorage или sessionStorage.
  • Map/Объекты JavaScript — позволяют хранить дополнительные данные о каждом элементе (например, идентификаторы, метки или порядок сортировки).

Пример структуры состояния:

const state = {
    left: ['item1', 'item3'],
    right: ['item2', 'item4']
};

Где ключи — идентификаторы контейнеров, а массивы — идентификаторы элементов в порядке отображения.

События для обновления состояния

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

  • drag — начинается перемещение элемента.
  • drop — элемент помещён в новый контейнер.
  • remove — элемент удалён при removeOnSpill.

Пример обновления состояния при событии drop:

drake.on('drop', (el, target, source, sibling) => {
    const sourceId = source.id;
    const targetId = target.id;
    
    const elementId = el.id;

    // Удаляем из старого контейнера
    state[sourceId] = state[sourceId].filter(id => id !== elementId);

    // Вставляем в новый контейнер в нужное место
    const index = Array.from(target.children).indexOf(el);
    state[targetId].splice(index, 0, elementId);

    // Сохраняем состояние в localStorage
    localStorage.setItem('dragulaState', JSON.stringify(state));
});

Восстановление состояния

Для восстановления состояния после перезагрузки страницы необходимо:

  1. Получить сохранённый JSON из localStorage.
  2. Пройтись по контейнерам и расставить элементы в соответствии с порядком в массиве.

Пример восстановления:

const savedState = JSON.parse(localStorage.getItem('dragulaState') || '{}');

for (const containerId in savedState) {
    const container = document.getElementById(containerId);
    savedState[containerId].forEach(elId => {
        const el = document.getElementById(elId);
        if (el) container.appendChild(el);
    });
}

Оптимизация кэширования

  • Дебаунс сохранения: для уменьшения количества операций записи в localStorage можно использовать функцию дебаунса, чтобы состояние сохранялось не при каждом перемещении, а через фиксированный интервал.
  • Сохранение изменений по контейнерам: если интерфейс содержит много контейнеров, лучше обновлять состояние только того контейнера, в котором произошло изменение, чтобы не сериализовать весь объект каждый раз.

Обработка сложных сценариев

  • Множественные уровни вложенности: для списков с вложенными контейнерами состояние можно хранить рекурсивно, например, в виде дерева.
  • Атрибуты и данные элементов: кроме идентификатора можно сохранять дополнительные свойства, такие как метки, статусы или классы, для полного восстановления интерфейса.
  • События динамического добавления элементов: при создании новых элементов необходимо добавлять их в структуру состояния и присваивать уникальные идентификаторы.

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

Dragula легко интегрируется с React, Vue и Angular, если хранить состояние в соответствующих хранилищах (например, Redux или Vuex). События Dragula можно связывать с действиями этих библиотек для автоматического обновления виртуального состояния.

Пример с React:

drake.on('drop', (el, target, source, sibling) => {
    dispatch({
        type: 'UPDATE_DRAG_STATE',
        payload: { elementId: el.id, from: source.id, to: target.id, index: Array.from(target.children).indexOf(el) }
    });
});

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

Резюме по кэшированию

Кэширование состояния в Dragula требует аккуратного подхода к структуре данных и событиям библиотеки. Основная стратегия — отслеживать drop и remove, сохранять структуру контейнеров и порядок элементов, использовать localStorage или аналогичные механизмы для персистентности и при необходимости интегрировать с архитектурой приложения для динамических обновлений интерфейса.