Для интеграции Shopify Draggable в проект на Svelte сначала необходимо установить библиотеку через npm:
npm install @shopify/draggable
Импортировать основные модули можно следующим образом:
import { Draggable, Sortable, Swappable, Droppable } from '@shopify/draggable';
В Svelte компонентах лучше использовать
onMount для инициализации Draggable после
того, как DOM элементы уже отрендерены:
import { onMount } from 'svelte';
import { Sortable } from '@shopify/draggable';
let container;
onMount(() => {
const sortable = new Sortable(container, {
draggable: '.draggable-item',
handle: '.handle',
mirror: {
constrainDimensions: true
}
});
return () => {
sortable.destroy();
};
});
draggable – селектор элементов, которые
можно перемещать. handle – селектор
элемента, за который пользователь может захватывать объект для
перетаскивания. mirror.constrainDimensions
– ограничивает размеры зеркального элемента, повторяя исходный элемент.
delay – задержка начала перетаскивания в
миллисекундах, полезна для предотвращения случайных драг-энд-дропов.
sortable,
swappable,
droppable – разные классы для специфичных
сценариев: сортировка, обмен местами, дроп зоны.
Пример использования задержки:
const sortable = new Sortable(container, {
draggable: '.draggable-item',
delay: 150
});
В Svelte важно привязывать контейнеры к переменным через
bind:this:
<div bind:this={container} class="sortable-list">
<div class="draggable-item">Элемент 1</div>
<div class="draggable-item">Элемент 2</div>
<div class="draggable-item">Элемент 3</div>
</div>
Это гарантирует, что библиотека будет работать с реально существующими DOM-элементами после монтирования компонента.
Draggable предоставляет богатый набор событий:
drag:start, drag:move, drag:stop,
sortable:sorted и другие. В Svelte их удобно подписывать
через метод .on():
sortable.on('sortable:sorted', (event) => {
console.log('Элемент перемещён:', event.data.dragEvent.source);
});
Каждое событие содержит объект event.data, который
включает:
dragEvent.source – исходный элемент, который был
перемещён.dragEvent.mirror – копия элемента, отображаемая во
время перетаскивания.dragEvent.over – элемент, над которым происходит
перетаскивание.Svelte позволяет динамически генерировать элементы через
{#each}. Для корректной работы Draggable необходимо
обновлять его после изменения списка:
{#each items as item (item.id)}
<div class="draggable-item">{item.name}</div>
{/each}
Если массив items изменяется, следует вручную
пересоздавать экземпляр Sortable:
$: if (items) {
sortable.destroy();
sortable = new Sortable(container, { draggable: '.draggable-item' });
}
Зеркальный элемент (mirror) создаётся автоматически во
время перетаскивания. Можно кастомизировать его через CSS или через
опции mirror:
mirror: {
constrainDimensions: true,
xAxis: true,
yAxis: true,
}
Это позволяет ограничивать движение зеркала по определённой оси и сохранять размеры оригинала.
Для плавного визуального отклика можно использовать Svelte
transitions совместно с Draggable. Например, fade при
удалении элементов:
import { fade } from 'svelte/transition';
{#each items as item (item.id)}
<div class="draggable-item" transition:fade>{{item.name}}</div>
{/each}
requestAnimationFrame внутри событий drag:move
для минимизации нагрузки на рендер.destroy() перед пересозданием.mirror.constrainDimensions и handle, чтобы
уменьшить случайные срабатывания.<script>
import { onMount } from 'svelte';
import { Sortable } from '@shopify/draggable';
let container;
let items = [
{ id: 1, name: 'Элемент 1' },
{ id: 2, name: 'Элемент 2' },
{ id: 3, name: 'Элемент 3' }
];
let sortable;
onMount(() => {
sortable = new Sortable(container, {
draggable: '.draggable-item',
handle: '.handle',
mirror: { constrainDimensions: true }
});
sortable.on('sortable:sorted', (event) => {
const { source, over } = event.data.dragEvent;
console.log('Перемещён элемент:', source, 'над:', over);
});
return () => sortable.destroy();
});
</script>
<style>
.sortable-list { display: flex; flex-direction: column; gap: 8px; }
.draggable-item { padding: 12px; border: 1px solid #ccc; background: #f9f9f9; cursor: grab; }
.handle { cursor: grab; margin-right: 8px; }
</style>
<div bind:this={container} class="sortable-list">
{#each items as item (item.id)}
<div class="draggable-item">
<span class="handle">≡</span>
{item.name}
</div>
{/each}
</div>
Этот пример демонстрирует полное сочетание возможностей Svelte и Draggable: динамический список, события перетаскивания, использование зеркала и хэндла для захвата элементов.