Drag Drop

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

Механизм drag & drop в контексте мультиселекта основан на перераспределении DOM-элементов, которые представляют выбранные значения. Каждый выбранный элемент превращается в отдельный токен (item), и именно эти токены становятся draggable-узлами.

Ключевые особенности поведения:

  • перемещение элементов влияет только на порядок выбранных значений;
  • исходный список опций не изменяется;
  • состояние компонента синхронизируется с новым порядком DOM;
  • порядок значений в value массива соответствует визуальному порядку.

Внутри плагина используется логика сортировки DOM-узлов контейнера выбранных элементов, после чего пересчитывается внутреннее состояние инстанса селекта.

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

Активация функциональности осуществляется через систему плагинов:

new TomSelect('#select', {
  plugins: ['drag_drop']
});

После подключения элементы выбора становятся перетаскиваемыми внутри контейнера выбранных значений. Никаких дополнительных настроек не требуется для базового поведения.

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

Базовая инициализация

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

<select id="select" multiple>
  <option value="js">JavaScript</option>
  <option value="ts">TypeScript</option>
  <option value="py">Python</option>
  <option value="go">Go</option>
</select>
new TomSelect('#select', {
  plugins: ['drag_drop'],
  maxItems: null
});

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

Механизм сортировки выбранных элементов

Сортировка реализуется через изменение порядка DOM-элементов внутри контейнера .ts-control (или аналогичного root-элемента управления выбранными значениями). При завершении drag-сессии выполняются следующие шаги:

  • фиксируется новый порядок узлов;
  • извлекаются соответствующие значения value;
  • обновляется внутренний массив выбранных элементов;
  • триггерится обновление состояния инстанса.

Фактически, DOM становится источником истины для порядка элементов до момента синхронизации с моделью данных.

Ограничения и поведение при maxItems

При использовании ограничения количества выбранных элементов (maxItems) drag & drop продолжает работать без ограничений, однако важно учитывать:

  • изменение порядка не влияет на количество элементов;
  • при динамическом добавлении новых элементов они встают в конец списка;
  • сортировка не сбрасывает ограничения.

Пример:

new TomSelect('#select', {
  plugins: ['drag_drop'],
  maxItems: 3
});

Если выбрано 3 элемента, их можно свободно переставлять, но добавить четвёртый нельзя без удаления одного из текущих.

Работа с опциями и кастомизация

Плагин допускает ограниченную кастомизацию поведения, в основном через стилизацию и вмешательство в DOM.

Визуальный handle

Часто используется визуальный индикатор перетаскивания. Он задаётся через CSS:

.ts-dropdown .item {
  cursor: grab;
}

.ts-dropdown .item:active {
  cursor: grabbing;
}

При необходимости можно добавить отдельную область для захвата (drag handle), модифицируя шаблон рендера элементов:

new TomSelect('#select', {
  plugins: ['drag_drop'],
  render: {
    item: function(data, escape) {
      return `
        <div class="item">
          <span class="handle">⋮⋮</span>
          <span class="label">${escape(data.text)}</span>
        </div>
      `;
    }
  }
});

Хотя визуальный handle присутствует, логика перетаскивания обычно привязана ко всему элементу.

Направление сортировки

По умолчанию сортировка происходит горизонтально или вертикально в зависимости от CSS-раскладки контейнера. Поведение не задаётся через API, а определяется стилями flex или inline-block.

События и интеграция

Drag & drop влияет на порядок значений, что может быть отслежено через стандартные события изменения состояния селекта.

Основное событие:

  • change — вызывается после изменения порядка элементов.

Пример обработки:

new TomSelect('#select', {
  plugins: ['drag_drop'],
  onChange: function(value) {
    console.log('Новый порядок:', value);
  }
});

Значение value в этом случае будет массивом, отражающим текущую последовательность после сортировки.

Дополнительно может использоваться событие update, если требуется реагировать на любые изменения DOM-состояния, однако оно менее предсказуемо в зависимости от версии сборки.

Drag & Drop в режиме multiple vs single

Drag & drop имеет смысл только при множественном выборе:

  • multiple mode: сортировка активна, элементы независимы;
  • single mode: плагин не оказывает эффекта, так как существует только одно значение.

Попытка включить drag_drop в одиночном режиме не приводит к ошибкам, но функциональность фактически отключена.

Мобильные устройства и touch

На мобильных устройствах drag & drop работает через touch-события. Поведение зависит от реализации браузера:

  • поддержка long-press для начала перетаскивания;
  • возможные конфликты со скроллом;
  • чувствительность к жестам зависит от ОС.

В некоторых сценариях требуется дополнительная настройка CSS:

.ts-control {
  touch-action: manipulation;
}

Это уменьшает конфликты между прокруткой и перетаскиванием.

Практические паттерны использования

Сортировка выбранных элементов часто применяется в следующих сценариях:

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

При этом порядок значений часто сохраняется на сервере как массив, где позиция элемента имеет смысловое значение.

Пример сохранения:

const select = new TomSelect('#select', {
  plugins: ['drag_drop'],
  onChange: function(value) {
    fetch('/save', {
      method: 'POST',
      body: JSON.stringify({ order: value })
    });
  }
});

Производительность и внутренние особенности

При большом количестве выбранных элементов drag & drop может создавать нагрузку, связанную с:

  • частыми DOM-reflow при перетаскивании;
  • пересчётом позиций элементов;
  • обновлением массива значений после каждого drop.

Оптимизация обычно достигается за счёт:

  • минимизации количества элементов в selection;
  • использования простых CSS-структур без сложных вложенных layout-правил;
  • отключения лишних плагинов, влияющих на рендер.

Внутренняя логика не пересоздаёт весь компонент при сортировке, а ограничивается перестановкой узлов, что снижает стоимость операции по сравнению с полной реинициализацией инстанса.